Merge tag 'android-16.0.0_r1' of https://android.googlesource.com/platform/external/wpa_supplicant_8 into HEAD

Android 16.0.0 release 1

Change-Id: I9a3f162b090ba8c23f9861cd2b339252aae8bec1

# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCaEhhDgAKCRDorT+BmrEO
# eDWUAJ49gBhXolkvlNntwtcWVMZv4gLExQCePC2TLgNxwMAcxnl2qe/+fT4kXbk=
# =lhnR
# -----END PGP SIGNATURE-----
# gpg: Signature faite le mar 10 jun 2025 12:45:02 EDT
# gpg:                avec la clef DSA 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Impossible de vérifier la signature : Pas de clef publique
diff --git a/Android.bp b/Android.bp
index a083f89..470dd0f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -39,10 +39,7 @@
 filegroup {
     name: "hs20-osu-client_srcs",
     srcs: [
-        "hs20/client/spp_client.c",
-        "hs20/client/oma_dm_client.c",
         "hs20/client/osu_client.c",
-        "hs20/client/est.c",
         "src/common/wpa_ctrl.c",
         "src/common/wpa_helpers.c",
         "src/crypto/crypto_internal.c",
@@ -76,10 +73,7 @@
 filegroup {
     name: "hs20_client_srcs",
     srcs: [
-        "hs20/client/est.c",
-        "hs20/client/oma_dm_client.c",
         "hs20/client/osu_client.c",
-        "hs20/client/spp_client.c",
         "src/common/wpa_ctrl.c",
         "src/common/wpa_helpers.c",
         "src/crypto/crypto_internal.c",
diff --git a/OWNERS b/OWNERS
index bed29cb..5ddabe7 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,4 @@
 etancohen@google.com
 arabawy@google.com
 kumachang@google.com
+include platform/system/core:/janitors/OWNERS #{LAST_RESORT_SUGGESTION}
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..6aad837
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,49 @@
+prebuilt_etc {
+    name: "wpa_supplicant.conf.prebuilt",
+    src: ":wpa_supplicant_template.conf",
+    filename: "wpa_supplicant.conf",
+    relative_install_path: "wifi",
+    installable: false,
+}
+
+genrule {
+    name: "com.android.hardware.hostapd.rc-gen",
+    srcs: ["android.hardware.hostapd.rc"],
+    out: ["com.android.hardware.hostapd.rc"],
+    cmd: "sed -E 's@/vendor/bin@/apex/com.android.hardware.wpa_supplicant/bin@' $(in) > $(out)",
+}
+
+prebuilt_etc {
+    name: "com.android.hardware.hostapd.rc",
+    src: ":com.android.hardware.hostapd.rc-gen",
+    installable: false,
+}
+
+apex {
+    name: "com.android.hardware.wpa_supplicant",
+    manifest: "apex_manifest.json",
+    file_contexts: "file_contexts",
+    // TODO: b/363080108 - Replace placeholder key with release key
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    updatable: false,
+    vendor: true,
+
+    enabled: select(soong_config_variable("wpa_supplicant_8", "wpa_build_hostapd"), {
+        true: true,
+        default: false,
+    }),
+    binaries: [
+        "wpa_supplicant",
+        "hostapd",
+    ],
+    prebuilts: [
+        "com.android.hardware.wpa_supplicant.rc",
+        "com.android.hardware.hostapd.rc",
+        "wpa_supplicant.conf.prebuilt",
+    ],
+    vintf_fragment_modules: [
+        "android.hardware.wifi.hostapd.xml",
+        "android.hardware.wifi.supplicant.xml",
+    ],
+}
diff --git a/apex/android.hardware.hostapd.rc b/apex/android.hardware.hostapd.rc
new file mode 100644
index 0000000..d89752e
--- /dev/null
+++ b/apex/android.hardware.hostapd.rc
@@ -0,0 +1,21 @@
+#
+# init.rc fragment for hostapd on Android
+# Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+on property:apex.all.ready=true
+    mkdir /data/vendor/wifi 0770 wifi wifi
+    mkdir /data/vendor/wifi/hostapd 0770 wifi wifi
+    mkdir /data/vendor/wifi/hostapd/sockets 0770 wifi wifi
+
+service hostapd /vendor/bin/hw/hostapd
+    interface aidl android.hardware.wifi.hostapd.IHostapd/default
+    class main
+    capabilities NET_ADMIN NET_RAW
+    user wifi
+    group wifi net_raw net_admin
+    disabled
+    oneshot
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
new file mode 100644
index 0000000..58b0640
--- /dev/null
+++ b/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.wpa_supplicant",
+    "version": 1
+}
\ No newline at end of file
diff --git a/apex/file_contexts b/apex/file_contexts
new file mode 100644
index 0000000..64599d6
--- /dev/null
+++ b/apex/file_contexts
@@ -0,0 +1,4 @@
+(/.*)?                    u:object_r:vendor_file:s0
+/etc(/.*)?                u:object_r:vendor_configs_file:s0
+/bin/hw/wpa_supplicant    u:object_r:hal_wifi_supplicant_default_exec:s0
+/bin/hw/hostapd           u:object_r:hal_wifi_hostapd_default_exec:s0
\ No newline at end of file
diff --git a/board_config_wpa_supplicant.mk b/board_config_wpa_supplicant.mk
new file mode 100644
index 0000000..c03f94a
--- /dev/null
+++ b/board_config_wpa_supplicant.mk
@@ -0,0 +1,96 @@
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# ###############################################################
+# This file adds wpa_supplicant_8 variables into soong config namespace (`wpa_supplicant_8`)
+# ###############################################################
+
+ifdef BOARD_HOSTAPD_DRIVER
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_build_hostapd,true)
+ifneq ($(BOARD_HOSTAPD_DRIVER),NL80211)
+    $(error BOARD_HOSTAPD_DRIVER set to $(BOARD_HOSTAPD_DRIVER) but current soong expected it should be NL80211 only!)
+endif
+endif
+
+ifdef BOARD_WPA_SUPPLICANT_DRIVER
+ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),NL80211)
+    $(error BOARD_WPA_SUPPLICANT_DRIVER set to $(BOARD_WPA_SUPPLICANT_DRIVER) but current soong expected it should be NL80211 only!)
+endif
+endif
+
+# This is for CONFIG_DRIVER_NL80211_BRCM, CONFIG_DRIVER_NL80211_SYNA, CONFIG_DRIVER_NL80211_QCA
+# And it is only used for a cflags setting in driver.
+$(call soong_config_set,wpa_supplicant_8,board_wlan_device,$(BOARD_WLAN_DEVICE))
+
+# Belong to CONFIG_IEEE80211AX definition
+ifeq ($(WIFI_FEATURE_HOSTAPD_11AX),true)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11ax,true)
+endif
+
+ifeq ($(WIFI_FEATURE_SUPPLICANT_11AX),true)
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_11ax,true)
+endif
+
+# Belong to CONFIG_IEEE80211BE definition
+ifeq ($(WIFI_FEATURE_HOSTAPD_11BE),true)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11be,true)
+endif
+
+ifeq ($(WIFI_FEATURE_SUPPLICANT_11BE),true)
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_11be,true)
+endif
+
+# PLATFORM_VERSION
+$(call soong_config_set,wpa_supplicant_8,platform_version,$(PLATFORM_VERSION))
+
+# BOARD_HOSTAPD_PRIVATE_LIB
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_use_stub_lib,true)
+else
+$(call soong_config_set,wpa_supplicant_8,board_hostapd_private_lib,$(BOARD_HOSTAPD_PRIVATE_LIB))
+endif
+
+ifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true)
+$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_config_80211w_mfp_optional,true)
+endif
+
+ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB_EVENT),)
+$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_private_lib_event,true)
+endif
+
+# BOARD_WPA_SUPPLICANT_PRIVATE_LIB
+ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_use_stub_lib,true)
+else
+$(call soong_config_set,wpa_supplicant_8,board_wpa_supplicant_private_lib,$(BOARD_WPA_SUPPLICANT_PRIVATE_LIB))
+endif
+
+ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),)
+$(call soong_config_set_bool,wpa_supplicant_8,board_wpa_supplicant_private_lib_event,true)
+endif
+
+ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_priv_cmd_update_mbo_cell_status,true)
+endif
+
+ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_hidl_unified_supplicant_service_rc_entry,true)
+endif
+
+# New added in internal main
+ifeq ($(WIFI_BRCM_OPEN_SOURCE_MULTI_AKM), enabled)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_brcm_open_source_multi_akm,true)
+endif
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 4f76a30..533a917 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -12,6 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// The hostapd related modules are split into 2 sections:
+// 1. For cuttlefish series products, start from `hostapd_headers` to `hostapd_cli_srcs`.
+// 2. For non-cuttlefish series products, ex: physical devices created by oems,
+//    the section starts from `hostapd_driver_srcs_default` to the end of this file.
+
 package {
     default_applicable_licenses: [
         "external_wpa_supplicant_8_license",
@@ -29,6 +34,9 @@
     ],
 }
 
+// The section below is for cuttlefish series products. For non-cuttlefish
+// products please update the section starting at `hostapd_driver_srcs_default`.
+// Start of cuttlefish section.
 cc_library_headers {
     name: "hostapd_headers",
     export_include_dirs: [
@@ -363,7 +371,29 @@
     installable: false,
 }
 
-// For converting the default to soong
+// End of cuttlefish section.
+
+// The section starting below is for non-cuttlefish products.
+// For cuttlefish series please update the section starting from `hostapd_headers`.
+
+// If you need to add a new build setting based on a product config, ex:
+// ifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true)
+//   L_CFLAGS += -DENABLE_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL
+// endif
+
+// In order to export the Makefile variable to soong, you will need to use a `soong_config_set` method
+// under `build/core/board_config_wpa_supplicant.mk`. Ex:
+// ifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true)
+//   $(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_config_80211w_mfp_optional,true)
+// endif
+
+// And then use the select statement in Android.bp to reflect the condition you need, ex:
+// select(soong_config_variable("wpa_supplicant_8", "board_hostapd_config_80211w_mfp_optional"), {
+//     true: ["-DENABLE_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL"],
+//     default: [],
+// })
+
+// Start of non-cuttlefish section
 cc_defaults {
     name: "hostapd_driver_srcs_default",
     srcs: [
@@ -451,6 +481,9 @@
     }) + select(soong_config_variable("wpa_supplicant_8", "hostapd_11ax"), {
         true: ["-DCONFIG_IEEE80211AX"],
         default: [],
+    }) + select(soong_config_variable("wpa_supplicant_8", "hostapd_11be"), {
+        true: ["-DCONFIG_IEEE80211BE"],
+        default: [],
     }) + select(soong_config_variable("wpa_supplicant_8", "board_hostapd_config_80211w_mfp_optional"), {
         true: ["-DENABLE_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL"],
         default: [],
@@ -602,6 +635,10 @@
     ] + select(soong_config_variable("wpa_supplicant_8", "hostapd_11ax"), {
         true: ["src/ap/ieee802_11_he.c"],
         default: [],
+    }) +
+    select(soong_config_variable("wpa_supplicant_8", "hostapd_11be"), {
+        true: ["src/ap/ieee802_11_eht.c"],
+        default: [],
     }),
     defaults: [
         "hostapd_driver_srcs_default",
@@ -704,3 +741,5 @@
         "hostapd_includes_default",
     ],
 }
+
+// End of non-cuttlefish section
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 680c572..0c13dab 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -143,7 +143,7 @@
 LDFLAGS += -rdynamic
 L_CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-L_CFLAGS += -DWPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls
 LIBS += -lbfd
 LIBS_c += -lbfd
 LIBS_h += -lbfd
@@ -276,6 +276,7 @@
 ifdef CONFIG_SAE_PK
 L_CFLAGS += -DCONFIG_SAE_PK
 NEED_AES_SIV=y
+NEED_BASE64=y
 OBJS += src/common/sae_pk.c
 endif
 NEED_ECC=y
@@ -956,6 +957,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
+
 L_CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), gnutls)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 489922c..db843a6 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -116,7 +116,7 @@
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD
+CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls
 LIBS += -lbfd -ldl -liberty -lz
 LIBS_c += -lbfd -ldl -liberty -lz
 LIBS_h += -lbfd -ldl -liberty -lz
@@ -299,6 +299,7 @@
 ifdef CONFIG_SAE_PK
 CFLAGS += -DCONFIG_SAE_PK
 NEED_AES_SIV=y
+NEED_BASE64=y
 OBJS += ../src/common/sae_pk.o
 endif
 NEED_ECC=y
@@ -712,6 +713,7 @@
 endif
 
 ifeq ($(CONFIG_TLS), wolfssl)
+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
 CONFIG_CRYPTO=wolfssl
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_wolfssl.o
@@ -1069,6 +1071,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
+
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
@@ -1369,6 +1382,8 @@
 SOBJS += ../src/common/sae_pk.o
 SOBJS += ../src/common/dragonfly.o
 SOBJS += $(AESOBJS)
+SOBJS += ../src/crypto/sha384.o
+SOBJS += ../src/crypto/sha512.o
 SOBJS += ../src/crypto/sha256-prf.o
 SOBJS += ../src/crypto/sha384-prf.o
 SOBJS += ../src/crypto/sha512-prf.o
@@ -1376,6 +1391,10 @@
 SOBJS += ../src/crypto/sha256-kdf.o
 SOBJS += ../src/crypto/sha384-kdf.o
 SOBJS += ../src/crypto/sha512-kdf.o
+SOBJS += ../src/common/wpa_common.o
+SOBJS += ../src/crypto/random.o
+SOBJS += ../src/crypto/sha1-prf.o
+SOBJS += ../src/utils/eloop.o
 
 _OBJS_VAR := NOBJS
 include ../src/objs.mk
diff --git a/hostapd/README-MULTI-AP b/hostapd/README-MULTI-AP
index ccee69e..6190cac 100644
--- a/hostapd/README-MULTI-AP
+++ b/hostapd/README-MULTI-AP
@@ -155,6 +155,6 @@
 ----------
 
 [1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh
-[2] https://github.com/prplfoundation/prplMesh
+[2] https://gitlab.com/prpl-foundation/prplmesh/prplMesh
 [3] https://www.wi-fi.org/file/multi-ap-specification-v10
     (requires registration)
diff --git a/hostapd/aidl/hostapd.cpp b/hostapd/aidl/hostapd.cpp
index 12d0d9e..745fab8 100644
--- a/hostapd/aidl/hostapd.cpp
+++ b/hostapd/aidl/hostapd.cpp
@@ -138,6 +138,12 @@
 int32_t aidl_client_version = 0;
 int32_t aidl_service_version = 0;
 
+inline std::array<uint8_t, ETH_ALEN> macAddrToArray(const uint8_t* mac_addr) {
+	std::array<uint8_t, ETH_ALEN> arr;
+	std::copy(mac_addr, mac_addr + ETH_ALEN, std::begin(arr));
+	return arr;
+}
+
 /**
  * Check that the AIDL service is running at least the expected version.
  * Use to avoid the case where the AIDL interface version
@@ -875,7 +881,7 @@
 		"%s\n"
 		"interface=%s\n"
 		"driver=nl80211\n"
-		"ctrl_interface=/data/vendor/wifi/hostapd/ctrl_%s\n"
+		"ctrl_interface=/data/vendor/wifi/hostapd/ctrl\n"
 		// ssid2 signals to hostapd that the value is not a literal value
 		// for use as a SSID.  In this case, we're giving it a hex
 		// std::string and hostapd needs to expect that.
@@ -901,7 +907,6 @@
 		"%s\n",
 		sanitized_overlay.c_str(),
 		iface_params.usesMlo ? br_name.c_str() : iface_params.name.c_str(),
-		iface_params.name.c_str(),
 		ssid_as_string.c_str(),
 		channel_config_as_string.c_str(),
 		iface_params.hwModeParams.enable80211N ? 1 : 0,
@@ -922,20 +927,40 @@
 		ap_isolation_as_string.c_str());
 }
 
-Generation getGeneration(hostapd_hw_modes *current_mode)
+Generation getGeneration(hostapd_hw_modes *current_mode,
+						 bool is_conf_enable_11ax,
+						 bool is_conf_enable_11be)
 {
 	wpa_printf(MSG_DEBUG, "getGeneration hwmode=%d, ht_enabled=%d,"
-		   " vht_enabled=%d, he_supported=%d",
-		   current_mode->mode, current_mode->ht_capab != 0,
-		   current_mode->vht_capab != 0, current_mode->he_capab->he_supported);
+			" vht_enabled=%d, he_supported=%d, eht_supported=%d,"
+			" ieee80211ax = %d, ieee80211be=%d",
+			current_mode->mode, current_mode->ht_capab != 0,
+			current_mode->vht_capab != 0,
+			current_mode->he_capab[IEEE80211_MODE_AP].he_supported,
+			current_mode->eht_capab[IEEE80211_MODE_AP].eht_supported,
+			is_conf_enable_11ax,
+			is_conf_enable_11be);
 	switch (current_mode->mode) {
 	case HOSTAPD_MODE_IEEE80211B:
 		return Generation::WIFI_STANDARD_LEGACY;
 	case HOSTAPD_MODE_IEEE80211G:
+		if (is_conf_enable_11be
+			&& current_mode->eht_capab[IEEE80211_MODE_AP].eht_supported) {
+			return Generation::WIFI_STANDARD_11BE;
+		}
+		if (is_conf_enable_11ax
+			&& current_mode->he_capab[IEEE80211_MODE_AP].he_supported) {
+			return Generation::WIFI_STANDARD_11AX;
+		}
 		return current_mode->ht_capab == 0 ?
 				Generation::WIFI_STANDARD_LEGACY : Generation::WIFI_STANDARD_11N;
 	case HOSTAPD_MODE_IEEE80211A:
-		if (current_mode->he_capab->he_supported) {
+		if (is_conf_enable_11be
+			&& current_mode->eht_capab[IEEE80211_MODE_AP].eht_supported) {
+			return Generation::WIFI_STANDARD_11BE;
+		}
+		if (is_conf_enable_11ax
+			&& current_mode->he_capab[IEEE80211_MODE_AP].he_supported) {
 			return Generation::WIFI_STANDARD_11AX;
 		}
 		return current_mode->vht_capab == 0 ?
@@ -1279,7 +1304,7 @@
 				return hapd;
 		}
 	}
-#endif
+#endif /* CONFIG_IEEE80211BE */
 	return NULL;
 }
 
@@ -1299,19 +1324,32 @@
 	const std::string owe_transition_ifname)
 {
 	if (iface_params.usesMlo) { // the mlo case, iface name is instance name which is mld_link_id
-		if (hostapd_get_iface_by_link_id(interfaces_, (size_t) iface_params.name.c_str())) {
+		if (hostapd_get_iface_by_link_id(interfaces_, std::stoi(iface_params.name.c_str()))) {
 			wpa_printf(
 				MSG_ERROR, "Instance link id %s already present",
 				iface_params.name.c_str());
 			return createStatus(HostapdStatusCode::FAILURE_IFACE_EXISTS);
 		}
-	}
-	if (hostapd_get_iface(interfaces_,
-			iface_params.usesMlo ? br_name.c_str() : iface_params.name.c_str())) {
-		wpa_printf(
-			MSG_ERROR, "Instance interface %s already present",
-			iface_params.usesMlo ? br_name.c_str() : iface_params.name.c_str());
-		return createStatus(HostapdStatusCode::FAILURE_IFACE_EXISTS);
+#ifdef CONFIG_IEEE80211BE
+		// The MLO AP uses the same interface name for all links. Thus, make sure the
+		// interface name wasn't used for non-mld AP only when adding a new interface.
+		// Also it is valid to have a hostapd_data with the same interface name when adding
+		// the second link instance.
+		struct hostapd_data* hapd = hostapd_get_iface(interfaces_, br_name.c_str());
+		if (hapd && !hapd->conf->mld_ap) {
+			wpa_printf(
+				MSG_ERROR, "Instance interface %s already present",
+						br_name.c_str());
+			return createStatus(HostapdStatusCode::FAILURE_IFACE_EXISTS);
+		}
+#endif
+	} else {
+		if (hostapd_get_iface(interfaces_, iface_params.name.c_str())) {
+			wpa_printf(
+				MSG_ERROR, "Instance interface %s already present",
+					iface_params.name.c_str());
+			return createStatus(HostapdStatusCode::FAILURE_IFACE_EXISTS);
+		}
 	}
 	const auto conf_params = CreateHostapdConfig(iface_params, channelParams, nw_params,
 					br_name, owe_transition_ifname);
@@ -1339,7 +1377,7 @@
 
 	// find the iface and set up callback.
 	struct hostapd_data* iface_hapd = iface_params.usesMlo ?
-		hostapd_get_iface_by_link_id(interfaces_, (size_t) iface_params.name.c_str()) :
+		hostapd_get_iface_by_link_id(interfaces_, std::stoi(iface_params.name.c_str())) :
 		hostapd_get_iface(interfaces_, iface_params.name.c_str());
 	WPA_ASSERT(iface_hapd != nullptr && iface_hapd->iface != nullptr);
 	if (iface_params.usesMlo) {
@@ -1374,7 +1412,7 @@
 						&& strlen(iface_hapd->conf->bridge) == 0) {
 					instanceName = std::to_string(iface_hapd->mld_link_id);
 				}
-#endif
+#endif /* CONFIG_IEEE80211BE */
 				for (const auto& callback : callbacks_) {
 					auto status = callback->onFailure(
 						strlen(iface_hapd->conf->bridge) > 0 ?
@@ -1403,7 +1441,7 @@
 				&& strlen(iface_hapd->conf->bridge) == 0) {
 			instanceName = std::to_string(iface_hapd->mld_link_id);
 		}
-#endif
+#endif /* CONFIG_IEEE80211BE */
 		info.apIfaceInstance = instanceName;
 		info.clientAddress.assign(mac_addr, mac_addr + ETH_ALEN);
 		info.isConnected = authorized;
@@ -1439,16 +1477,23 @@
 			if (iface_hapd->conf->mld_ap && strlen(iface_hapd->conf->bridge) == 0) {
 				instanceName = std::to_string(iface_hapd->mld_link_id);
 			}
-#endif
+#endif /* CONFIG_IEEE80211BE */
 			ApInfo info;
 			info.ifaceName = strlen(iface_hapd->conf->bridge) > 0 ?
 				iface_hapd->conf->bridge : iface_hapd->conf->iface,
 			info.apIfaceInstance = instanceName;
 			info.freqMhz = iface_hapd->iface->freq;
 			info.channelBandwidth = getChannelBandwidth(iface_hapd->iconf);
-			info.generation = getGeneration(iface_hapd->iface->current_mode);
+			info.generation =
+				getGeneration(iface_hapd->iface->current_mode,
+					iface_hapd->iconf->ieee80211ax, iface_hapd->iconf->ieee80211be);
 			info.apIfaceInstanceMacAddress.assign(iface_hapd->own_addr,
 				iface_hapd->own_addr + ETH_ALEN);
+#ifdef CONFIG_IEEE80211BE
+			if (iface_hapd->conf->mld_ap) {
+				info.mldMacAddress = macAddrToArray(iface_hapd->mld->mld_addr);
+			}
+#endif /* CONFIG_IEEE80211BE */
 			for (const auto &callback : callbacks_) {
 				auto status = callback->onApInstanceInfoChanged(info);
 				if (!status.isOk()) {
@@ -1464,7 +1509,7 @@
 			if (iface_hapd->conf->mld_ap && strlen(iface_hapd->conf->bridge) == 0) {
 				instanceName = std::to_string(iface_hapd->mld_link_id);
 			}
-#endif
+#endif /* CONFIG_IEEE80211BE */
 			// Invoke the failure callback on all registered clients.
 			for (const auto& callback : callbacks_) {
 				auto status =
@@ -1589,18 +1634,26 @@
 ::ndk::ScopedAStatus Hostapd::removeLinkFromMultipleLinkBridgedApIfaceInternal(
 const std::string& iface_name, const std::string& linkIdentity)
 {
+#ifdef CONFIG_IEEE80211BE
 	if (!hostapd_get_iface(interfaces_, iface_name.c_str())) {
 		wpa_printf(MSG_ERROR, "Interface %s doesn't exist", iface_name.c_str());
 		return createStatus(HostapdStatusCode::FAILURE_IFACE_UNKNOWN);
 	}
 	struct hostapd_data* iface_hapd =
-		hostapd_get_iface_by_link_id(interfaces_, (size_t) linkIdentity.c_str());
+		hostapd_get_iface_by_link_id(interfaces_, std::stoi(linkIdentity.c_str()));
 	if (iface_hapd) {
+// Currently, hostapd_link_remove is still under CONFIG_TESTING_OPTIONS.
+// TODO: b/340821197 - Make sure to take out the hostapd_link_remove() and other related code
+// out of CONFIG_TESTING_OPTIONS.
+#ifdef CONFIG_TESTING_OPTIONS
 		if (0 == hostapd_link_remove(iface_hapd, 1)) {
 			return ndk::ScopedAStatus::ok();
 		}
+#endif /* CONFIG_TESTING_OPTIONS */
 	}
 	return createStatus(HostapdStatusCode::FAILURE_ARGS_INVALID);
+#endif /* CONFIG_IEEE80211BE */
+	return createStatus(HostapdStatusCode::FAILURE_UNKNOWN);
 }
 
 }  // namespace hostapd
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 9470cae..a9310f2 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -713,10 +713,6 @@
 		else if (os_strcmp(start, "DPP") == 0)
 			val |= WPA_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-		else if (os_strcmp(start, "OSEN") == 0)
-			val |= WPA_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_PASN
 		else if (os_strcmp(start, "PASN") == 0)
 			val |= WPA_KEY_MGMT_PASN;
@@ -1834,233 +1830,6 @@
 	return 0;
 }
 
-
-static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
-{
-	struct hs20_icon *icon;
-	char *end;
-
-	icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
-				sizeof(struct hs20_icon));
-	if (icon == NULL)
-		return -1;
-	bss->hs20_icons = icon;
-	icon = &bss->hs20_icons[bss->hs20_icons_count];
-	os_memset(icon, 0, sizeof(*icon));
-
-	icon->width = atoi(pos);
-	pos = os_strchr(pos, ':');
-	if (pos == NULL)
-		return -1;
-	pos++;
-
-	icon->height = atoi(pos);
-	pos = os_strchr(pos, ':');
-	if (pos == NULL)
-		return -1;
-	pos++;
-
-	end = os_strchr(pos, ':');
-	if (end == NULL || end - pos > 3)
-		return -1;
-	os_memcpy(icon->language, pos, end - pos);
-	pos = end + 1;
-
-	end = os_strchr(pos, ':');
-	if (end == NULL || end - pos > 255)
-		return -1;
-	os_memcpy(icon->type, pos, end - pos);
-	pos = end + 1;
-
-	end = os_strchr(pos, ':');
-	if (end == NULL || end - pos > 255)
-		return -1;
-	os_memcpy(icon->name, pos, end - pos);
-	pos = end + 1;
-
-	if (os_strlen(pos) > 255)
-		return -1;
-	os_memcpy(icon->file, pos, os_strlen(pos));
-
-	bss->hs20_icons_count++;
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
-			       char *pos, int line)
-{
-	size_t slen;
-	char *str;
-
-	str = wpa_config_parse_string(pos, &slen);
-	if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
-		wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
-		os_free(str);
-		return -1;
-	}
-
-	os_memcpy(bss->osu_ssid, str, slen);
-	bss->osu_ssid_len = slen;
-	os_free(str);
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
-				     char *pos, int line)
-{
-	struct hs20_osu_provider *p;
-
-	p = os_realloc_array(bss->hs20_osu_providers,
-			     bss->hs20_osu_providers_count + 1, sizeof(*p));
-	if (p == NULL)
-		return -1;
-
-	bss->hs20_osu_providers = p;
-	bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
-	bss->hs20_osu_providers_count++;
-	os_memset(bss->last_osu, 0, sizeof(*p));
-	bss->last_osu->server_uri = os_strdup(pos);
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
-					char *pos, int line)
-{
-	if (bss->last_osu == NULL) {
-		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
-		return -1;
-	}
-
-	if (parse_lang_string(&bss->last_osu->friendly_name,
-			      &bss->last_osu->friendly_name_count, pos)) {
-		wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
-			   line, pos);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
-			      char *pos, int line)
-{
-	if (bss->last_osu == NULL) {
-		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
-		return -1;
-	}
-
-	os_free(bss->last_osu->osu_nai);
-	bss->last_osu->osu_nai = os_strdup(pos);
-	if (bss->last_osu->osu_nai == NULL)
-		return -1;
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss,
-			       char *pos, int line)
-{
-	if (bss->last_osu == NULL) {
-		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
-		return -1;
-	}
-
-	os_free(bss->last_osu->osu_nai2);
-	bss->last_osu->osu_nai2 = os_strdup(pos);
-	if (bss->last_osu->osu_nai2 == NULL)
-		return -1;
-	bss->hs20_osu_providers_nai_count++;
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
-				      int line)
-{
-	if (bss->last_osu == NULL) {
-		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
-		return -1;
-	}
-
-	if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
-		wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
-			       int line)
-{
-	char **n;
-	struct hs20_osu_provider *p = bss->last_osu;
-
-	if (p == NULL) {
-		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
-		return -1;
-	}
-
-	n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
-	if (n == NULL)
-		return -1;
-	p->icons = n;
-	p->icons[p->icons_count] = os_strdup(pos);
-	if (p->icons[p->icons_count] == NULL)
-		return -1;
-	p->icons_count++;
-
-	return 0;
-}
-
-
-static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
-				       char *pos, int line)
-{
-	if (bss->last_osu == NULL) {
-		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
-		return -1;
-	}
-
-	if (parse_lang_string(&bss->last_osu->service_desc,
-			      &bss->last_osu->service_desc_count, pos)) {
-		wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
-			   line, pos);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos,
-				    int line)
-{
-	char **n;
-
-	n = os_realloc_array(bss->hs20_operator_icon,
-			     bss->hs20_operator_icon_count + 1, sizeof(char *));
-	if (!n)
-		return -1;
-	bss->hs20_operator_icon = n;
-	bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos);
-	if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count])
-		return -1;
-	bss->hs20_operator_icon_count++;
-
-	return 0;
-}
-
 #endif /* CONFIG_HS20 */
 
 
@@ -2756,8 +2525,6 @@
 			return 1;
 		}
 		bss->eap_teap_auth = val;
-	} else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) {
-		bss->eap_teap_pac_no_inner = atoi(pos);
 	} else if (os_strcmp(buf, "eap_teap_separate_result") == 0) {
 		bss->eap_teap_separate_result = atoi(pos);
 	} else if (os_strcmp(buf, "eap_teap_id") == 0) {
@@ -3382,6 +3149,8 @@
 		bss->radius_server_auth_port = atoi(pos);
 	} else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
 		bss->radius_server_acct_port = atoi(pos);
+	} else if (os_strcmp(buf, "radius_server_acct_log") == 0) {
+		bss->radius_server_acct_log = atoi(pos);
 	} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
 		bss->radius_server_ipv6 = atoi(pos);
 #endif /* RADIUS_SERVER */
@@ -3709,6 +3478,8 @@
 		bss->rsn_override_mfp = atoi(pos);
 	} else if (os_strcmp(buf, "rsn_override_mfp_2") == 0) {
 		bss->rsn_override_mfp_2 = atoi(pos);
+	} else if (os_strcmp(buf, "spp_amsdu") == 0) {
+		bss->spp_amsdu = !!atoi(pos);
 	} else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
 		if (os_strcmp(pos, "AES-128-CMAC") == 0) {
 			bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
@@ -3964,6 +3735,10 @@
 			return 1;
 		}
 		conf->mbssid = mbssid;
+	} else if (os_strcmp(buf, "mbssid_index") == 0) {
+		bss->mbssid_index = atoi(pos);
+	} else if (os_strcmp(buf, "mbssid_max") == 0) {
+		conf->mbssid_max = atoi(pos);
 #endif /* CONFIG_IEEE80211AX */
 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 		bss->max_listen_interval = atoi(pos);
@@ -4381,8 +4156,6 @@
 		bss->disable_dgaf = atoi(pos);
 	} else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
 		bss->na_mcast_to_ucast = atoi(pos);
-	} else if (os_strcmp(buf, "osen") == 0) {
-		bss->osen = atoi(pos);
 	} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
 		bss->anqp_domain_id = atoi(pos);
 	} else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
@@ -4421,44 +4194,6 @@
 		os_free(bss->hs20_operating_class);
 		bss->hs20_operating_class = oper_class;
 		bss->hs20_operating_class_len = oper_class_len;
-	} else if (os_strcmp(buf, "hs20_icon") == 0) {
-		if (hs20_parse_icon(bss, pos) < 0) {
-			wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'",
-				   line, pos);
-			return 1;
-		}
-	} else if (os_strcmp(buf, "osu_ssid") == 0) {
-		if (hs20_parse_osu_ssid(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "osu_server_uri") == 0) {
-		if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "osu_friendly_name") == 0) {
-		if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "osu_nai") == 0) {
-		if (hs20_parse_osu_nai(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "osu_nai2") == 0) {
-		if (hs20_parse_osu_nai2(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "osu_method_list") == 0) {
-		if (hs20_parse_osu_method_list(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "osu_icon") == 0) {
-		if (hs20_parse_osu_icon(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "osu_service_desc") == 0) {
-		if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "operator_icon") == 0) {
-		if (hs20_parse_operator_icon(bss, pos, line) < 0)
-			return 1;
-	} else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
-		os_free(bss->subscr_remediation_url);
-		bss->subscr_remediation_url = os_strdup(pos);
-	} else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
-		bss->subscr_remediation_method = atoi(pos);
 	} else if (os_strcmp(buf, "hs20_t_c_filename") == 0) {
 		os_free(bss->t_c_filename);
 		bss->t_c_filename = os_strdup(pos);
@@ -4467,9 +4202,6 @@
 	} else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) {
 		os_free(bss->t_c_server_url);
 		bss->t_c_server_url = os_strdup(pos);
-	} else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) {
-		os_free(bss->hs20_sim_provisioning_url);
-		bss->hs20_sim_provisioning_url = os_strdup(pos);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_MBO
 	} else if (os_strcmp(buf, "mbo") == 0) {
@@ -4499,6 +4231,8 @@
 	PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
 	} else if (os_strcmp(buf, "ecsa_ie_only") == 0) {
 		conf->ecsa_ie_only = atoi(pos);
+	} else if (os_strcmp(buf, "csa_ie_only") == 0) {
+		conf->csa_ie_only = atoi(pos);
 	} else if (os_strcmp(buf, "bss_load_test") == 0) {
 		WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
 		pos = os_strchr(pos, ':');
@@ -4639,6 +4373,8 @@
 				   line);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "sae_track_password") == 0) {
+		bss->sae_track_password = atoi(pos);
 #endif /* CONFIG_SAE */
 	} else if (os_strcmp(buf, "vendor_elements") == 0) {
 		if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
@@ -5059,6 +4795,16 @@
 			return 1;
 		}
 		bss->macsec_csindex = macsec_csindex;
+	} else if (os_strcmp(buf, "macsec_icv_indicator") == 0) {
+		int macsec_icv_indicator = atoi(pos);
+
+		if (macsec_icv_indicator < 0 || macsec_icv_indicator > 1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid macsec_icv_indicator (%d): '%s'.",
+				   line, macsec_icv_indicator, pos);
+			return 1;
+		}
+		bss->macsec_icv_indicator = macsec_icv_indicator;
 	} else if (os_strcmp(buf, "mka_cak") == 0) {
 		size_t len = os_strlen(pos);
 
@@ -5132,6 +4878,16 @@
 		if (val < 0 || val > 1)
 			return 1;
 		bss->ssid_protection = val;
+	} else if (os_strcmp(buf, "known_sta_identification") == 0) {
+		int val = atoi(pos);
+
+		if (val < 0 || val > 1)
+			return 1;
+		bss->known_sta_identification = val;
+	} else if (os_strcmp(buf, "channel_usage") == 0) {
+		conf->channel_usage = atoi(pos);
+	} else if (os_strcmp(buf, "peer_to_peer_twt") == 0) {
+		conf->peer_to_peer_twt = atoi(pos);
 #ifdef CONFIG_IEEE80211BE
 	} else if (os_strcmp(buf, "ieee80211be") == 0) {
 		conf->ieee80211be = atoi(pos);
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index ea19ba7..e74b1c7 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -596,31 +596,8 @@
 
 #endif /* CONFIG_WPS */
 
+
 #ifdef CONFIG_HS20
-
-static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
-					     const char *cmd)
-{
-	u8 addr[ETH_ALEN];
-	const char *url;
-
-	if (hwaddr_aton(cmd, addr))
-		return -1;
-	url = cmd + 17;
-	if (*url == '\0') {
-		url = NULL;
-	} else {
-		if (*url != ' ')
-			return -1;
-		url++;
-		if (*url == '\0')
-			url = NULL;
-	}
-
-	return hs20_send_wnm_notification(hapd, addr, 1, url);
-}
-
-
 static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
 					      const char *cmd)
 {
@@ -669,7 +646,6 @@
 	wpabuf_free(req);
 	return ret;
 }
-
 #endif /* CONFIG_HS20 */
 
 
@@ -2707,7 +2683,7 @@
 	}
 
 	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
-					     settings.punct_bitmap);
+					     settings.freq_params.punct_bitmap);
 	if (ret) {
 		wpa_printf(MSG_INFO,
 			   "chanswitch: invalid frequency settings provided");
@@ -3210,6 +3186,7 @@
 
 
 #ifdef NEED_AP_MLME
+
 static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
 					     char *buf, size_t buflen)
 {
@@ -3243,6 +3220,35 @@
 
 	return pos - buf;
 }
+
+
+static int hostapd_ctrl_iface_dump_beacon(struct hostapd_data *hapd,
+					  char *buf, size_t buflen)
+{
+	struct beacon_data beacon;
+	char *pos, *end;
+	int ret;
+
+	if (hostapd_build_beacon_data(hapd, &beacon) < 0)
+		return -1;
+
+	if (2 * (beacon.head_len + beacon.tail_len) > buflen)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	ret = wpa_snprintf_hex(pos, end - pos, beacon.head, beacon.head_len);
+	pos += ret;
+
+	ret = wpa_snprintf_hex(pos, end - pos, beacon.tail, beacon.tail_len);
+	pos += ret;
+
+	free_beacon_data(&beacon);
+
+	return pos - buf;
+}
+
 #endif /* NEED_AP_MLME */
 
 
@@ -4026,7 +4032,7 @@
 	}
 
 	ret = hostapd_nan_usd_transmit(hapd, handle, ssi, NULL, peer_addr,
-				    req_instance_id);
+				       req_instance_id);
 fail:
 	wpabuf_free(ssi);
 	return ret;
@@ -4189,9 +4195,6 @@
 			reply_len = -1;
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
-	} else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
-		if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
-			reply_len = -1;
 	} else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
 		if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
 			reply_len = -1;
@@ -4347,6 +4350,9 @@
 	} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
 		reply_len = hostapd_ctrl_iface_track_sta_list(
 			hapd, reply, reply_size);
+	} else if (os_strcmp(buf, "DUMP_BEACON") == 0) {
+		reply_len = hostapd_ctrl_iface_dump_beacon(hapd, reply,
+							   reply_size);
 #endif /* NEED_AP_MLME */
 	} else if (os_strcmp(buf, "PMKSA") == 0) {
 		reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c
index 3e870c7..8bb25f2 100644
--- a/hostapd/eap_register.c
+++ b/hostapd/eap_register.c
@@ -44,13 +44,6 @@
 		ret = eap_server_unauth_tls_register();
 #endif /* EAP_SERVER_TLS */
 
-#ifdef EAP_SERVER_TLS
-#ifdef CONFIG_HS20
-	if (ret == 0)
-		ret = eap_server_wfa_unauth_tls_register();
-#endif /* CONFIG_HS20 */
-#endif /* EAP_SERVER_TLS */
-
 #ifdef EAP_SERVER_MSCHAPV2
 	if (ret == 0)
 		ret = eap_server_mschapv2_register();
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index 5caa779..291acf9 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
 /*
  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2024, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -66,6 +66,7 @@
 static int sqn_changes = 0;
 static int ind_len = 5;
 static int stdout_debug = 1;
+static bool stop = false;
 
 /* GSM triplets */
 struct gsm_triplet {
@@ -877,6 +878,12 @@
 	if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0)
 		return aka_auts(cmd + 9, resp, resp_len);
 
+	if (strncmp(cmd, "TERMINATE", 9) == 0) {
+		stop = true;
+		resp[0] = '\0';
+		return 0;
+	}
+
 	printf("Unknown request: %s\n", cmd);
 	return -1;
 }
@@ -931,10 +938,13 @@
 	struct gsm_triplet *g, *gprev;
 	struct milenage_parameters *m, *prev;
 
-	if (update_milenage && milenage_file && sqn_changes)
+	if (update_milenage && milenage_file && sqn_changes) {
+		sqn_changes = 0;
 		update_milenage_file(milenage_file);
+	}
 
 	g = gsm_db;
+	gsm_db = NULL;
 	while (g) {
 		gprev = g;
 		g = g->next;
@@ -942,16 +952,21 @@
 	}
 
 	m = milenage_db;
+	milenage_db = NULL;
 	while (m) {
 		prev = m;
 		m = m->next;
 		os_free(prev);
 	}
 
-	if (serv_sock >= 0)
+	if (serv_sock >= 0) {
 		close(serv_sock);
-	if (socket_path)
+		serv_sock = -1;
+	}
+	if (socket_path) {
 		unlink(socket_path);
+		socket_path = NULL;
+	}
 
 #ifdef CONFIG_SQLITE
 	if (sqlite_db) {
@@ -965,6 +980,7 @@
 static void handle_term(int sig)
 {
 	printf("Signal %d - terminate\n", sig);
+	cleanup();
 	exit(0);
 }
 
@@ -973,7 +989,7 @@
 {
 	printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
 	       "database/authenticator\n"
-	       "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n"
+	       "Copyright (c) 2005-2024, Jouni Malinen <j@w1.fi>\n"
 	       "\n"
 	       "usage:\n"
 	       "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
@@ -1081,8 +1097,9 @@
 		signal(SIGTERM, handle_term);
 		signal(SIGINT, handle_term);
 
-		for (;;)
+		while (!stop)
 			process(serv_sock);
+		cleanup();
 	} else {
 		char buf[1000];
 		socket_path = NULL;
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 93524cf..338a16c 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1030,6 +1030,18 @@
 # Valid range: 0..20 TUs; default is 0 (disabled)
 #unsol_bcast_probe_resp_interval=0
 
+#channel_usage: Whether Channel Usage procedures is supported by AP.
+# 0 = Channel Usage support is disabled (default)
+# 1 = Channel Usage support is enabled
+#channel_usage=0
+
+#peer_to_peer_twt: Indicates an HE AP supports negotiating a peer-to-peer
+# TWT schedule that is requested by a non-AP STA to establish a
+# channel-usage-aidable BSS or an off-channel TDLS direct link.
+# 0 = Does not support Peer-to-peer TWT (default)
+# 1 = Supports Peer-to-peer TWT
+#peer_to_peer_twt=0
+
 ##### IEEE 802.11be related configuration #####################################
 
 #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
@@ -1207,6 +1219,10 @@
 # mka_priority (Priority of MKA Actor)
 # Range: 0..255 (default: 255)
 #
+# macsec_icv_indicator: Always include ICV indicator
+# 0 = ICV Indicator is not included when ICV has default length (default)
+# 1 = ICV Indicator is always included (compatibility mode)
+#
 # macsec_csindex: IEEE 802.1X/MACsec cipher suite
 # 0 = GCM-AES-128 (default)
 # 1 = GCM-AES-256 (default)
@@ -1786,6 +1802,9 @@
 # accounting while still enabling RADIUS authentication.
 #radius_server_acct_port=1813
 
+# Log received RADIUS accounting data
+#radius_server_acct_log=1
+
 # Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
 #radius_server_ipv6=1
 
@@ -1869,7 +1888,6 @@
 # FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
 # OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
 # DPP = Device Provisioning Protocol
-# OSEN = Hotspot 2.0 online signup with encryption
 # (dot11RSNAConfigAuthenticationSuitesTable)
 #wpa_key_mgmt=WPA-PSK WPA-EAP
 
@@ -2021,6 +2039,12 @@
 # 1 = enabled
 #beacon_prot=0
 
+# SPP (Signaling and Payload Protected) A-MSDU.
+# This depends on driver support and CCMP/GCMP cipher suite being used.
+# 0 = disabled (default)
+# 1 = enabled if driver indicates support for this
+#spp_amsdu=1
+
 # Association SA Query maximum timeout (in TU = 1.024 ms; for MFP)
 # (maximum time to wait for a SA Query response)
 # dot11AssociationSAQueryMaximumTimeout, 1...4294967295
@@ -2110,6 +2134,24 @@
 # contains and entry in the same format as sae_password uses.
 #sae_password_file=/tc/hostapd.sae_passwords
 
+# Tracking of SAE password use
+# While SAE design does not allow the AP to determine the used password robustly
+# if multiple password are configured without use of password identifiers, a
+# small number of such passwords might be usable with minimal impact to STAs.
+# This parameter can be used to enable such mechanism by tracking which password
+# STAs have tried and either succeeded or failed to complete authentication
+# with. Configured passwords are then tried one by one until success. This shows
+# up as a potential attack to the STA, though, and as such, may result in the AP
+# getting rejected after a couple of attempts. Only one password can be tested
+# per attempt, so this limits this mechanism to only a small number (e.g., 2-3)
+# passwords without showing significant usability issues with some STAs. This
+# is meant as a workaround until SAE with password identifiers is deployed on
+# STAs.
+# This parameter sets the maximum number of STA MAC addresses to track per
+# SAE password. This should be set sufficiently high to cover the expected
+# number of active STAs.
+#sae_track_password=0
+
 # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
 # This parameter defines how many open SAE instances can be in progress at the
 # same time before the anti-clogging mechanism is taken into use.
@@ -2298,6 +2340,16 @@
 #
 #ssid_protection=0
 
+# Known STA Identification
+# IEEE Std 802.11-2024 adds a mechanism that allows the SA Query procedure on
+# (re)association to the previously used AP to be skipped when that AP still
+# has a valid security association. This can speed up cases where a STA needs to
+# reassociate back to the same AP to update some association parameters.
+#
+# 0 = Do not process Known STA Identification (default)
+# 1 = Allow Known STA Identification to be used to skip SA Query procedure
+#known_sta_identification=0
+
 # RSNE/RSNXE override
 #
 # These parameters can be used to configure RSN parameters for STAs that support
@@ -2634,7 +2686,7 @@
 # message when acting as a Registrar. If skip_cred_build=1, this data will also
 # be able to override the Credential attribute that would have otherwise been
 # automatically generated based on network configuration. This configuration
-# option points to an external file that much contain the WPS Credential
+# option points to an external file that contains the WPS Credential
 # attribute(s) as binary data.
 #extra_cred=hostapd.cred
 
@@ -3031,9 +3083,6 @@
 # forging such frames to other stations in the BSS.
 #disable_dgaf=1
 
-# OSU Server-Only Authenticated L2 Encryption Network
-#osen=1
-
 # ANQP Domain ID (0..65535)
 # An identifier for a set of APs in an ESS that share the same common ANQP
 # information. 0 = Some of the ANQP information is unique to this AP (default).
@@ -3114,42 +3163,6 @@
 # @1@ = MAC address of the STA (colon separated hex octets)
 #hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123
 
-# OSU and Operator icons
-# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
-#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
-#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
-
-# OSU SSID (see ssid2 for format description)
-# This is the SSID used for all OSU connections to all the listed OSU Providers.
-#osu_ssid="example"
-
-# OSU Providers
-# One or more sets of following parameter. Each OSU provider is started by the
-# mandatory osu_server_uri item. The other parameters add information for the
-# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN
-# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI
-# value for OSEN authentication when using a shared BSS (Single SSID) for OSU.
-#
-#osu_server_uri=https://example.com/osu/
-#osu_friendly_name=eng:Example operator
-#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
-#osu_nai=anonymous@example.com
-#osu_nai2=anonymous@example.com
-#osu_method_list=1 0
-#osu_icon=icon32
-#osu_icon=icon64
-#osu_service_desc=eng:Example services
-#osu_service_desc=fin:Esimerkkipalveluja
-#
-#osu_server_uri=...
-
-# Operator Icons
-# Operator icons are specified using references to the hs20_icon entries
-# (Name subfield). This information, if present, is advertsised in the
-# Operator Icon Metadata ANQO-element.
-#operator_icon=icon32
-#operator_icon=icon64
-
 ##### Multiband Operation (MBO) ###############################################
 #
 # MBO enabled
@@ -3328,6 +3341,10 @@
 # (channel switch operating class is needed)
 #ecsa_ie_only=0
 #
+# Include only CSA IE without ECSA IE
+# (the operating class is not mentioned)
+#csa_ie_only=0
+
 # Delay EAPOL-Key messages 1/4 and 3/4 by not sending the frame until the last
 # attempt (wpa_pairwise_update_count). This will trigger a timeout on all
 # previous attempts and thus delays the frame. (testing only)
@@ -3411,6 +3428,18 @@
 # 2 = Enhanced multiple BSSID advertisement enabled.
 #mbssid=0
 #
+# Maximum number of BSSs that can be added into a Multiple BSSID set
+# This is a radio level parameter. If not set (or 0), the maximum is determined
+# automatically based on the configured BSSs which may limit dynamic addition
+# of new BSSs.
+#mbssid_max=0
+#
+# Multiple BSSID Index override
+# This is a BSS level parameter. If not set (or 0), the BSSID index is
+# determined automatically based on the configured BSSs which may limit dynamic
+# addition of new BSSs.
+#mbssid_index=0
+#
 # The transmitting interface should be added with the 'interface' option while
 # the non-transmitting interfaces should be added using the 'bss' option.
 # Security configuration should be added separately per interface, if required.
diff --git a/hostapd/main.c b/hostapd/main.c
index 50b9f04..5769fa0 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -162,6 +162,7 @@
 	struct wpa_driver_capa capa;
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_data *h_hapd = NULL;
+	void *shared_hapd = NULL;
 #endif /* CONFIG_IEEE80211BE */
 
 	if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
@@ -170,8 +171,11 @@
 	}
 
 #ifdef CONFIG_IEEE80211BE
-	if (conf->mld_ap)
+	if (conf->mld_ap) {
+		if (!hapd->mld)
+			hostapd_bss_setup_multi_link(hapd, iface->interfaces);
 		h_hapd = hostapd_mld_get_first_bss(hapd);
+	}
 
 	if (h_hapd) {
 		hapd->drv_priv = h_hapd->drv_priv;
@@ -255,6 +259,42 @@
 
 	params.own_addr = hapd->own_addr;
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->driver->can_share_drv &&
+	    hapd->driver->can_share_drv(hapd, &params, &shared_hapd)) {
+		char force_ifname[IFNAMSIZ];
+		const u8 *addr = params.bssid;
+		u8 if_addr[ETH_ALEN];
+
+		if (!shared_hapd) {
+			wpa_printf(MSG_ERROR, "Failed to get the shared drv");
+			os_free(params.bridge);
+			return -1;
+		}
+
+		/* Share an already initialized driver interface instance
+		 * using an AP mode BSS in it instead of adding a new driver
+		 * interface instance for the same driver. */
+		if (hostapd_if_add(shared_hapd, WPA_IF_AP_BSS,
+				   params.ifname, addr, hapd,
+				   &hapd->drv_priv, force_ifname, if_addr,
+				   params.num_bridge && params.bridge[0] ?
+				   params.bridge[0] : NULL,
+				   0)) {
+			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+				   MACSTR ")", MAC2STR(hapd->own_addr));
+			os_free(params.bridge);
+			return -1;
+		}
+		os_free(params.bridge);
+
+		hapd->interface_added = 1;
+		os_memcpy(params.own_addr, addr ? addr : if_addr, ETH_ALEN);
+
+		goto pre_setup_mld;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	hapd->drv_priv = hapd->driver->hapd_init(hapd, &params);
 	os_free(params.bridge);
 	if (hapd->drv_priv == NULL) {
@@ -265,6 +305,7 @@
 	}
 
 #ifdef CONFIG_IEEE80211BE
+pre_setup_mld:
 	/*
 	 * This is the first interface added to the AP MLD, so have the
 	 * interface hardware address be the MLD address, while the link address
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
index bb77341..a8ff6fc 100644
--- a/hs20/client/Android.mk
+++ b/hs20/client/Android.mk
@@ -30,10 +30,7 @@
 L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
 
-OBJS = spp_client.c
-OBJS += oma_dm_client.c
-OBJS += osu_client.c
-OBJS += est.c
+OBJS = osu_client.c
 OBJS += ../../src/common/wpa_ctrl.c
 OBJS += ../../src/common/wpa_helpers.c
 OBJS += ../../src/utils/xml-utils.c
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
index 4dcfe2d..9e65dc6 100644
--- a/hs20/client/Makefile
+++ b/hs20/client/Makefile
@@ -24,10 +24,7 @@
 endif
 endif
 
-OBJS=spp_client.o
-OBJS += oma_dm_client.o
-OBJS += osu_client.o
-OBJS += est.o
+OBJS = osu_client.o
 OBJS += ../../src/utils/xml-utils.o
 CFLAGS += -DCONFIG_CTRL_IFACE
 CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
diff --git a/hs20/client/devdetail.xml b/hs20/client/devdetail.xml
deleted file mode 100644
index 6d0389e..0000000
--- a/hs20/client/devdetail.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
-	<Ext>
-		<org.wi-fi>
-			<Wi-Fi>
-				<EAPMethodList>
-					<EAPMethod1>
-						<EAPType>13</EAPType>
-					</EAPMethod1>
-					<EAPMethod2>
-						<EAPType>21</EAPType>
-						<InnerMethod>MS-CHAP-V2</InnerMethod>
-					</EAPMethod2>
-					<EAPMethod3>
-						<EAPType>18</EAPType>
-					</EAPMethod3>
-					<EAPMethod4>
-						<EAPType>23</EAPType>
-					</EAPMethod4>
-					<EAPMethod5>
-						<EAPType>50</EAPType>
-					</EAPMethod5>
-				</EAPMethodList>
-				<ManufacturingCertificate>false</ManufacturingCertificate>
-				<Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
-				<IMSI>310026000000000</IMSI>
-				<IMEI_MEID>imei:490123456789012</IMEI_MEID>
-				<ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
-				<Ops>
-					<launchBrowserToURI></launchBrowserToURI>
-					<negotiateClientCertTLS></negotiateClientCertTLS>
-					<getCertificate></getCertificate>
-				</Ops>
-			</Wi-Fi>
-		</org.wi-fi>
-	</Ext>
-	<URI>
-		<MaxDepth>0</MaxDepth>
-		<MaxTotLen>0</MaxTotLen>
-		<MaxSegLen>0</MaxSegLen>
-	</URI>
-	<DevType>MobilePhone</DevType>
-	<OEM>Manufacturer</OEM>
-	<FwV>1.0</FwV>
-	<SwV>1.0</SwV>
-	<HwV>1.0</HwV>
-	<LrgObj>false</LrgObj>
-</DevDetail>
diff --git a/hs20/client/devinfo.xml b/hs20/client/devinfo.xml
deleted file mode 100644
index d48a520..0000000
--- a/hs20/client/devinfo.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
-	<DevId>urn:Example:HS20-station:123456</DevId>
-	<Man>Manufacturer</Man>
-	<Mod>HS20-station</Mod>
-	<DmV>1.2</DmV>
-	<Lang>en</Lang>
-</DevInfo>
diff --git a/hs20/client/est.c b/hs20/client/est.c
deleted file mode 100644
index 425b72d..0000000
--- a/hs20/client/est.c
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * Hotspot 2.0 OSU client - EST client
- * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs7.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
-#include <openssl/opensslv.h>
-#include <openssl/buffer.h>
-
-#include "common.h"
-#include "utils/base64.h"
-#include "utils/xml-utils.h"
-#include "utils/http-utils.h"
-#include "osu_client.h"
-
-
-static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
-			 size_t len, char *pem_file, char *der_file)
-{
-#ifdef OPENSSL_IS_BORINGSSL
-	CBS pkcs7_cbs;
-#else /* OPENSSL_IS_BORINGSSL */
-	PKCS7 *p7 = NULL;
-	const unsigned char *p = pkcs7;
-#endif /* OPENSSL_IS_BORINGSSL */
-	STACK_OF(X509) *certs;
-	int i, num, ret = -1;
-	BIO *out = NULL;
-
-#ifdef OPENSSL_IS_BORINGSSL
-	certs = sk_X509_new_null();
-	if (!certs)
-		goto fail;
-	CBS_init(&pkcs7_cbs, pkcs7, len);
-	if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
-		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		write_result(ctx, "Could not parse PKCS#7 object from EST");
-		goto fail;
-	}
-#else /* OPENSSL_IS_BORINGSSL */
-	p7 = d2i_PKCS7(NULL, &p, len);
-	if (p7 == NULL) {
-		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		write_result(ctx, "Could not parse PKCS#7 object from EST");
-		goto fail;
-	}
-
-	switch (OBJ_obj2nid(p7->type)) {
-	case NID_pkcs7_signed:
-		certs = p7->d.sign->cert;
-		break;
-	case NID_pkcs7_signedAndEnveloped:
-		certs = p7->d.signed_and_enveloped->cert;
-		break;
-	default:
-		certs = NULL;
-		break;
-	}
-#endif /* OPENSSL_IS_BORINGSSL */
-
-	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
-		wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
-		write_result(ctx, "No certificates found in PKCS#7 object");
-		goto fail;
-	}
-
-	if (der_file) {
-		FILE *f = fopen(der_file, "wb");
-		if (f == NULL)
-			goto fail;
-		i2d_X509_fp(f, sk_X509_value(certs, 0));
-		fclose(f);
-	}
-
-	if (pem_file) {
-		out = BIO_new(BIO_s_file());
-		if (out == NULL ||
-		    BIO_write_filename(out, pem_file) <= 0)
-			goto fail;
-
-		for (i = 0; i < num; i++) {
-			X509 *cert = sk_X509_value(certs, i);
-			X509_print(out, cert);
-			PEM_write_bio_X509(out, cert);
-			BIO_puts(out, "\n");
-		}
-	}
-
-	ret = 0;
-
-fail:
-#ifdef OPENSSL_IS_BORINGSSL
-	if (certs)
-		sk_X509_pop_free(certs, X509_free);
-#else /* OPENSSL_IS_BORINGSSL */
-	PKCS7_free(p7);
-#endif /* OPENSSL_IS_BORINGSSL */
-	if (out)
-		BIO_free_all(out);
-
-	return ret;
-}
-
-
-int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
-{
-	char *buf, *resp;
-	size_t buflen;
-	unsigned char *pkcs7;
-	size_t pkcs7_len, resp_len;
-	int res;
-
-	buflen = os_strlen(url) + 100;
-	buf = os_malloc(buflen);
-	if (buf == NULL)
-		return -1;
-
-	os_snprintf(buf, buflen, "%s/cacerts", url);
-	wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
-	write_summary(ctx, "Download EST cacerts from %s", buf);
-	ctx->no_osu_cert_validation = 1;
-	http_ocsp_set(ctx->http, 1);
-	res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
-				 ctx->ca_fname);
-	http_ocsp_set(ctx->http,
-		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
-	ctx->no_osu_cert_validation = 0;
-	if (res < 0) {
-		wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
-			   buf);
-		write_result(ctx, "Failed to download EST cacerts from %s",
-			     buf);
-		os_free(buf);
-		return -1;
-	}
-	os_free(buf);
-
-	resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
-	if (resp == NULL) {
-		wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
-		write_result(ctx, "Could not read EST cacerts");
-		return -1;
-	}
-
-	pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
-	if (pkcs7 && pkcs7_len < resp_len / 2) {
-		wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
-			   (unsigned int) pkcs7_len, (unsigned int) resp_len);
-		os_free(pkcs7);
-		pkcs7 = NULL;
-	}
-	if (pkcs7 == NULL) {
-		wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
-		pkcs7 = os_malloc(resp_len);
-		if (pkcs7) {
-			os_memcpy(pkcs7, resp, resp_len);
-			pkcs7_len = resp_len;
-		}
-	}
-	os_free(resp);
-
-	if (pkcs7 == NULL) {
-		wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
-		write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
-		return -1;
-	}
-
-	res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
-			    NULL);
-	os_free(pkcs7);
-	if (res < 0) {
-		wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
-		write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
-		return -1;
-	}
-	unlink("Cert/est-cacerts.txt");
-
-	return 0;
-}
-
-
-/*
- * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
- *
- * AttrOrOID ::= CHOICE {
- *   oid OBJECT IDENTIFIER,
- *   attribute Attribute }
- *
- * Attribute ::= SEQUENCE {
- *   type OBJECT IDENTIFIER,
- *   values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
- */
-
-typedef struct {
-	ASN1_OBJECT *type;
-	STACK_OF(ASN1_OBJECT) *values;
-} Attribute;
-
-typedef struct {
-	int type;
-	union {
-		ASN1_OBJECT *oid;
-		Attribute *attribute;
-	} d;
-} AttrOrOID;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-DEFINE_STACK_OF(AttrOrOID)
-#endif
-
-typedef struct {
-	int type;
-	STACK_OF(AttrOrOID) *attrs;
-} CsrAttrs;
-
-ASN1_SEQUENCE(Attribute) = {
-	ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
-	ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
-} ASN1_SEQUENCE_END(Attribute);
-
-ASN1_CHOICE(AttrOrOID) = {
-	ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
-	ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
-} ASN1_CHOICE_END(AttrOrOID);
-
-ASN1_CHOICE(CsrAttrs) = {
-	ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
-} ASN1_CHOICE_END(CsrAttrs);
-
-IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
-
-
-static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
-			     STACK_OF(X509_EXTENSION) *exts)
-{
-	char txt[100];
-	int res;
-
-	if (!oid)
-		return;
-
-	res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
-	if (res < 0 || res >= (int) sizeof(txt))
-		return;
-
-	if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
-		wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
-	} else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
-		wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
-	} else {
-		wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
-	}
-}
-
-
-static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
-				 STACK_OF(ASN1_OBJECT) *values,
-				 STACK_OF(X509_EXTENSION) *exts)
-{
-	char txt[100];
-	int i, num, res;
-
-	num = sk_ASN1_OBJECT_num(values);
-	for (i = 0; i < num; i++) {
-		ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
-
-		res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
-		if (res < 0 || res >= (int) sizeof(txt))
-			continue;
-
-		if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
-			wpa_printf(MSG_INFO, "TODO: extReq macAddress");
-		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
-			wpa_printf(MSG_INFO, "TODO: extReq imei");
-		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
-			wpa_printf(MSG_INFO, "TODO: extReq meid");
-		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
-			wpa_printf(MSG_INFO, "TODO: extReq DevId");
-		} else {
-			wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
-				   txt);
-		}
-	}
-}
-
-
-static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
-			      STACK_OF(X509_EXTENSION) *exts)
-{
-	char txt[100], txt2[100];
-	int i, num, res;
-
-	if (!attr || !attr->type || !attr->values)
-		return;
-
-	res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
-	if (res < 0 || res >= (int) sizeof(txt))
-		return;
-
-	if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
-		add_csrattrs_ext_req(ctx, attr->values, exts);
-		return;
-	}
-
-	num = sk_ASN1_OBJECT_num(attr->values);
-	for (i = 0; i < num; i++) {
-		ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
-
-		res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
-		if (res < 0 || res >= (int) sizeof(txt2))
-			continue;
-
-		wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
-			   txt, txt2);
-	}
-}
-
-
-static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
-			 STACK_OF(X509_EXTENSION) *exts)
-{
-	int i, num;
-
-	if (!csrattrs || ! csrattrs->attrs)
-		return;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-	num = sk_AttrOrOID_num(csrattrs->attrs);
-#else
-	num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
-#endif
-	for (i = 0; i < num; i++) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-		AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i);
-#else
-		AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
-#endif
-		switch (ao->type) {
-		case 0:
-			add_csrattrs_oid(ctx, ao->d.oid, exts);
-			break;
-		case 1:
-			add_csrattrs_attr(ctx, ao->d.attribute, exts);
-			break;
-		}
-	}
-}
-
-
-static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
-			char *csr_pem, char *est_req, char *old_cert,
-			CsrAttrs *csrattrs)
-{
-	EVP_PKEY_CTX *pctx = NULL;
-	EVP_PKEY *pkey = NULL;
-	X509_REQ *req = NULL;
-	int ret = -1;
-	unsigned int val;
-	X509_NAME *subj = NULL;
-	char name[100];
-	STACK_OF(X509_EXTENSION) *exts = NULL;
-	X509_EXTENSION *ex;
-	BIO *out;
-	CONF *ctmp = NULL;
-
-	wpa_printf(MSG_INFO, "Generate RSA private key");
-	write_summary(ctx, "Generate RSA private key");
-	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
-	if (!pctx)
-		return -1;
-
-	if (EVP_PKEY_keygen_init(pctx) <= 0)
-		goto fail;
-
-	if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
-		goto fail;
-
-	if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
-		goto fail;
-	EVP_PKEY_CTX_free(pctx);
-	pctx = NULL;
-
-	if (key_pem) {
-		FILE *f = fopen(key_pem, "wb");
-		if (f == NULL)
-			goto fail;
-		if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
-			wpa_printf(MSG_INFO, "Could not write private key: %s",
-				   ERR_error_string(ERR_get_error(), NULL));
-			fclose(f);
-			goto fail;
-		}
-		fclose(f);
-	}
-
-	wpa_printf(MSG_INFO, "Generate CSR");
-	write_summary(ctx, "Generate CSR");
-	req = X509_REQ_new();
-	if (req == NULL)
-		goto fail;
-
-	if (old_cert) {
-		FILE *f;
-		X509 *cert;
-		int res;
-
-		f = fopen(old_cert, "r");
-		if (f == NULL)
-			goto fail;
-		cert = PEM_read_X509(f, NULL, NULL, NULL);
-		fclose(f);
-
-		if (cert == NULL)
-			goto fail;
-		res = X509_REQ_set_subject_name(req,
-						X509_get_subject_name(cert));
-		X509_free(cert);
-		if (!res)
-			goto fail;
-	} else {
-		os_get_random((u8 *) &val, sizeof(val));
-		os_snprintf(name, sizeof(name), "cert-user-%u", val);
-		subj = X509_NAME_new();
-		if (subj == NULL ||
-		    !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
-						(unsigned char *) name,
-						-1, -1, 0) ||
-		    !X509_REQ_set_subject_name(req, subj))
-			goto fail;
-		X509_NAME_free(subj);
-		subj = NULL;
-	}
-
-	if (!X509_REQ_set_pubkey(req, pkey))
-		goto fail;
-
-	exts = sk_X509_EXTENSION_new_null();
-	if (!exts)
-		goto fail;
-
-	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
-				  "CA:FALSE");
-	if (ex == NULL ||
-	    !sk_X509_EXTENSION_push(exts, ex))
-		goto fail;
-
-	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
-				  "nonRepudiation,digitalSignature,keyEncipherment");
-	if (ex == NULL ||
-	    !sk_X509_EXTENSION_push(exts, ex))
-		goto fail;
-
-	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
-				  "1.3.6.1.4.1.40808.1.1.2");
-	if (ex == NULL ||
-	    !sk_X509_EXTENSION_push(exts, ex))
-		goto fail;
-
-	add_csrattrs(ctx, csrattrs, exts);
-
-	if (!X509_REQ_add_extensions(req, exts))
-		goto fail;
-	sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
-	exts = NULL;
-
-	if (!X509_REQ_sign(req, pkey, EVP_sha256()))
-		goto fail;
-
-	out = BIO_new(BIO_s_mem());
-	if (out) {
-		char *txt;
-		size_t rlen;
-
-#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
-		X509_REQ_print(out, req);
-#endif
-		rlen = BIO_ctrl_pending(out);
-		txt = os_malloc(rlen + 1);
-		if (txt) {
-			int res = BIO_read(out, txt, rlen);
-			if (res > 0) {
-				txt[res] = '\0';
-				wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
-					   txt);
-			}
-			os_free(txt);
-		}
-		BIO_free(out);
-	}
-
-	if (csr_pem) {
-		FILE *f = fopen(csr_pem, "w");
-		if (f == NULL)
-			goto fail;
-#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
-		X509_REQ_print_fp(f, req);
-#endif
-		if (!PEM_write_X509_REQ(f, req)) {
-			fclose(f);
-			goto fail;
-		}
-		fclose(f);
-	}
-
-	if (est_req) {
-		BIO *mem = BIO_new(BIO_s_mem());
-		BUF_MEM *ptr;
-		char *pos, *end, *buf_end;
-		FILE *f;
-
-		if (mem == NULL)
-			goto fail;
-		if (!PEM_write_bio_X509_REQ(mem, req)) {
-			BIO_free(mem);
-			goto fail;
-		}
-
-		BIO_get_mem_ptr(mem, &ptr);
-		pos = ptr->data;
-		buf_end = pos + ptr->length;
-
-		/* Remove START/END lines */
-		while (pos < buf_end && *pos != '\n')
-			pos++;
-		if (pos == buf_end) {
-			BIO_free(mem);
-			goto fail;
-		}
-		pos++;
-
-		end = pos;
-		while (end < buf_end && *end != '-')
-			end++;
-
-		f = fopen(est_req, "w");
-		if (f == NULL) {
-			BIO_free(mem);
-			goto fail;
-		}
-		fwrite(pos, end - pos, 1, f);
-		fclose(f);
-
-		BIO_free(mem);
-	}
-
-	ret = 0;
-fail:
-	if (exts)
-		sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
-	if (subj)
-		X509_NAME_free(subj);
-	if (req)
-		X509_REQ_free(req);
-	if (pkey)
-		EVP_PKEY_free(pkey);
-	if (pctx)
-		EVP_PKEY_CTX_free(pctx);
-	return ret;
-}
-
-
-int est_build_csr(struct hs20_osu_client *ctx, const char *url)
-{
-	char *buf;
-	size_t buflen;
-	int res;
-	char old_cert_buf[200];
-	char *old_cert = NULL;
-	CsrAttrs *csrattrs = NULL;
-
-	buflen = os_strlen(url) + 100;
-	buf = os_malloc(buflen);
-	if (buf == NULL)
-		return -1;
-
-	os_snprintf(buf, buflen, "%s/csrattrs", url);
-	wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
-	write_summary(ctx, "Download EST csrattrs from %s", buf);
-	ctx->no_osu_cert_validation = 1;
-	http_ocsp_set(ctx->http, 1);
-	res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
-				 ctx->ca_fname);
-	http_ocsp_set(ctx->http,
-		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
-	ctx->no_osu_cert_validation = 0;
-	os_free(buf);
-	if (res < 0) {
-		wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
-	} else {
-		size_t resp_len;
-		char *resp;
-		unsigned char *attrs;
-		const unsigned char *pos;
-		size_t attrs_len;
-
-		resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
-		if (resp == NULL) {
-			wpa_printf(MSG_INFO, "Could not read csrattrs");
-			return -1;
-		}
-
-		attrs = base64_decode(resp, resp_len, &attrs_len);
-		os_free(resp);
-
-		if (attrs == NULL) {
-			wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
-			return -1;
-		}
-		unlink("Cert/est-csrattrs.txt");
-
-		pos = attrs;
-		csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
-		os_free(attrs);
-		if (csrattrs == NULL) {
-			wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
-			/* Continue assuming no additional requirements */
-		}
-	}
-
-	if (ctx->client_cert_present) {
-		os_snprintf(old_cert_buf, sizeof(old_cert_buf),
-			    "SP/%s/client-cert.pem", ctx->fqdn);
-		old_cert = old_cert_buf;
-	}
-
-	res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
-			   "Cert/est-req.b64", old_cert, csrattrs);
-	if (csrattrs)
-		CsrAttrs_free(csrattrs);
-
-	return res;
-}
-
-
-int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
-		      const char *user, const char *pw)
-{
-	char *buf, *resp, *req, *req2;
-	size_t buflen, resp_len, len, pkcs7_len;
-	unsigned char *pkcs7;
-	char client_cert_buf[200];
-	char client_key_buf[200];
-	const char *client_cert = NULL, *client_key = NULL;
-	int res;
-
-	req = os_readfile("Cert/est-req.b64", &len);
-	if (req == NULL) {
-		wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
-		return -1;
-	}
-	req2 = os_realloc(req, len + 1);
-	if (req2 == NULL) {
-		os_free(req);
-		return -1;
-	}
-	req2[len] = '\0';
-	req = req2;
-	wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
-
-	buflen = os_strlen(url) + 100;
-	buf = os_malloc(buflen);
-	if (buf == NULL) {
-		os_free(req);
-		return -1;
-	}
-
-	if (ctx->client_cert_present) {
-		os_snprintf(buf, buflen, "%s/simplereenroll", url);
-		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
-			    "SP/%s/client-cert.pem", ctx->fqdn);
-		client_cert = client_cert_buf;
-		os_snprintf(client_key_buf, sizeof(client_key_buf),
-			    "SP/%s/client-key.pem", ctx->fqdn);
-		client_key = client_key_buf;
-	} else
-		os_snprintf(buf, buflen, "%s/simpleenroll", url);
-	wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
-	write_summary(ctx, "EST simpleenroll URL: %s", buf);
-	ctx->no_osu_cert_validation = 1;
-	http_ocsp_set(ctx->http, 1);
-	resp = http_post(ctx->http, buf, req, "application/pkcs10",
-			 "Content-Transfer-Encoding: base64",
-			 ctx->ca_fname, user, pw, client_cert, client_key,
-			 &resp_len);
-	http_ocsp_set(ctx->http,
-		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
-	ctx->no_osu_cert_validation = 0;
-	os_free(buf);
-	if (resp == NULL) {
-		wpa_printf(MSG_INFO, "EST certificate enrollment failed");
-		write_result(ctx, "EST certificate enrollment failed");
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
-
-	pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
-	if (pkcs7 == NULL) {
-		wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
-		pkcs7 = os_malloc(resp_len);
-		if (pkcs7) {
-			os_memcpy(pkcs7, resp, resp_len);
-			pkcs7_len = resp_len;
-		}
-	}
-	os_free(resp);
-
-	if (pkcs7 == NULL) {
-		wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
-		write_result(ctx, "Failed to parse EST simpleenroll base64 response");
-		return -1;
-	}
-
-	res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
-			    "Cert/est_cert.der");
-	os_free(pkcs7);
-
-	if (res < 0) {
-		wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
-		write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
-		return -1;
-	}
-
-	wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
-		   ctx->client_cert_present ? "re" : "");
-	write_summary(ctx, "EST simple%senroll completed successfully",
-		      ctx->client_cert_present ? "re" : "");
-
-	return 0;
-}
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
deleted file mode 100644
index bcd68b8..0000000
--- a/hs20/client/oma_dm_client.c
+++ /dev/null
@@ -1,1398 +0,0 @@
-/*
- * Hotspot 2.0 - OMA DM client
- * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "wpa_helpers.h"
-#include "xml-utils.h"
-#include "http-utils.h"
-#include "utils/browser.h"
-#include "osu_client.h"
-
-
-#define DM_SERVER_INITIATED_MGMT 1200
-#define DM_CLIENT_INITIATED_MGMT 1201
-#define DM_GENERIC_ALERT 1226
-
-/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
-#define DM_RESP_OK 200
-#define DM_RESP_AUTH_ACCEPTED 212
-#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
-#define DM_RESP_NOT_EXECUTED 215
-#define DM_RESP_ATOMIC_ROLL_BACK_OK 216
-#define DM_RESP_NOT_MODIFIED 304
-#define DM_RESP_BAD_REQUEST 400
-#define DM_RESP_UNAUTHORIZED 401
-#define DM_RESP_FORBIDDEN 403
-#define DM_RESP_NOT_FOUND 404
-#define DM_RESP_COMMAND_NOT_ALLOWED 405
-#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
-#define DM_RESP_MISSING_CREDENTIALS 407
-#define DM_RESP_CONFLICT 409
-#define DM_RESP_GONE 410
-#define DM_RESP_INCOMPLETE_COMMAND 412
-#define DM_RESP_REQ_ENTITY_TOO_LARGE 413
-#define DM_RESP_URI_TOO_LONG 414
-#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
-#define DM_RESP_REQ_TOO_BIG 416
-#define DM_RESP_ALREADY_EXISTS 418
-#define DM_RESP_DEVICE_FULL 420
-#define DM_RESP_SIZE_MISMATCH 424
-#define DM_RESP_PERMISSION_DENIED 425
-#define DM_RESP_COMMAND_FAILED 500
-#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
-#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
-
-#define DM_HS20_SUBSCRIPTION_CREATION \
-	"org.wi-fi.hotspot2dot0.SubscriptionCreation"
-#define DM_HS20_SUBSCRIPTION_PROVISIONING \
-	"org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
-#define DM_HS20_SUBSCRIPTION_REMEDIATION \
-	"org.wi-fi.hotspot2dot0.SubscriptionRemediation"
-#define DM_HS20_POLICY_UPDATE \
-	"org.wi-fi.hotspot2dot0.PolicyUpdate"
-
-#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
-#define DM_URI_LAUNCH_BROWSER \
-	"./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
-
-
-static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
-		     const char *locuri, const char *data);
-
-
-static const char * int2str(int val)
-{
-	static char buf[20];
-	snprintf(buf, sizeof(buf), "%d", val);
-	return buf;
-}
-
-
-static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
-				       xml_node_t *node)
-{
-	xml_node_t *locuri;
-	char *uri, *ret = NULL;
-
-	locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
-	if (locuri == NULL)
-		return NULL;
-
-	uri = xml_node_get_text(ctx->xml, locuri);
-	if (uri)
-		ret = os_strdup(uri);
-	xml_node_get_text_free(ctx->xml, uri);
-	return ret;
-}
-
-
-static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
-			      const char *element, const char *uri)
-{
-	xml_node_t *node;
-
-	node = xml_node_create(ctx->xml, parent, NULL, element);
-	if (node == NULL)
-		return;
-	xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
-}
-
-
-static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
-				     const char *url, int msgid)
-{
-	xml_node_t *syncml, *synchdr;
-	xml_namespace_t *ns;
-
-	if (!ctx->devid) {
-		wpa_printf(MSG_ERROR,
-			   "DevId from devinfo.xml is not available - cannot use OMA DM");
-		return NULL;
-	}
-
-	syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
-				      "SyncML");
-
-	synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
-	xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
-	xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
-	xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
-	xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
-
-	oma_dm_add_locuri(ctx, synchdr, "Target", url);
-	oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
-
-	return syncml;
-}
-
-
-static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
-			     int cmdid)
-{
-	xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
-}
-
-
-static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
-			      int cmdid, int data)
-{
-	xml_node_t *node;
-
-	node = xml_node_create(ctx->xml, parent, NULL, "Alert");
-	if (node == NULL)
-		return NULL;
-	oma_dm_add_cmdid(ctx, node, cmdid);
-	xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
-
-	return node;
-}
-
-
-static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
-			       int msgref, int cmdref, int cmdid,
-			       const char *cmd, int data, const char *targetref)
-{
-	xml_node_t *node;
-
-	node = xml_node_create(ctx->xml, parent, NULL, "Status");
-	if (node == NULL)
-		return NULL;
-	oma_dm_add_cmdid(ctx, node, cmdid);
-	xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
-	if (cmdref)
-		xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
-				     int2str(cmdref));
-	xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
-	xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
-	if (targetref) {
-		xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
-				     targetref);
-	}
-
-	return node;
-}
-
-
-static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
-				int msgref, int cmdref, int cmdid,
-				const char *locuri, const char *data)
-{
-	xml_node_t *node;
-
-	node = xml_node_create(ctx->xml, parent, NULL, "Results");
-	if (node == NULL)
-		return NULL;
-
-	oma_dm_add_cmdid(ctx, node, cmdid);
-	xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
-	xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
-	add_item(ctx, node, locuri, data);
-
-	return node;
-}
-
-
-static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
-		     const char *fname)
-{
-	xml_node_t *fnode, *tnds;
-	char *str;
-
-	fnode = node_from_file(ctx->xml, fname);
-	if (!fnode)
-		return NULL;
-	tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
-	xml_node_free(ctx->xml, fnode);
-	if (!tnds)
-		return NULL;
-
-	str = xml_node_to_str(ctx->xml, tnds);
-	xml_node_free(ctx->xml, tnds);
-	if (str == NULL)
-		return NULL;
-	wpa_printf(MSG_INFO, "MgmtTree: %s", str);
-
-	return str;
-}
-
-
-static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
-		     const char *locuri, const char *data)
-{
-	xml_node_t *item, *node;
-
-	item = xml_node_create(ctx->xml, parent, NULL, "Item");
-	oma_dm_add_locuri(ctx, item, "Source", locuri);
-	node = xml_node_create(ctx->xml, item, NULL, "Meta");
-	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
-				"Chr");
-	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
-				"text/plain");
-	xml_node_create_text(ctx->xml, item, NULL, "Data", data);
-}
-
-
-static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
-				int cmdid)
-{
-	xml_node_t *info, *child, *replace;
-	const char *name;
-	char locuri[200], *txt;
-
-	info = node_from_file(ctx->xml, "devinfo.xml");
-	if (info == NULL) {
-		wpa_printf(MSG_INFO, "Could not read devinfo.xml");
-		return;
-	}
-
-	replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
-	if (replace == NULL) {
-		xml_node_free(ctx->xml, info);
-		return;
-	}
-	oma_dm_add_cmdid(ctx, replace, cmdid);
-
-	xml_node_for_each_child(ctx->xml, child, info) {
-		xml_node_for_each_check(ctx->xml, child);
-		name = xml_node_get_localname(ctx->xml, child);
-		os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
-		txt = xml_node_get_text(ctx->xml, child);
-		if (txt) {
-			add_item(ctx, replace, locuri, txt);
-			xml_node_get_text_free(ctx->xml, txt);
-		}
-	}
-
-	xml_node_free(ctx->xml, info);
-}
-
-
-static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
-					  xml_node_t *syncbody,
-					  int cmdid, const char *oper,
-					  const char *data)
-{
-	xml_node_t *node, *item;
-	char buf[200];
-
-	node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
-
-	item = xml_node_create(ctx->xml, node, NULL, "Item");
-	oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
-	node = xml_node_create(ctx->xml, item, NULL, "Meta");
-	snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
-	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
-	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
-				"xml");
-	xml_node_create_text(ctx->xml, item, NULL, "Data", data);
-}
-
-
-static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
-				   const char *url, int msgid, const char *oper)
-{
-	xml_node_t *syncml, *syncbody;
-	char *str;
-	int cmdid = 0;
-
-	syncml = oma_dm_build_hdr(ctx, url, msgid);
-	if (syncml == NULL)
-		return NULL;
-
-	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
-	if (syncbody == NULL) {
-		xml_node_free(ctx->xml, syncml);
-		return NULL;
-	}
-
-	cmdid++;
-	add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
-
-	str = mo_str(ctx, NULL, "devdetail.xml");
-	if (str == NULL) {
-		xml_node_free(ctx->xml, syncml);
-		return NULL;
-	}
-	cmdid++;
-	oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
-	os_free(str);
-
-	cmdid++;
-	add_replace_devinfo(ctx, syncbody, cmdid);
-
-	xml_node_create(ctx->xml, syncbody, NULL, "Final");
-
-	return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
-					   const char *url, int msgid)
-{
-	xml_node_t *syncml;
-
-	syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
-	if (syncml)
-		debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
-
-	return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
-					    const char *url, int msgid)
-{
-	xml_node_t *syncml;
-
-	syncml = build_oma_dm_1(ctx, url, msgid,
-				DM_HS20_SUBSCRIPTION_PROVISIONING);
-	if (syncml)
-		debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
-
-	return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
-					   const char *url, int msgid)
-{
-	xml_node_t *syncml;
-
-	syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
-	if (syncml)
-		debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
-
-	return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
-					   const char *url, int msgid)
-{
-	xml_node_t *syncml;
-
-	syncml = build_oma_dm_1(ctx, url, msgid,
-				DM_HS20_SUBSCRIPTION_REMEDIATION);
-	if (syncml)
-		debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
-
-	return syncml;
-}
-
-
-static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
-	xml_node_t *node;
-	char *data;
-	int res;
-
-	node = get_node(ctx->xml, exec, "Item/Data");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No Data node found");
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	data = xml_node_get_text(ctx->xml, node);
-	if (data == NULL) {
-		wpa_printf(MSG_INFO, "Invalid data");
-		return DM_RESP_BAD_REQUEST;
-	}
-	wpa_printf(MSG_INFO, "Data: %s", data);
-	wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
-	write_summary(ctx, "Launch browser to URI '%s'", data);
-	res = hs20_web_browser(data, 1);
-	xml_node_get_text_free(ctx->xml, data);
-	if (res > 0) {
-		wpa_printf(MSG_INFO, "User response in browser completed successfully");
-		write_summary(ctx, "User response in browser completed successfully");
-		return DM_RESP_OK;
-	} else {
-		wpa_printf(MSG_INFO, "Failed to receive user response");
-		write_summary(ctx, "Failed to receive user response");
-		return DM_RESP_COMMAND_FAILED;
-	}
-}
-
-
-static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
-	xml_node_t *node, *getcert;
-	char *data;
-	const char *name;
-	int res;
-
-	wpa_printf(MSG_INFO, "Client certificate enrollment");
-	write_summary(ctx, "Client certificate enrollment");
-
-	node = get_node(ctx->xml, exec, "Item/Data");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No Data node found");
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	data = xml_node_get_text(ctx->xml, node);
-	if (data == NULL) {
-		wpa_printf(MSG_INFO, "Invalid data");
-		return DM_RESP_BAD_REQUEST;
-	}
-	wpa_printf(MSG_INFO, "Data: %s", data);
-	getcert = xml_node_from_buf(ctx->xml, data);
-	xml_node_get_text_free(ctx->xml, data);
-
-	if (getcert == NULL) {
-		wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
-
-	name = xml_node_get_localname(ctx->xml, getcert);
-	if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
-		wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
-			   name);
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	res = osu_get_certificate(ctx, getcert);
-
-	xml_node_free(ctx->xml, getcert);
-
-	return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
-}
-
-
-static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
-	char *locuri;
-	int ret;
-
-	locuri = oma_dm_get_target_locuri(ctx, exec);
-	if (locuri == NULL) {
-		wpa_printf(MSG_INFO, "No Target LocURI node found");
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
-
-	if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
-			  "launchBrowserToURI") == 0) {
-		ret = oma_dm_exec_browser(ctx, exec);
-	} else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
-			  "getCertificate") == 0) {
-		ret = oma_dm_exec_get_cert(ctx, exec);
-	} else {
-		wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
-		ret = DM_RESP_NOT_FOUND;
-	}
-	os_free(locuri);
-
-	return ret;
-}
-
-
-static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
-			  xml_node_t *add, xml_node_t *pps,
-			  const char *pps_fname)
-{
-	const char *pos;
-	size_t fqdn_len;
-	xml_node_t *node, *tnds, *unode, *pps_node;
-	char *data, *uri, *upos, *end;
-	int use_tnds = 0;
-	size_t uri_len;
-
-	wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
-
-	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
-		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos = locuri + 8;
-
-	if (ctx->fqdn == NULL)
-		return DM_RESP_COMMAND_FAILED;
-	fqdn_len = os_strlen(ctx->fqdn);
-	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
-	    pos[fqdn_len] != '/') {
-		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
-			   ctx->fqdn);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos += fqdn_len + 1;
-
-	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
-		wpa_printf(MSG_INFO,
-			   "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
-			   ctx->fqdn);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos += 24;
-
-	wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
-
-	pps_node = get_node(ctx->xml, pps, pos);
-	if (pps_node) {
-		wpa_printf(MSG_INFO, "Specified PPS node exists already");
-		return DM_RESP_ALREADY_EXISTS;
-	}
-
-	uri = os_strdup(pos);
-	if (uri == NULL)
-		return DM_RESP_COMMAND_FAILED;
-	while (!pps_node) {
-		upos = os_strrchr(uri, '/');
-		if (!upos)
-			break;
-		upos[0] = '\0';
-		pps_node = get_node(ctx->xml, pps, uri);
-		wpa_printf(MSG_INFO, "Node %s %s", uri,
-			   pps_node ? "exists" : "does not exist");
-	}
-
-	wpa_printf(MSG_INFO, "Parent URI: %s", uri);
-
-	if (!pps_node) {
-		/* Add at root of PPS MO */
-		pps_node = pps;
-	}
-
-	uri_len = os_strlen(uri);
-	os_strlcpy(uri, pos + uri_len, os_strlen(pos));
-	upos = uri;
-	while (*upos == '/')
-		upos++;
-	wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
-
-	for (;;) {
-		end = os_strchr(upos, '/');
-		if (!end)
-			break;
-		*end = '\0';
-		wpa_printf(MSG_INFO, "Adding interim node %s", upos);
-		pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
-		if (pps_node == NULL) {
-			os_free(uri);
-			return DM_RESP_COMMAND_FAILED;
-		}
-		upos = end + 1;
-	}
-
-	wpa_printf(MSG_INFO, "Adding node %s", upos);
-
-	node = get_node(ctx->xml, add, "Item/Meta/Type");
-	if (node) {
-		char *type;
-		type = xml_node_get_text(ctx->xml, node);
-		if (type == NULL) {
-			wpa_printf(MSG_ERROR, "Could not find type text");
-			os_free(uri);
-			return DM_RESP_BAD_REQUEST;
-		}
-		use_tnds = node &&
-			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
-	}
-
-	node = get_node(ctx->xml, add, "Item/Data");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No Add/Item/Data found");
-		os_free(uri);
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	data = xml_node_get_text(ctx->xml, node);
-	if (data == NULL) {
-		wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
-		os_free(uri);
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
-
-	if (use_tnds) {
-		tnds = xml_node_from_buf(ctx->xml, data);
-		xml_node_get_text_free(ctx->xml, data);
-		if (tnds == NULL) {
-			wpa_printf(MSG_INFO,
-				   "Could not parse Add/Item/Data text");
-			os_free(uri);
-			return DM_RESP_BAD_REQUEST;
-		}
-
-		unode = tnds_to_mo(ctx->xml, tnds);
-		xml_node_free(ctx->xml, tnds);
-		if (unode == NULL) {
-			wpa_printf(MSG_INFO, "Could not parse TNDS text");
-			os_free(uri);
-			return DM_RESP_BAD_REQUEST;
-		}
-
-		debug_dump_node(ctx, "Parsed TNDS", unode);
-
-		xml_node_add_child(ctx->xml, pps_node, unode);
-	} else {
-		/* TODO: What to do here? */
-		os_free(uri);
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	os_free(uri);
-
-	if (update_pps_file(ctx, pps_fname, pps) < 0)
-		return DM_RESP_COMMAND_FAILED;
-
-	ctx->pps_updated = 1;
-
-	return DM_RESP_OK;
-}
-
-
-static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
-		      xml_node_t *pps, const char *pps_fname)
-{
-	xml_node_t *node;
-	char *locuri;
-	char fname[300];
-	int ret;
-
-	node = get_node(ctx->xml, add, "Item/Target/LocURI");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No Target LocURI node found");
-		return DM_RESP_BAD_REQUEST;
-	}
-	locuri = xml_node_get_text(ctx->xml, node);
-	if (locuri == NULL) {
-		wpa_printf(MSG_ERROR, "No LocURI node text found");
-		return DM_RESP_BAD_REQUEST;
-	}
-	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
-	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
-		wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
-		xml_node_get_text_free(ctx->xml, locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-
-	node = get_node(ctx->xml, add, "Item/Data");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No Data node found");
-		xml_node_get_text_free(ctx->xml, locuri);
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	if (pps_fname && os_file_exists(pps_fname)) {
-		ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
-		if (ret != DM_RESP_OK) {
-			xml_node_get_text_free(ctx->xml, locuri);
-			return ret;
-		}
-		ret = 0;
-		os_strlcpy(fname, pps_fname, sizeof(fname));
-	} else
-		ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
-	xml_node_get_text_free(ctx->xml, locuri);
-	if (ret < 0)
-		return ret == -2 ? DM_RESP_ALREADY_EXISTS :
-			DM_RESP_COMMAND_FAILED;
-
-	if (ctx->no_reconnect == 2) {
-		os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
-			    fname);
-		ctx->pps_cred_set = 1;
-		return DM_RESP_OK;
-	}
-
-	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
-	cmd_set_pps(ctx, fname);
-
-	if (ctx->no_reconnect)
-		return DM_RESP_OK;
-
-	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
-	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
-		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
-
-	return DM_RESP_OK;
-}
-
-
-static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
-			  xml_node_t *pps, const char *pps_fname)
-{
-	char *locuri, *pos;
-	size_t fqdn_len;
-	xml_node_t *node, *tnds, *unode, *pps_node, *parent;
-	char *data;
-	int use_tnds = 0;
-
-	locuri = oma_dm_get_target_locuri(ctx, replace);
-	if (locuri == NULL)
-		return DM_RESP_BAD_REQUEST;
-
-	wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
-	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
-		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
-		os_free(locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos = locuri + 8;
-
-	if (ctx->fqdn == NULL) {
-		os_free(locuri);
-		return DM_RESP_COMMAND_FAILED;
-	}
-	fqdn_len = os_strlen(ctx->fqdn);
-	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
-	    pos[fqdn_len] != '/') {
-		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
-			   ctx->fqdn);
-		os_free(locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos += fqdn_len + 1;
-
-	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
-		wpa_printf(MSG_INFO,
-			   "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
-			   ctx->fqdn);
-		os_free(locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos += 24;
-
-	wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
-
-	pps_node = get_node(ctx->xml, pps, pos);
-	if (pps_node == NULL) {
-		wpa_printf(MSG_INFO, "Specified PPS node not found");
-		os_free(locuri);
-		return DM_RESP_NOT_FOUND;
-	}
-
-	node = get_node(ctx->xml, replace, "Item/Meta/Type");
-	if (node) {
-		char *type;
-		type = xml_node_get_text(ctx->xml, node);
-		if (type == NULL) {
-			wpa_printf(MSG_INFO, "Could not find type text");
-			os_free(locuri);
-			return DM_RESP_BAD_REQUEST;
-		}
-		use_tnds = node &&
-			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
-	}
-
-	node = get_node(ctx->xml, replace, "Item/Data");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No Replace/Item/Data found");
-		os_free(locuri);
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	data = xml_node_get_text(ctx->xml, node);
-	if (data == NULL) {
-		wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
-		os_free(locuri);
-		return DM_RESP_BAD_REQUEST;
-	}
-
-	wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
-
-	if (use_tnds) {
-		tnds = xml_node_from_buf(ctx->xml, data);
-		xml_node_get_text_free(ctx->xml, data);
-		if (tnds == NULL) {
-			wpa_printf(MSG_INFO,
-				   "Could not parse Replace/Item/Data text");
-			os_free(locuri);
-			return DM_RESP_BAD_REQUEST;
-		}
-
-		unode = tnds_to_mo(ctx->xml, tnds);
-		xml_node_free(ctx->xml, tnds);
-		if (unode == NULL) {
-			wpa_printf(MSG_INFO, "Could not parse TNDS text");
-			os_free(locuri);
-			return DM_RESP_BAD_REQUEST;
-		}
-
-		debug_dump_node(ctx, "Parsed TNDS", unode);
-
-		parent = xml_node_get_parent(ctx->xml, pps_node);
-		xml_node_detach(ctx->xml, pps_node);
-		xml_node_add_child(ctx->xml, parent, unode);
-	} else {
-		xml_node_set_text(ctx->xml, pps_node, data);
-		xml_node_get_text_free(ctx->xml, data);
-	}
-
-	os_free(locuri);
-
-	if (update_pps_file(ctx, pps_fname, pps) < 0)
-		return DM_RESP_COMMAND_FAILED;
-
-	ctx->pps_updated = 1;
-
-	return DM_RESP_OK;
-}
-
-
-static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
-		      xml_node_t *pps, const char *pps_fname, char **value)
-{
-	char *locuri, *pos;
-	size_t fqdn_len;
-	xml_node_t *pps_node;
-	const char *name;
-
-	*value = NULL;
-
-	locuri = oma_dm_get_target_locuri(ctx, get);
-	if (locuri == NULL)
-		return DM_RESP_BAD_REQUEST;
-
-	wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
-	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
-		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
-		os_free(locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos = locuri + 8;
-
-	if (ctx->fqdn == NULL)
-		return DM_RESP_COMMAND_FAILED;
-	fqdn_len = os_strlen(ctx->fqdn);
-	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
-	    pos[fqdn_len] != '/') {
-		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
-			   ctx->fqdn);
-		os_free(locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos += fqdn_len + 1;
-
-	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
-		wpa_printf(MSG_INFO,
-			   "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
-			   ctx->fqdn);
-		os_free(locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-	pos += 24;
-
-	wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
-
-	pps_node = get_node(ctx->xml, pps, pos);
-	if (pps_node == NULL) {
-		wpa_printf(MSG_INFO, "Specified PPS node not found");
-		os_free(locuri);
-		return DM_RESP_NOT_FOUND;
-	}
-
-	name = xml_node_get_localname(ctx->xml, pps_node);
-	wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
-	if (os_strcasecmp(name, "Password") == 0) {
-		wpa_printf(MSG_INFO, "Do not allow Get for Password node");
-		os_free(locuri);
-		return DM_RESP_PERMISSION_DENIED;
-	}
-
-	/*
-	 * TODO: No support for DMTNDS, so if interior node, reply with a
-	 * list of children node names in Results element. The child list type is
-	 * defined in [DMTND].
-	 */
-
-	*value = xml_node_get_text(ctx->xml, pps_node);
-	if (*value == NULL)
-		return DM_RESP_COMMAND_FAILED;
-
-	return DM_RESP_OK;
-}
-
-
-static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
-{
-	xml_node_t *cnode;
-	char *str;
-	int ret;
-
-	cnode = get_node(ctx->xml, node, "CmdID");
-	if (cnode == NULL)
-		return 0;
-
-	str = xml_node_get_text(ctx->xml, cnode);
-	if (str == NULL)
-		return 0;
-	ret = atoi(str);
-	xml_node_get_text_free(ctx->xml, str);
-	return ret;
-}
-
-
-static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
-				     const char *url, xml_node_t *syncml,
-				     const char *ext_hdr,
-				     const char *username, const char *password,
-				     const char *client_cert,
-				     const char *client_key)
-{
-	xml_node_t *resp;
-	char *str, *res;
-	char *resp_uri = NULL;
-
-	str = xml_node_to_str(ctx->xml, syncml);
-	xml_node_free(ctx->xml, syncml);
-	if (str == NULL)
-		return NULL;
-
-	wpa_printf(MSG_INFO, "Send OMA DM Package");
-	write_summary(ctx, "Send OMA DM Package");
-	os_free(ctx->server_url);
-	ctx->server_url = os_strdup(url);
-	res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
-			ext_hdr, ctx->ca_fname, username, password,
-			client_cert, client_key, NULL);
-	os_free(str);
-	os_free(resp_uri);
-	resp_uri = NULL;
-
-	if (res == NULL) {
-		const char *err = http_get_err(ctx->http);
-		if (err) {
-			wpa_printf(MSG_INFO, "HTTP error: %s", err);
-			write_result(ctx, "HTTP error: %s", err);
-		} else {
-			write_summary(ctx, "Failed to send OMA DM Package");
-		}
-		return NULL;
-	}
-	wpa_printf(MSG_DEBUG, "Server response: %s", res);
-
-	wpa_printf(MSG_INFO, "Process OMA DM Package");
-	write_summary(ctx, "Process received OMA DM Package");
-	resp = xml_node_from_buf(ctx->xml, res);
-	os_free(res);
-	if (resp == NULL) {
-		wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
-		return NULL;
-	}
-
-	debug_dump_node(ctx, "OMA DM Package", resp);
-
-	return resp;
-}
-
-
-static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
-				   xml_node_t *resp, int msgid,
-				   char **ret_resp_uri,
-				   xml_node_t *pps, const char *pps_fname)
-{
-	xml_node_t *syncml, *syncbody, *hdr, *body, *child;
-	const char *name;
-	char *resp_uri = NULL;
-	int server_msgid = 0;
-	int cmdid = 0;
-	int server_cmdid;
-	int resp_needed = 0;
-	char *tmp;
-	int final = 0;
-	char *locuri;
-
-	*ret_resp_uri = NULL;
-
-	name = xml_node_get_localname(ctx->xml, resp);
-	if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
-		wpa_printf(MSG_INFO, "SyncML node not found");
-		return NULL;
-	}
-
-	hdr = get_node(ctx->xml, resp, "SyncHdr");
-	body = get_node(ctx->xml, resp, "SyncBody");
-	if (hdr == NULL || body == NULL) {
-		wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
-		return NULL;
-	}
-
-	xml_node_for_each_child(ctx->xml, child, hdr) {
-		xml_node_for_each_check(ctx->xml, child);
-		name = xml_node_get_localname(ctx->xml, child);
-		wpa_printf(MSG_INFO, "SyncHdr %s", name);
-		if (os_strcasecmp(name, "RespURI") == 0) {
-			tmp = xml_node_get_text(ctx->xml, child);
-			if (tmp)
-				resp_uri = os_strdup(tmp);
-			xml_node_get_text_free(ctx->xml, tmp);
-		} else if (os_strcasecmp(name, "MsgID") == 0) {
-			tmp = xml_node_get_text(ctx->xml, child);
-			if (tmp)
-				server_msgid = atoi(tmp);
-			xml_node_get_text_free(ctx->xml, tmp);
-		}
-	}
-
-	wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
-	if (resp_uri)
-		wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
-
-	syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
-	if (syncml == NULL) {
-		os_free(resp_uri);
-		return NULL;
-	}
-
-	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
-	cmdid++;
-	add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
-		   DM_RESP_AUTH_ACCEPTED, NULL);
-
-	xml_node_for_each_child(ctx->xml, child, body) {
-		xml_node_for_each_check(ctx->xml, child);
-		server_cmdid = oma_dm_get_cmdid(ctx, child);
-		name = xml_node_get_localname(ctx->xml, child);
-		wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
-			   server_cmdid, name);
-		if (os_strcasecmp(name, "Exec") == 0) {
-			int res = oma_dm_exec(ctx, child);
-			cmdid++;
-			locuri = oma_dm_get_target_locuri(ctx, child);
-			if (locuri == NULL)
-				res = DM_RESP_BAD_REQUEST;
-			add_status(ctx, syncbody, server_msgid, server_cmdid,
-				   cmdid, name, res, locuri);
-			os_free(locuri);
-			resp_needed = 1;
-		} else if (os_strcasecmp(name, "Add") == 0) {
-			int res = oma_dm_add(ctx, child, pps, pps_fname);
-			cmdid++;
-			locuri = oma_dm_get_target_locuri(ctx, child);
-			if (locuri == NULL)
-				res = DM_RESP_BAD_REQUEST;
-			add_status(ctx, syncbody, server_msgid, server_cmdid,
-				   cmdid, name, res, locuri);
-			os_free(locuri);
-			resp_needed = 1;
-		} else if (os_strcasecmp(name, "Replace") == 0) {
-			int res;
-			res = oma_dm_replace(ctx, child, pps, pps_fname);
-			cmdid++;
-			locuri = oma_dm_get_target_locuri(ctx, child);
-			if (locuri == NULL)
-				res = DM_RESP_BAD_REQUEST;
-			add_status(ctx, syncbody, server_msgid, server_cmdid,
-				   cmdid, name, res, locuri);
-			os_free(locuri);
-			resp_needed = 1;
-		} else if (os_strcasecmp(name, "Status") == 0) {
-			/* TODO: Verify success */
-		} else if (os_strcasecmp(name, "Get") == 0) {
-			int res;
-			char *value;
-			res = oma_dm_get(ctx, child, pps, pps_fname, &value);
-			cmdid++;
-			locuri = oma_dm_get_target_locuri(ctx, child);
-			if (locuri == NULL)
-				res = DM_RESP_BAD_REQUEST;
-			add_status(ctx, syncbody, server_msgid, server_cmdid,
-				   cmdid, name, res, locuri);
-			if (res == DM_RESP_OK && value) {
-				cmdid++;
-				add_results(ctx, syncbody, server_msgid,
-					    server_cmdid, cmdid, locuri, value);
-			}
-			os_free(locuri);
-			xml_node_get_text_free(ctx->xml, value);
-			resp_needed = 1;
-#if 0 /* TODO: MUST support */
-		} else if (os_strcasecmp(name, "Delete") == 0) {
-#endif
-#if 0 /* TODO: MUST support */
-		} else if (os_strcasecmp(name, "Sequence") == 0) {
-#endif
-		} else if (os_strcasecmp(name, "Final") == 0) {
-			final = 1;
-			break;
-		} else {
-			locuri = oma_dm_get_target_locuri(ctx, child);
-			add_status(ctx, syncbody, server_msgid, server_cmdid,
-				   cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
-				   locuri);
-			os_free(locuri);
-			resp_needed = 1;
-		}
-	}
-
-	if (!final) {
-		wpa_printf(MSG_INFO, "Final node not found");
-		xml_node_free(ctx->xml, syncml);
-		os_free(resp_uri);
-		return NULL;
-	}
-
-	if (!resp_needed) {
-		wpa_printf(MSG_INFO, "Exchange completed - no response needed");
-		xml_node_free(ctx->xml, syncml);
-		os_free(resp_uri);
-		return NULL;
-	}
-
-	xml_node_create(ctx->xml, syncbody, NULL, "Final");
-
-	debug_dump_node(ctx, "OMA-DM Package 3", syncml);
-
-	*ret_resp_uri = resp_uri;
-	return syncml;
-}
-
-
-int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
-{
-	xml_node_t *syncml, *resp;
-	char *resp_uri = NULL;
-	int msgid = 0;
-
-	if (url == NULL) {
-		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
-		return -1;
-	}
-
-	wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
-	write_summary(ctx, "OMA-DM credential provisioning");
-
-	msgid++;
-	syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
-	if (syncml == NULL)
-		return -1;
-
-	while (syncml) {
-		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
-					syncml, NULL, NULL, NULL, NULL, NULL);
-		if (resp == NULL)
-			return -1;
-
-		msgid++;
-		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
-					NULL, NULL);
-		xml_node_free(ctx->xml, resp);
-	}
-
-	os_free(resp_uri);
-
-	return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
-{
-	xml_node_t *syncml, *resp;
-	char *resp_uri = NULL;
-	int msgid = 0;
-
-	if (url == NULL) {
-		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
-		return -1;
-	}
-
-	wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
-	ctx->no_reconnect = 2;
-
-	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
-	write_summary(ctx, "Wait for IP address before starting SIM provisioning");
-
-	if (wait_ip_addr(ctx->ifname, 15) < 0) {
-		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
-	}
-	write_summary(ctx, "OMA-DM SIM provisioning");
-
-	msgid++;
-	syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
-	if (syncml == NULL)
-		return -1;
-
-	while (syncml) {
-		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
-					syncml, NULL, NULL, NULL, NULL, NULL);
-		if (resp == NULL)
-			return -1;
-
-		msgid++;
-		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
-					NULL, NULL);
-		xml_node_free(ctx->xml, resp);
-	}
-
-	os_free(resp_uri);
-
-	if (ctx->pps_cred_set) {
-		wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
-		cmd_set_pps(ctx, ctx->pps_fname);
-
-		wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
-		write_summary(ctx, "Requesting reconnection with updated configuration");
-		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
-			wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
-			write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
-			return -1;
-		}
-	}
-
-	return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
-		    const char *pps_fname,
-		    const char *client_cert, const char *client_key,
-		    const char *cred_username, const char *cred_password,
-		    xml_node_t *pps)
-{
-	xml_node_t *syncml, *resp;
-	char *resp_uri = NULL;
-	int msgid = 0;
-
-	wpa_printf(MSG_INFO, "OMA-DM policy update");
-	write_summary(ctx, "OMA-DM policy update");
-
-	msgid++;
-	syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
-	if (syncml == NULL)
-		return;
-
-	while (syncml) {
-		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
-					syncml, NULL, cred_username,
-					cred_password, client_cert, client_key);
-		if (resp == NULL)
-			return;
-
-		msgid++;
-		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
-					pps, pps_fname);
-		xml_node_free(ctx->xml, resp);
-	}
-
-	os_free(resp_uri);
-
-	if (ctx->pps_updated) {
-		wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
-		write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
-		cmd_set_pps(ctx, pps_fname);
-		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
-			wpa_printf(MSG_INFO,
-				   "Failed to request wpa_supplicant to reconnect");
-			write_summary(ctx,
-				      "Failed to request wpa_supplicant to reconnect");
-		}
-	}
-}
-
-
-void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
-		    const char *pps_fname,
-		    const char *client_cert, const char *client_key,
-		    const char *cred_username, const char *cred_password,
-		    xml_node_t *pps)
-{
-	xml_node_t *syncml, *resp;
-	char *resp_uri = NULL;
-	int msgid = 0;
-
-	wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
-	write_summary(ctx, "OMA-DM subscription remediation");
-
-	msgid++;
-	syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
-	if (syncml == NULL)
-		return;
-
-	while (syncml) {
-		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
-					syncml, NULL, cred_username,
-					cred_password, client_cert, client_key);
-		if (resp == NULL)
-			return;
-
-		msgid++;
-		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
-					pps, pps_fname);
-		xml_node_free(ctx->xml, resp);
-	}
-
-	os_free(resp_uri);
-
-	wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
-	write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
-	cmd_set_pps(ctx, pps_fname);
-	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
-		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
-		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
-	}
-}
-
-
-void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
-		    const char *add_fname)
-{
-	xml_node_t *pps, *add;
-	int res;
-
-	ctx->fqdn = os_strdup("wi-fi.org");
-
-	pps = node_from_file(ctx->xml, pps_fname);
-	if (pps == NULL) {
-		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
-			   pps_fname);
-		return;
-	}
-
-	add = node_from_file(ctx->xml, add_fname);
-	if (add == NULL) {
-		wpa_printf(MSG_INFO, "Add file %s could not be parsed",
-			   add_fname);
-		xml_node_free(ctx->xml, pps);
-		return;
-	}
-
-	res = oma_dm_add(ctx, add, pps, pps_fname);
-	wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
-
-	xml_node_free(ctx->xml, pps);
-	xml_node_free(ctx->xml, add);
-}
-
-
-void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
-			const char *replace_fname)
-{
-	xml_node_t *pps, *replace;
-	int res;
-
-	ctx->fqdn = os_strdup("wi-fi.org");
-
-	pps = node_from_file(ctx->xml, pps_fname);
-	if (pps == NULL) {
-		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
-			   pps_fname);
-		return;
-	}
-
-	replace = node_from_file(ctx->xml, replace_fname);
-	if (replace == NULL) {
-		wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
-			   replace_fname);
-		xml_node_free(ctx->xml, pps);
-		return;
-	}
-
-	res = oma_dm_replace(ctx, replace, pps, pps_fname);
-	wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
-
-	xml_node_free(ctx->xml, pps);
-	xml_node_free(ctx->xml, replace);
-}
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 2ca85f9..f679df4 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -9,9 +9,6 @@
 #include "includes.h"
 #include <time.h>
 #include <sys/stat.h>
-#ifdef ANDROID
-#include "private/android_filesystem_config.h"
-#endif /* ANDROID */
 
 #include "common.h"
 #include "utils/browser.h"
@@ -25,10 +22,9 @@
 #include "crypto/sha256.h"
 #include "osu_client.h"
 
-const char *spp_xsd_fname = "spp.xsd";
+static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...);
 
-
-void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+static void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
 {
 	va_list ap;
 	FILE *f;
@@ -54,7 +50,7 @@
 }
 
 
-void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
 {
 	va_list ap;
 	FILE *f;
@@ -74,231 +70,6 @@
 }
 
 
-void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
-		     xml_node_t *node)
-{
-	char *str = xml_node_to_str(ctx->xml, node);
-	wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
-	free(str);
-}
-
-
-static int valid_fqdn(const char *fqdn)
-{
-	const char *pos;
-
-	/* TODO: could make this more complete.. */
-	if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
-		return 0;
-	for (pos = fqdn; *pos; pos++) {
-		if (*pos >= 'a' && *pos <= 'z')
-			continue;
-		if (*pos >= 'A' && *pos <= 'Z')
-			continue;
-		if (*pos >= '0' && *pos <= '9')
-			continue;
-		if (*pos == '-' || *pos == '.' || *pos == '_')
-			continue;
-		return 0;
-	}
-	return 1;
-}
-
-
-static int android_update_permission(const char *path, mode_t mode)
-{
-#ifdef ANDROID
-	/* we need to change file/folder permission for Android */
-
-	if (!path) {
-		wpa_printf(MSG_ERROR, "file path null");
-		return -1;
-	}
-
-	/* Allow processes running with Group ID as AID_WIFI,
-	 * to read files from SP, SP/<fqdn>, Cert and osu-info directories */
-	if (lchown(path, -1, AID_WIFI)) {
-		wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s",
-			   strerror(errno));
-		return -1;
-	}
-
-	if (chmod(path, mode) < 0) {
-		wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
-			   strerror(errno));
-		return -1;
-	}
-#endif  /* ANDROID */
-
-	return 0;
-}
-
-
-int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
-{
-	xml_node_t *node;
-	char *url, *user = NULL, *pw = NULL;
-	char *proto;
-	int ret = -1;
-
-	proto = xml_node_get_attr_value(ctx->xml, getcert,
-					"enrollmentProtocol");
-	if (!proto)
-		return -1;
-	wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
-	write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
-	if (os_strcasecmp(proto, "EST") != 0) {
-		wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
-		xml_node_get_attr_value_free(ctx->xml, proto);
-		return -1;
-	}
-	xml_node_get_attr_value_free(ctx->xml, proto);
-
-	node = get_node(ctx->xml, getcert, "enrollmentServerURI");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
-		xml_node_get_attr_value_free(ctx->xml, proto);
-		return -1;
-	}
-	url = xml_node_get_text(ctx->xml, node);
-	if (url == NULL) {
-		wpa_printf(MSG_INFO, "Could not get URL text");
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
-	write_summary(ctx, "enrollmentServerURI: %s", url);
-
-	node = get_node(ctx->xml, getcert, "estUserID");
-	if (node == NULL && !ctx->client_cert_present) {
-		wpa_printf(MSG_INFO, "Could not find estUserID node");
-		goto fail;
-	}
-	if (node) {
-		user = xml_node_get_text(ctx->xml, node);
-		if (user == NULL) {
-			wpa_printf(MSG_INFO, "Could not get estUserID text");
-			goto fail;
-		}
-		wpa_printf(MSG_INFO, "estUserID: %s", user);
-		write_summary(ctx, "estUserID: %s", user);
-	}
-
-	node = get_node(ctx->xml, getcert, "estPassword");
-	if (node == NULL && !ctx->client_cert_present) {
-		wpa_printf(MSG_INFO, "Could not find estPassword node");
-		goto fail;
-	}
-	if (node) {
-		pw = xml_node_get_base64_text(ctx->xml, node, NULL);
-		if (pw == NULL) {
-			wpa_printf(MSG_INFO, "Could not get estPassword text");
-			goto fail;
-		}
-		wpa_printf(MSG_INFO, "estPassword: %s", pw);
-	}
-
-	mkdir("Cert", S_IRWXU);
-	android_update_permission("Cert", S_IRWXU | S_IRWXG);
-
-	if (est_load_cacerts(ctx, url) < 0 ||
-	    est_build_csr(ctx, url) < 0 ||
-	    est_simple_enroll(ctx, url, user, pw) < 0)
-		goto fail;
-
-	ret = 0;
-fail:
-	xml_node_get_text_free(ctx->xml, url);
-	xml_node_get_text_free(ctx->xml, user);
-	xml_node_get_text_free(ctx->xml, pw);
-
-	return ret;
-}
-
-
-static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
-			    const char *fqdn)
-{
-	u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
-	char *der, *pem;
-	size_t der_len, pem_len;
-	char *fingerprint;
-	char buf[200];
-
-	wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
-
-	fingerprint = xml_node_get_text(ctx->xml, cert);
-	if (fingerprint == NULL)
-		return -1;
-	if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
-		wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
-		write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
-		xml_node_get_text_free(ctx->xml, fingerprint);
-		return -1;
-	}
-	xml_node_get_text_free(ctx->xml, fingerprint);
-
-	der = os_readfile("Cert/est_cert.der", &der_len);
-	if (der == NULL) {
-		wpa_printf(MSG_INFO, "Could not find client certificate from EST");
-		write_result(ctx, "Could not find client certificate from EST");
-		return -1;
-	}
-
-	if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
-		os_free(der);
-		return -1;
-	}
-	os_free(der);
-
-	if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
-		wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
-		write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
-		return -1;
-	}
-
-	wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
-	unlink("Cert/est_cert.der");
-
-	os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
-	if (rename("Cert/est-cacerts.pem", buf) < 0) {
-		wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
-			   strerror(errno));
-		return -1;
-	}
-	pem = os_readfile(buf, &pem_len);
-
-	os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
-	if (rename("Cert/est_cert.pem", buf) < 0) {
-		wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
-			   strerror(errno));
-		os_free(pem);
-		return -1;
-	}
-
-	if (pem) {
-		FILE *f = fopen(buf, "a");
-		if (f) {
-			fwrite(pem, pem_len, 1, f);
-			fclose(f);
-		}
-		os_free(pem);
-	}
-
-	os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
-	if (rename("Cert/privkey-plain.pem", buf) < 0) {
-		wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
-			   strerror(errno));
-		return -1;
-	}
-
-	unlink("Cert/est-req.b64");
-	unlink("Cert/est-req.pem");
-	rmdir("Cert");
-
-	return 0;
-}
-
-
 #define TMP_CERT_DL_FILE "tmp-cert-download"
 
 static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
@@ -337,12 +108,10 @@
 	xml_node_get_text_free(ctx->xml, hash);
 
 	write_summary(ctx, "Download certificate from %s", url);
-	ctx->no_osu_cert_validation = 1;
 	http_ocsp_set(ctx->http, 1);
 	res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
 	http_ocsp_set(ctx->http,
 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
-	ctx->no_osu_cert_validation = 0;
 	xml_node_get_text_free(ctx->xml, url);
 	if (res < 0)
 		return -1;
@@ -392,60 +161,6 @@
 }
 
 
-static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
-			 const char *ca_fname)
-{
-	xml_node_t *pps, *node;
-	int ret;
-
-	pps = node_from_file(ctx->xml, pps_fname);
-	if (pps == NULL) {
-		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
-		return -1;
-	}
-
-	node = get_child_node(ctx->xml, pps,
-			      "SubscriptionUpdate/TrustRoot");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
-		xml_node_free(ctx->xml, pps);
-		return -1;
-	}
-
-	ret = download_cert(ctx, node, ca_fname);
-	xml_node_free(ctx->xml, pps);
-
-	return ret;
-}
-
-
-static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
-			    const char *ca_fname)
-{
-	xml_node_t *pps, *node;
-	int ret;
-
-	pps = node_from_file(ctx->xml, pps_fname);
-	if (pps == NULL) {
-		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
-		return -1;
-	}
-
-	node = get_child_node(ctx->xml, pps,
-			      "Policy/PolicyUpdate/TrustRoot");
-	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
-		xml_node_free(ctx->xml, pps);
-		return -2;
-	}
-
-	ret = download_cert(ctx, node, ca_fname);
-	xml_node_free(ctx->xml, pps);
-
-	return ret;
-}
-
-
 static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
 			 const char *ca_fname)
 {
@@ -480,298 +195,6 @@
 }
 
 
-static int download_trust_roots(struct hs20_osu_client *ctx,
-				const char *pps_fname)
-{
-	char *dir, *pos;
-	char fname[300];
-	int ret, ret1;
-
-	dir = os_strdup(pps_fname);
-	if (dir == NULL)
-		return -1;
-	pos = os_strrchr(dir, '/');
-	if (pos == NULL) {
-		os_free(dir);
-		return -1;
-	}
-	*pos = '\0';
-
-	snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
-	ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
-	snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
-	ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname);
-	if (ret == 0 && ret1 == -1)
-		ret = -1;
-	snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
-	ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname);
-	if (ret == 0 && ret1 == -1)
-		ret = -1;
-
-	os_free(dir);
-
-	return ret;
-}
-
-
-static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
-				       const char *fqdn)
-{
-	size_t match_len, len, i;
-	const char *val;
-
-	match_len = os_strlen(fqdn);
-
-	for (i = 0; i < ctx->server_dnsname_count; i++) {
-		wpa_printf(MSG_INFO,
-			   "Checking suffix match against server dNSName %s",
-			   ctx->server_dnsname[i]);
-		val = ctx->server_dnsname[i];
-		len = os_strlen(val);
-
-		if (match_len > len)
-			continue;
-
-		if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
-			continue; /* no match */
-
-		if (match_len == len)
-			return 1; /* exact match */
-
-		if (val[len - match_len - 1] == '.')
-			return 1; /* full label match completes suffix match */
-
-		/* Reject due to incomplete label match */
-	}
-
-	/* None of the dNSName(s) matched */
-	return 0;
-}
-
-
-int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
-		    xml_node_t *add_mo, char *fname, size_t fname_len)
-{
-	char *str;
-	char *fqdn, *pos;
-	xml_node_t *tnds, *mo, *cert;
-	const char *name;
-	int ret;
-
-	if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
-		wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
-			   uri);
-		write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
-			     uri);
-		return -1;
-	}
-
-	fqdn = strdup(uri + 8);
-	if (fqdn == NULL)
-		return -1;
-	pos = strchr(fqdn, '/');
-	if (pos) {
-		if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
-			wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
-				   uri);
-			write_result(ctx, "Unsupported location for addMO to "
-				     "add PPS MO (extra directory): '%s'", uri);
-			free(fqdn);
-			return -1;
-		}
-		*pos = '\0'; /* remove trailing slash and PPS node name */
-	}
-	wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
-
-	if (!server_dnsname_suffix_match(ctx, fqdn)) {
-		wpa_printf(MSG_INFO,
-			   "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d",
-			   fqdn, (int) ctx->server_dnsname_count);
-		write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
-			     fqdn);
-		free(fqdn);
-		return -1;
-	}
-
-	if (!valid_fqdn(fqdn)) {
-		wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
-		write_result(ctx, "Invalid FQDN '%s'", fqdn);
-		free(fqdn);
-		return -1;
-	}
-
-	mkdir("SP", S_IRWXU);
-	snprintf(fname, fname_len, "SP/%s", fqdn);
-	if (mkdir(fname, S_IRWXU) < 0) {
-		if (errno != EEXIST) {
-			int err = errno;
-			wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
-				   fname, strerror(err));
-			free(fqdn);
-			return -1;
-		}
-	}
-
-	android_update_permission("SP", S_IRWXU | S_IRWXG);
-	android_update_permission(fname, S_IRWXU | S_IRWXG);
-
-	snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
-
-	if (os_file_exists(fname)) {
-		wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
-			   fname);
-		write_result(ctx, "PPS file '%s' exists - reject addMO",
-			     fname);
-		free(fqdn);
-		return -2;
-	}
-	wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
-
-	str = xml_node_get_text(ctx->xml, add_mo);
-	if (str == NULL) {
-		wpa_printf(MSG_INFO, "Could not extract MO text");
-		free(fqdn);
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
-
-	tnds = xml_node_from_buf(ctx->xml, str);
-	xml_node_get_text_free(ctx->xml, str);
-	if (tnds == NULL) {
-		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
-		free(fqdn);
-		return -1;
-	}
-
-	mo = tnds_to_mo(ctx->xml, tnds);
-	if (mo == NULL) {
-		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
-		free(fqdn);
-		return -1;
-	}
-
-	debug_dump_node(ctx, "Parsed TNDS", mo);
-
-	name = xml_node_get_localname(ctx->xml, mo);
-	if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
-		wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
-			   name);
-		free(fqdn);
-		return -1;
-	}
-
-	cert = get_child_node(ctx->xml, mo,
-			      "Credential/DigitalCertificate/"
-			      "CertSHA256Fingerprint");
-	if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
-		xml_node_free(ctx->xml, mo);
-		free(fqdn);
-		return -1;
-	}
-	free(fqdn);
-
-	if (node_to_file(ctx->xml, fname, mo) < 0) {
-		wpa_printf(MSG_INFO, "Could not write MO to file");
-		xml_node_free(ctx->xml, mo);
-		return -1;
-	}
-	xml_node_free(ctx->xml, mo);
-
-	wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
-	write_summary(ctx, "A new PPS MO added as '%s'", fname);
-
-	ret = download_trust_roots(ctx, fname);
-	if (ret < 0) {
-		wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
-		write_summary(ctx, "Remove invalid PPS MO file");
-		unlink(fname);
-	}
-
-	return ret;
-}
-
-
-int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
-		    xml_node_t *pps)
-{
-	char *str;
-	FILE *f;
-	char backup[300];
-
-	if (ctx->client_cert_present) {
-		xml_node_t *cert;
-		cert = get_child_node(ctx->xml, pps,
-				      "Credential/DigitalCertificate/"
-				      "CertSHA256Fingerprint");
-		if (cert && os_file_exists("Cert/est_cert.der") &&
-		    process_est_cert(ctx, cert, ctx->fqdn) < 0) {
-			wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
-			return -1;
-		}
-	}
-
-	wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
-
-	str = xml_node_to_str(ctx->xml, pps);
-	if (str == NULL) {
-		wpa_printf(MSG_ERROR, "No node found");
-		return -1;
-	}
-	wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
-
-	snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
-	rename(pps_fname, backup);
-	f = fopen(pps_fname, "w");
-	if (f == NULL) {
-		wpa_printf(MSG_INFO, "Could not write PPS");
-		rename(backup, pps_fname);
-		free(str);
-		return -1;
-	}
-	fprintf(f, "%s\n", str);
-	fclose(f);
-
-	free(str);
-
-	return 0;
-}
-
-
-void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
-		 const char *alt_loc, char **user, char **pw)
-{
-	xml_node_t *node;
-
-	node = get_child_node(ctx->xml, pps,
-			      "Credential/UsernamePassword/Username");
-	if (node)
-		*user = xml_node_get_text(ctx->xml, node);
-
-	node = get_child_node(ctx->xml, pps,
-			      "Credential/UsernamePassword/Password");
-	if (node)
-		*pw = xml_node_get_base64_text(ctx->xml, node, NULL);
-
-	node = get_child_node(ctx->xml, pps, alt_loc);
-	if (node) {
-		xml_node_t *a;
-		a = get_node(ctx->xml, node, "Username");
-		if (a) {
-			xml_node_get_text_free(ctx->xml, *user);
-			*user = xml_node_get_text(ctx->xml, a);
-			wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
-		}
-
-		a = get_node(ctx->xml, node, "Password");
-		if (a) {
-			free(*pw);
-			*pw = xml_node_get_base64_text(ctx->xml, a, NULL);
-			wpa_printf(MSG_INFO, "Use OSU password");
-		}
-	}
-}
-
-
 /* Remove old credentials based on HomeSP/FQDN */
 static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
 {
@@ -1874,14 +1297,13 @@
 			wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
 		wpa_printf(MSG_INFO, "credential localname: '%s'", name);
 		set_pps_credential(ctx, id, child, fqdn);
-		ctx->pps_cred_set = 1;
 	}
 
 	xml_node_get_text_free(ctx->xml, update_identifier);
 }
 
 
-void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
+static void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
 {
 	xml_node_t *pps;
 	const char *fqdn;
@@ -1988,1167 +1410,20 @@
 }
 
 
-struct osu_icon {
-	int id;
-	char lang[4];
-	char mime_type[256];
-	char filename[256];
-};
-
-struct osu_data {
-	char bssid[20];
-	char url[256];
-	unsigned int methods;
-	char osu_ssid[33];
-	char osu_ssid2[33];
-	char osu_nai[256];
-	char osu_nai2[256];
-	struct osu_lang_text friendly_name[MAX_OSU_VALS];
-	size_t friendly_name_count;
-	struct osu_lang_text serv_desc[MAX_OSU_VALS];
-	size_t serv_desc_count;
-	struct osu_icon icon[MAX_OSU_VALS];
-	size_t icon_count;
-};
-
-
-static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
-{
-	FILE *f;
-	char buf[1000];
-	struct osu_data *osu = NULL, *last = NULL;
-	size_t osu_count = 0;
-	char *pos, *end;
-	int res;
-
-	f = fopen(fname, "r");
-	if (f == NULL) {
-		wpa_printf(MSG_ERROR, "Could not open %s", fname);
-		return NULL;
-	}
-
-	while (fgets(buf, sizeof(buf), f)) {
-		pos = strchr(buf, '\n');
-		if (pos)
-			*pos = '\0';
-
-		if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
-			last = realloc(osu, (osu_count + 1) * sizeof(*osu));
-			if (last == NULL)
-				break;
-			osu = last;
-			last = &osu[osu_count++];
-			memset(last, 0, sizeof(*last));
-			res = os_snprintf(last->bssid, sizeof(last->bssid),
-					  "%s", buf + 13);
-			if (os_snprintf_error(sizeof(last->bssid), res))
-				break;
-			continue;
-		}
-		if (!last)
-			continue;
-
-		if (strncmp(buf, "uri=", 4) == 0) {
-			res = os_snprintf(last->url, sizeof(last->url),
-					  "%s", buf + 4);
-			if (os_snprintf_error(sizeof(last->url), res))
-				break;
-			continue;
-		}
-
-		if (strncmp(buf, "methods=", 8) == 0) {
-			last->methods = strtol(buf + 8, NULL, 16);
-			continue;
-		}
-
-		if (strncmp(buf, "osu_ssid=", 9) == 0) {
-			res = os_snprintf(last->osu_ssid,
-					  sizeof(last->osu_ssid),
-					  "%s", buf + 9);
-			if (os_snprintf_error(sizeof(last->osu_ssid), res))
-				break;
-			continue;
-		}
-
-		if (strncmp(buf, "osu_ssid2=", 10) == 0) {
-			res = os_snprintf(last->osu_ssid2,
-					  sizeof(last->osu_ssid2),
-					  "%s", buf + 10);
-			if (os_snprintf_error(sizeof(last->osu_ssid2), res))
-				break;
-			continue;
-		}
-
-		if (os_strncmp(buf, "osu_nai=", 8) == 0) {
-			res = os_snprintf(last->osu_nai, sizeof(last->osu_nai),
-					  "%s", buf + 8);
-			if (os_snprintf_error(sizeof(last->osu_nai), res))
-				break;
-			continue;
-		}
-
-		if (os_strncmp(buf, "osu_nai2=", 9) == 0) {
-			res = os_snprintf(last->osu_nai2,
-					  sizeof(last->osu_nai2),
-					  "%s", buf + 9);
-			if (os_snprintf_error(sizeof(last->osu_nai2), res))
-				break;
-			continue;
-		}
-
-		if (strncmp(buf, "friendly_name=", 14) == 0) {
-			struct osu_lang_text *txt;
-			if (last->friendly_name_count == MAX_OSU_VALS)
-				continue;
-			pos = strchr(buf + 14, ':');
-			if (pos == NULL)
-				continue;
-			*pos++ = '\0';
-			txt = &last->friendly_name[last->friendly_name_count++];
-			res = os_snprintf(txt->lang, sizeof(txt->lang),
-					  "%s", buf + 14);
-			if (os_snprintf_error(sizeof(txt->lang), res))
-				break;
-			res = os_snprintf(txt->text, sizeof(txt->text),
-					  "%s", pos);
-			if (os_snprintf_error(sizeof(txt->text), res))
-				break;
-		}
-
-		if (strncmp(buf, "desc=", 5) == 0) {
-			struct osu_lang_text *txt;
-			if (last->serv_desc_count == MAX_OSU_VALS)
-				continue;
-			pos = strchr(buf + 5, ':');
-			if (pos == NULL)
-				continue;
-			*pos++ = '\0';
-			txt = &last->serv_desc[last->serv_desc_count++];
-			res = os_snprintf(txt->lang, sizeof(txt->lang),
-					  "%s", buf + 5);
-			if (os_snprintf_error(sizeof(txt->lang), res))
-				break;
-			res = os_snprintf(txt->text, sizeof(txt->text),
-					  "%s", pos);
-			if (os_snprintf_error(sizeof(txt->text), res))
-				break;
-		}
-
-		if (strncmp(buf, "icon=", 5) == 0) {
-			struct osu_icon *icon;
-			if (last->icon_count == MAX_OSU_VALS)
-				continue;
-			icon = &last->icon[last->icon_count++];
-			icon->id = atoi(buf + 5);
-			pos = strchr(buf, ':');
-			if (pos == NULL)
-				continue;
-			pos = strchr(pos + 1, ':');
-			if (pos == NULL)
-				continue;
-			pos = strchr(pos + 1, ':');
-			if (pos == NULL)
-				continue;
-			pos++;
-			end = strchr(pos, ':');
-			if (!end)
-				continue;
-			*end = '\0';
-			res = os_snprintf(icon->lang, sizeof(icon->lang),
-					  "%s", pos);
-			if (os_snprintf_error(sizeof(icon->lang), res))
-				break;
-			pos = end + 1;
-
-			end = strchr(pos, ':');
-			if (end)
-				*end = '\0';
-			res = os_snprintf(icon->mime_type,
-					  sizeof(icon->mime_type), "%s", pos);
-			if (os_snprintf_error(sizeof(icon->mime_type), res))
-				break;
-			if (!end)
-				continue;
-			pos = end + 1;
-
-			end = strchr(pos, ':');
-			if (end)
-				*end = '\0';
-			res = os_snprintf(icon->filename,
-					  sizeof(icon->filename), "%s", pos);
-			if (os_snprintf_error(sizeof(icon->filename), res))
-				break;
-			continue;
-		}
-	}
-
-	fclose(f);
-
-	*count = osu_count;
-	return osu;
-}
-
-
-static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
-		       const char *ssid, const char *ssid2, const char *url,
-		       unsigned int methods, int no_prod_assoc,
-		       const char *osu_nai, const char *osu_nai2)
-{
-	int id;
-	const char *ifname = ctx->ifname;
-	char buf[200];
-	struct wpa_ctrl *mon;
-	int res;
-
-	if (ssid2 && ssid2[0] == '\0')
-		ssid2 = NULL;
-
-	if (ctx->osu_ssid) {
-		if (os_strcmp(ssid, ctx->osu_ssid) == 0) {
-			wpa_printf(MSG_DEBUG,
-				   "Enforced OSU SSID matches ANQP info");
-			ssid2 = NULL;
-		} else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) {
-			wpa_printf(MSG_DEBUG,
-				   "Enforced OSU SSID matches RSN[OSEN] info");
-			ssid = ssid2;
-		} else {
-			wpa_printf(MSG_INFO, "Enforced OSU SSID did not match");
-			write_summary(ctx, "Enforced OSU SSID did not match");
-			return -1;
-		}
-	}
-
-	id = add_network(ifname);
-	if (id < 0)
-		return -1;
-	if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
-		return -1;
-	if (ssid2)
-		osu_nai = osu_nai2;
-	if (osu_nai && os_strlen(osu_nai) > 0) {
-		char dir[255], fname[300];
-		if (getcwd(dir, sizeof(dir)) == NULL)
-			return -1;
-		os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
-
-		if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0)
-			return -1;
-
-		if (set_network(ifname, id, "proto", "OSEN") < 0 ||
-		    set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
-		    set_network(ifname, id, "pairwise", "CCMP") < 0 ||
-		    set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 ||
-		    set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
-		    set_network(ifname, id, "ocsp", "2") < 0 ||
-		    set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
-		    set_network_quoted(ifname, id, "ca_cert", fname) < 0)
-			return -1;
-	} else if (ssid2) {
-		wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]");
-		write_summary(ctx, "No OSU_NAI set for RSN[OSEN]");
-		return -1;
-	} else {
-		if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
-			return -1;
-	}
-
-	mon = open_wpa_mon(ifname);
-	if (mon == NULL)
-		return -1;
-
-	wpa_printf(MSG_INFO, "Associate with OSU SSID");
-	write_summary(ctx, "Associate with OSU SSID");
-	snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
-	if (wpa_command(ifname, buf) < 0)
-		return -1;
-
-	res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
-				buf, sizeof(buf));
-
-	wpa_ctrl_detach(mon);
-	wpa_ctrl_close(mon);
-
-	if (res < 0) {
-		wpa_printf(MSG_INFO, "Could not connect to OSU network");
-		write_summary(ctx, "Could not connect to OSU network");
-		wpa_printf(MSG_INFO, "Remove OSU network connection");
-		snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
-		wpa_command(ifname, buf);
-		return -1;
-	}
-
-	write_summary(ctx, "Waiting for IP address for subscription registration");
-	if (wait_ip_addr(ifname, 15) < 0) {
-		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
-	}
-
-	if (no_prod_assoc) {
-		if (res < 0)
-			return -1;
-		wpa_printf(MSG_INFO, "No production connection used for testing purposes");
-		write_summary(ctx, "No production connection used for testing purposes");
-		return 0;
-	}
-
-	ctx->no_reconnect = 1;
-	if (methods & 0x02) {
-		wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect");
-		res = cmd_prov(ctx, url);
-	} else if (methods & 0x01) {
-		wpa_printf(MSG_DEBUG,
-			   "Calling cmd_oma_dm_prov from osu_connect");
-		res = cmd_oma_dm_prov(ctx, url);
-	}
-
-	wpa_printf(MSG_INFO, "Remove OSU network connection");
-	write_summary(ctx, "Remove OSU network connection");
-	snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
-	wpa_command(ifname, buf);
-
-	if (res < 0)
-		return -1;
-
-	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
-	write_summary(ctx, "Requesting reconnection with updated configuration");
-	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
-		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
-		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
-			  int connect, int no_prod_assoc,
-			  const char *friendly_name)
-{
-	char fname[255];
-	FILE *f;
-	struct osu_data *osu = NULL, *last = NULL;
-	size_t osu_count = 0, i, j;
-	int ret;
-
-	write_summary(ctx, "OSU provider selection");
-
-	if (dir == NULL) {
-		wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
-		return -1;
-	}
-
-	snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
-	osu = parse_osu_providers(fname, &osu_count);
-	if (osu == NULL) {
-		wpa_printf(MSG_INFO, "Could not find any OSU providers from %s",
-			   fname);
-		write_result(ctx, "No OSU providers available");
-		return -1;
-	}
-
-	if (friendly_name) {
-		for (i = 0; i < osu_count; i++) {
-			last = &osu[i];
-			for (j = 0; j < last->friendly_name_count; j++) {
-				if (os_strcmp(last->friendly_name[j].text,
-					      friendly_name) == 0)
-					break;
-			}
-			if (j < last->friendly_name_count)
-				break;
-		}
-		if (i == osu_count) {
-			wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
-				   friendly_name);
-			write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
-				      friendly_name);
-			free(osu);
-			return -1;
-		}
-
-		wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
-			   friendly_name);
-		write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
-			      friendly_name);
-		ret = i + 1;
-		goto selected;
-	}
-
-	snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
-	f = fopen(fname, "w");
-	if (f == NULL) {
-		wpa_printf(MSG_INFO, "Could not open %s", fname);
-		free(osu);
-		return -1;
-	}
-
-	fprintf(f, "<html><head>"
-		"<meta http-equiv=\"Content-type\" content=\"text/html; "
-		"charset=utf-8\"<title>Select service operator</title>"
-		"</head><body><h1>Select service operator</h1>\n");
-
-	if (osu_count == 0)
-		fprintf(f, "No online signup available\n");
-
-	for (i = 0; i < osu_count; i++) {
-		last = &osu[i];
-#ifdef ANDROID
-		fprintf(f, "<p>\n"
-			"<a href=\"http://localhost:12345/osu/%d\">"
-			"<table><tr><td>", (int) i + 1);
-#else /* ANDROID */
-		fprintf(f, "<p>\n"
-			"<a href=\"osu://%d\">"
-			"<table><tr><td>", (int) i + 1);
-#endif /* ANDROID */
-		for (j = 0; j < last->icon_count; j++) {
-			fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
-				last->icon[j].id,
-				strcasecmp(last->icon[j].mime_type,
-					   "image/png") == 0 ? "png" : "icon");
-		}
-		fprintf(f, "<td>");
-		for (j = 0; j < last->friendly_name_count; j++) {
-			fprintf(f, "<small>[%s]</small> %s<br>\n",
-				last->friendly_name[j].lang,
-				last->friendly_name[j].text);
-		}
-		fprintf(f, "<tr><td colspan=2>");
-		for (j = 0; j < last->serv_desc_count; j++) {
-			fprintf(f, "<small>[%s]</small> %s<br>\n",
-				last->serv_desc[j].lang,
-				last->serv_desc[j].text);
-		}
-		fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
-			"SSID: %s<br>\n",
-			last->bssid, last->osu_ssid);
-		if (last->osu_ssid2[0])
-			fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2);
-		if (last->osu_nai[0])
-			fprintf(f, "NAI: %s<br>\n", last->osu_nai);
-		if (last->osu_nai2[0])
-			fprintf(f, "NAI2: %s<br>\n", last->osu_nai2);
-		fprintf(f, "URL: %s<br>\n"
-			"methods:%s%s<br>\n"
-			"</small></p>\n",
-			last->url,
-			last->methods & 0x01 ? " OMA-DM" : "",
-			last->methods & 0x02 ? " SOAP-XML-SPP" : "");
-	}
-
-	fprintf(f, "</body></html>\n");
-
-	fclose(f);
-
-	snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
-	write_summary(ctx, "Start web browser with OSU provider selection page");
-	ret = hs20_web_browser(fname, 0);
-
-selected:
-	if (ret > 0 && (size_t) ret <= osu_count) {
-		char *data;
-		size_t data_len;
-
-		wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
-		last = &osu[ret - 1];
-		ret = 0;
-		wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
-		wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
-		if (last->osu_ssid2[0])
-			wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2);
-		wpa_printf(MSG_INFO, "URL: %s", last->url);
-		write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
-			      ret, last->bssid, last->osu_ssid, last->url);
-
-		ctx->friendly_name_count = last->friendly_name_count;
-		for (j = 0; j < last->friendly_name_count; j++) {
-			wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
-				   last->friendly_name[j].lang,
-				   last->friendly_name[j].text);
-			os_strlcpy(ctx->friendly_name[j].lang,
-				   last->friendly_name[j].lang,
-				   sizeof(ctx->friendly_name[j].lang));
-			os_strlcpy(ctx->friendly_name[j].text,
-				   last->friendly_name[j].text,
-				   sizeof(ctx->friendly_name[j].text));
-		}
-
-		ctx->icon_count = last->icon_count;
-		for (j = 0; j < last->icon_count; j++) {
-			char fname[256];
-
-			os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
-				    dir, last->icon[j].id,
-				    strcasecmp(last->icon[j].mime_type,
-					       "image/png") == 0 ?
-				    "png" : "icon");
-			wpa_printf(MSG_INFO, "ICON: %s (%s)",
-				   fname, last->icon[j].filename);
-			os_strlcpy(ctx->icon_filename[j],
-				   last->icon[j].filename,
-				   sizeof(ctx->icon_filename[j]));
-
-			data = os_readfile(fname, &data_len);
-			if (data) {
-				sha256_vector(1, (const u8 **) &data, &data_len,
-					      ctx->icon_hash[j]);
-				os_free(data);
-			}
-		}
-
-		if (connect == 2) {
-			if (last->methods & 0x02) {
-				wpa_printf(MSG_DEBUG,
-					   "Calling cmd_prov from cmd_osu_select");
-				ret = cmd_prov(ctx, last->url);
-			} else if (last->methods & 0x01) {
-				wpa_printf(MSG_DEBUG,
-					   "Calling cmd_oma_dm_prov from cmd_osu_select");
-				ret = cmd_oma_dm_prov(ctx, last->url);
-			} else {
-				wpa_printf(MSG_DEBUG,
-					   "No supported OSU provisioning method");
-				ret = -1;
-			}
-		} else if (connect) {
-			ret = osu_connect(ctx, last->bssid, last->osu_ssid,
-					  last->osu_ssid2,
-					  last->url, last->methods,
-					  no_prod_assoc, last->osu_nai,
-					  last->osu_nai2);
-		}
-	} else
-		ret = -1;
-
-	free(osu);
-
-	return ret;
-}
-
-
-static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
-		      const char *friendly_name)
-{
-	char dir[255];
-	char fname[300], buf[400];
-	struct wpa_ctrl *mon;
-	const char *ifname;
-	int res;
-
-	ifname = ctx->ifname;
-
-	if (getcwd(dir, sizeof(dir)) == NULL)
-		return -1;
-
-	snprintf(fname, sizeof(fname), "%s/osu-info", dir);
-	if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
-	    errno != EEXIST) {
-		wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
-			   fname, strerror(errno));
-		return -1;
-	}
-
-	android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-
-	snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
-	if (wpa_command(ifname, buf) < 0) {
-		wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
-		return -1;
-	}
-
-	mon = open_wpa_mon(ifname);
-	if (mon == NULL)
-		return -1;
-
-	wpa_printf(MSG_INFO, "Starting OSU fetch");
-	write_summary(ctx, "Starting OSU provider information fetch");
-	if (wpa_command(ifname, "FETCH_OSU") < 0) {
-		wpa_printf(MSG_INFO, "Could not start OSU fetch");
-		wpa_ctrl_detach(mon);
-		wpa_ctrl_close(mon);
-		return -1;
-	}
-	res = get_wpa_cli_event(mon, "OSU provider fetch completed",
-				buf, sizeof(buf));
-
-	wpa_ctrl_detach(mon);
-	wpa_ctrl_close(mon);
-
-	if (res < 0) {
-		wpa_printf(MSG_INFO, "OSU fetch did not complete");
-		write_summary(ctx, "OSU fetch did not complete");
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "OSU provider fetch completed");
-
-	return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name);
-}
-
-
-static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
-		       const char *pps_fname, const char *ca_fname)
-{
-	xml_node_t *pps, *node;
-	char pps_fname_buf[300];
-	char ca_fname_buf[200];
-	char *cred_username = NULL;
-	char *cred_password = NULL;
-	char *sub_rem_uri = NULL;
-	char client_cert_buf[200];
-	char *client_cert = NULL;
-	char client_key_buf[200];
-	char *client_key = NULL;
-	int spp;
-
-	wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
-		   address);
-
-	if (!pps_fname) {
-		char buf[256];
-		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
-		if (os_strncmp(address, "fqdn=", 5) == 0) {
-			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
-			os_snprintf(buf, sizeof(buf), "%s", address + 5);
-			address = NULL;
-		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
-					  sizeof(buf)) < 0) {
-			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
-			return -1;
-		}
-		os_free(ctx->fqdn);
-		ctx->fqdn = os_strdup(buf);
-		if (ctx->fqdn == NULL)
-			return -1;
-		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
-			   buf);
-		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
-			    "SP/%s/pps.xml", ctx->fqdn);
-		pps_fname = pps_fname_buf;
-
-		os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
-			    ctx->fqdn);
-		ca_fname = ca_fname_buf;
-	}
-
-	if (!os_file_exists(pps_fname)) {
-		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
-			   pps_fname);
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
-
-	if (ca_fname && !os_file_exists(ca_fname)) {
-		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
-			   ca_fname);
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
-	ctx->ca_fname = ca_fname;
-
-	pps = node_from_file(ctx->xml, pps_fname);
-	if (pps == NULL) {
-		wpa_printf(MSG_INFO, "Could not read PPS MO");
-		return -1;
-	}
-
-	if (!ctx->fqdn) {
-		char *tmp;
-		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
-		if (node == NULL) {
-			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
-			return -1;
-		}
-		tmp = xml_node_get_text(ctx->xml, node);
-		if (tmp == NULL) {
-			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
-			return -1;
-		}
-		ctx->fqdn = os_strdup(tmp);
-		xml_node_get_text_free(ctx->xml, tmp);
-		if (!ctx->fqdn) {
-			wpa_printf(MSG_INFO, "No FQDN known");
-			return -1;
-		}
-	}
-
-	node = get_child_node(ctx->xml, pps,
-			      "SubscriptionUpdate/UpdateMethod");
-	if (node) {
-		char *tmp;
-		tmp = xml_node_get_text(ctx->xml, node);
-		if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
-			spp = 0;
-		else
-			spp = 1;
-	} else {
-		wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
-		spp = 1;
-	}
-
-	get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
-		    &cred_username, &cred_password);
-	if (cred_username)
-		wpa_printf(MSG_INFO, "Using username: %s", cred_username);
-	if (cred_password)
-		wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
-
-	if (cred_username == NULL && cred_password == NULL &&
-	    get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
-		wpa_printf(MSG_INFO, "Using client certificate");
-		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
-			    "SP/%s/client-cert.pem", ctx->fqdn);
-		client_cert = client_cert_buf;
-		os_snprintf(client_key_buf, sizeof(client_key_buf),
-			    "SP/%s/client-key.pem", ctx->fqdn);
-		client_key = client_key_buf;
-		ctx->client_cert_present = 1;
-	}
-
-	node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
-	if (node) {
-		sub_rem_uri = xml_node_get_text(ctx->xml, node);
-		if (sub_rem_uri &&
-		    (!address || os_strcmp(address, sub_rem_uri) != 0)) {
-			wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
-				   sub_rem_uri);
-			address = sub_rem_uri;
-		}
-	}
-	if (!address) {
-		wpa_printf(MSG_INFO, "Server URL not known");
-		return -1;
-	}
-
-	write_summary(ctx, "Wait for IP address for subscriptiom remediation");
-	wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
-
-	if (wait_ip_addr(ctx->ifname, 15) < 0) {
-		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
-	}
-
-	if (spp)
-		spp_sub_rem(ctx, address, pps_fname,
-			    client_cert, client_key,
-			    cred_username, cred_password, pps);
-	else
-		oma_dm_sub_rem(ctx, address, pps_fname,
-			       client_cert, client_key,
-			       cred_username, cred_password, pps);
-
-	xml_node_get_text_free(ctx->xml, sub_rem_uri);
-	xml_node_get_text_free(ctx->xml, cred_username);
-	str_clear_free(cred_password);
-	xml_node_free(ctx->xml, pps);
-	return 0;
-}
-
-
-static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
-		       const char *pps_fname, const char *ca_fname)
-{
-	xml_node_t *pps;
-	xml_node_t *node;
-	char pps_fname_buf[300];
-	char ca_fname_buf[200];
-	char *uri = NULL;
-	char *cred_username = NULL;
-	char *cred_password = NULL;
-	char client_cert_buf[200];
-	char *client_cert = NULL;
-	char client_key_buf[200];
-	char *client_key = NULL;
-	int spp;
-
-	wpa_printf(MSG_INFO, "Policy update requested");
-
-	if (!pps_fname) {
-		char buf[256];
-		int res;
-
-		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
-		if (address && os_strncmp(address, "fqdn=", 5) == 0) {
-			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
-			os_snprintf(buf, sizeof(buf), "%s", address + 5);
-			address = NULL;
-		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
-					  sizeof(buf)) < 0) {
-			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
-			return -1;
-		}
-		os_free(ctx->fqdn);
-		ctx->fqdn = os_strdup(buf);
-		if (ctx->fqdn == NULL)
-			return -1;
-		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
-			   buf);
-		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
-			    "SP/%s/pps.xml", ctx->fqdn);
-		pps_fname = pps_fname_buf;
-
-		res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf),
-				  "SP/%s/ca.pem", buf);
-		if (os_snprintf_error(sizeof(ca_fname_buf), res)) {
-			os_free(ctx->fqdn);
-			ctx->fqdn = NULL;
-			return -1;
-		}
-		ca_fname = ca_fname_buf;
-	}
-
-	if (!os_file_exists(pps_fname)) {
-		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
-			   pps_fname);
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
-
-	if (ca_fname && !os_file_exists(ca_fname)) {
-		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
-			   ca_fname);
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
-	ctx->ca_fname = ca_fname;
-
-	pps = node_from_file(ctx->xml, pps_fname);
-	if (pps == NULL) {
-		wpa_printf(MSG_INFO, "Could not read PPS MO");
-		return -1;
-	}
-
-	if (!ctx->fqdn) {
-		char *tmp;
-		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
-		if (node == NULL) {
-			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
-			return -1;
-		}
-		tmp = xml_node_get_text(ctx->xml, node);
-		if (tmp == NULL) {
-			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
-			return -1;
-		}
-		ctx->fqdn = os_strdup(tmp);
-		xml_node_get_text_free(ctx->xml, tmp);
-		if (!ctx->fqdn) {
-			wpa_printf(MSG_INFO, "No FQDN known");
-			return -1;
-		}
-	}
-
-	node = get_child_node(ctx->xml, pps,
-			      "Policy/PolicyUpdate/UpdateMethod");
-	if (node) {
-		char *tmp;
-		tmp = xml_node_get_text(ctx->xml, node);
-		if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
-			spp = 0;
-		else
-			spp = 1;
-	} else {
-		wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
-		spp = 1;
-	}
-
-	get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
-		    &cred_username, &cred_password);
-	if (cred_username)
-		wpa_printf(MSG_INFO, "Using username: %s", cred_username);
-	if (cred_password)
-		wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
-
-	if (cred_username == NULL && cred_password == NULL &&
-	    get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
-		wpa_printf(MSG_INFO, "Using client certificate");
-		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
-			    "SP/%s/client-cert.pem", ctx->fqdn);
-		client_cert = client_cert_buf;
-		os_snprintf(client_key_buf, sizeof(client_key_buf),
-			    "SP/%s/client-key.pem", ctx->fqdn);
-		client_key = client_key_buf;
-	}
-
-	if (!address) {
-		node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
-		if (node) {
-			uri = xml_node_get_text(ctx->xml, node);
-			wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
-			address = uri;
-		}
-	}
-	if (!address) {
-		wpa_printf(MSG_INFO, "Server URL not known");
-		return -1;
-	}
-
-	if (spp)
-		spp_pol_upd(ctx, address, pps_fname,
-			    client_cert, client_key,
-			    cred_username, cred_password, pps);
-	else
-		oma_dm_pol_upd(ctx, address, pps_fname,
-			       client_cert, client_key,
-			       cred_username, cred_password, pps);
-
-	xml_node_get_text_free(ctx->xml, uri);
-	xml_node_get_text_free(ctx->xml, cred_username);
-	str_clear_free(cred_password);
-	xml_node_free(ctx->xml, pps);
-
-	return 0;
-}
-
-
-static char * get_hostname(const char *url)
-{
-	const char *pos, *end, *end2;
-	char *ret;
-
-	if (url == NULL)
-		return NULL;
-
-	pos = os_strchr(url, '/');
-	if (pos == NULL)
-		return NULL;
-	pos++;
-	if (*pos != '/')
-		return NULL;
-	pos++;
-
-	end = os_strchr(pos, '/');
-	end2 = os_strchr(pos, ':');
-	if ((end && end2 && end2 < end) || (!end && end2))
-		end = end2;
-	if (end)
-		end--;
-	else {
-		end = pos;
-		while (*end)
-			end++;
-		if (end > pos)
-			end--;
-	}
-
-	ret = os_malloc(end - pos + 2);
-	if (ret == NULL)
-		return NULL;
-
-	os_memcpy(ret, pos, end - pos + 1);
-	ret[end - pos + 1] = '\0';
-
-	return ret;
-}
-
-
-static int osu_cert_cb(void *_ctx, struct http_cert *cert)
-{
-	struct hs20_osu_client *ctx = _ctx;
-	size_t i, j;
-	int found;
-	char *host = NULL;
-
-	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);
-
-	if (ctx->no_osu_cert_validation && cert->url)
-		host = get_hostname(cert->url);
-	else
-		host = get_hostname(ctx->server_url);
-
-	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->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])
-				ctx->server_dnsname_count++;
-		}
-		if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
-			found = 1;
-		wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
-	}
-
-	if (host && !found) {
-		wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
-			   host);
-		write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
-			     host);
-		os_free(host);
-		return -1;
-	}
-
-	os_free(host);
-
-	for (i = 0; i < cert->num_othername; i++) {
-		if (os_strcmp(cert->othername[i].oid,
-			      "1.3.6.1.4.1.40808.1.1.1") == 0) {
-			wpa_hexdump_ascii(MSG_INFO,
-					  "id-wfa-hotspot-friendlyName",
-					  cert->othername[i].data,
-					  cert->othername[i].len);
-		}
-	}
-
-	for (j = 0; !ctx->no_osu_cert_validation &&
-		     j < ctx->friendly_name_count; j++) {
-		int found = 0;
-		for (i = 0; i < cert->num_othername; i++) {
-			if (os_strcmp(cert->othername[i].oid,
-				      "1.3.6.1.4.1.40808.1.1.1") != 0)
-				continue;
-			if (cert->othername[i].len < 3)
-				continue;
-			if (os_strncasecmp((char *) cert->othername[i].data,
-					   ctx->friendly_name[j].lang, 3) != 0)
-				continue;
-			if (os_strncmp((char *) cert->othername[i].data + 3,
-				       ctx->friendly_name[j].text,
-				       cert->othername[i].len - 3) == 0) {
-				found = 1;
-				break;
-			}
-		}
-
-		if (!found) {
-			wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
-				   ctx->friendly_name[j].lang,
-				   ctx->friendly_name[j].text);
-			write_result(ctx, "No friendly name match found for '[%s]%s'",
-				     ctx->friendly_name[j].lang,
-				     ctx->friendly_name[j].text);
-			return -1;
-		}
-	}
-
-	for (i = 0; i < cert->num_logo; i++) {
-		struct http_logo *logo = &cert->logo[i];
-
-		wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
-			   logo->alg_oid, logo->uri);
-		wpa_hexdump_ascii(MSG_INFO, "hashValue",
-				  logo->hash, logo->hash_len);
-	}
-
-	for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
-		int found = 0;
-		char *name = ctx->icon_filename[j];
-		size_t name_len = os_strlen(name);
-
-		wpa_printf(MSG_INFO,
-			   "[%zu] Looking for icon file name '%s' match",
-			   j, name);
-		for (i = 0; i < cert->num_logo; i++) {
-			struct http_logo *logo = &cert->logo[i];
-			size_t uri_len = os_strlen(logo->uri);
-			char *pos;
-
-			wpa_printf(MSG_INFO,
-				   "[%zu] Comparing to '%s' uri_len=%d name_len=%d",
-				   i, logo->uri, (int) uri_len, (int) name_len);
-			if (uri_len < 1 + name_len) {
-				wpa_printf(MSG_INFO, "URI Length is too short");
-				continue;
-			}
-			pos = &logo->uri[uri_len - name_len - 1];
-			if (*pos != '/')
-				continue;
-			pos++;
-			if (os_strcmp(pos, name) == 0) {
-				found = 1;
-				break;
-			}
-		}
-
-		if (!found) {
-			wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
-				   name);
-			write_result(ctx,
-				     "No icon filename match found for '%s'",
-				     name);
-			return -1;
-		}
-	}
-
-	for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
-		int found = 0;
-
-		for (i = 0; i < cert->num_logo; i++) {
-			struct http_logo *logo = &cert->logo[i];
-
-			if (logo->hash_len != 32) {
-				wpa_printf(MSG_INFO,
-					   "[%zu][%zu] Icon hash length invalid (should be 32): %d",
-					   j, i, (int) logo->hash_len);
-				continue;
-			}
-			if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
-				found = 1;
-				break;
-			}
-
-			wpa_printf(MSG_DEBUG,
-				   "[%zu][%zu] Icon hash did not match", j, i);
-			wpa_hexdump_ascii(MSG_DEBUG, "logo->hash",
-					  logo->hash, 32);
-			wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]",
-					  ctx->icon_hash[j], 32);
-		}
-
-		if (!found) {
-			wpa_printf(MSG_INFO,
-				   "No icon hash match (by hash) found");
-			write_result(ctx,
-				     "No icon hash match (by hash) found");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-
 static int init_ctx(struct hs20_osu_client *ctx)
 {
-	xml_node_t *devinfo, *devid;
-
 	os_memset(ctx, 0, sizeof(*ctx));
 	ctx->ifname = "wlan0";
 	ctx->xml = xml_node_init_ctx(ctx, NULL);
 	if (ctx->xml == NULL)
 		return -1;
 
-	devinfo = node_from_file(ctx->xml, "devinfo.xml");
-	if (devinfo) {
-		devid = get_node(ctx->xml, devinfo, "DevId");
-		if (devid) {
-			char *tmp = xml_node_get_text(ctx->xml, devid);
-
-			if (tmp) {
-				ctx->devid = os_strdup(tmp);
-				xml_node_get_text_free(ctx->xml, tmp);
-			}
-		}
-		xml_node_free(ctx->xml, devinfo);
-	}
-
 	ctx->http = http_init_ctx(ctx, ctx->xml);
 	if (ctx->http == NULL) {
 		xml_node_deinit_ctx(ctx->xml);
 		return -1;
 	}
 	http_ocsp_set(ctx->http, 2);
-	http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
 
 	return 0;
 }
@@ -3156,17 +1431,8 @@
 
 static void deinit_ctx(struct hs20_osu_client *ctx)
 {
-	size_t i;
-
 	http_deinit_ctx(ctx->http);
 	xml_node_deinit_ctx(ctx->xml);
-	os_free(ctx->fqdn);
-	os_free(ctx->server_url);
-	os_free(ctx->devid);
-
-	for (i = 0; i < ctx->server_dnsname_count; i++)
-		os_free(ctx->server_dnsname[i]);
-	os_free(ctx->server_dnsname);
 }
 
 
@@ -3209,19 +1475,8 @@
 	       "- from_tnds <XML MO in TNDS format> <XML MO>\n"
 	       "- set_pps <PerProviderSubscription XML file name>\n"
 	       "- get_fqdn <PerProviderSubscription XML file name>\n"
-	       "- pol_upd [Server URL] [PPS] [CA cert]\n"
-	       "- sub_rem <Server URL> [PPS] [CA cert]\n"
-	       "- prov <Server URL> [CA cert]\n"
-	       "- oma_dm_prov <Server URL> [CA cert]\n"
-	       "- sim_prov <Server URL> [CA cert]\n"
-	       "- oma_dm_sim_prov <Server URL> [CA cert]\n"
-	       "- signup [CA cert]\n"
-	       "- dl_osu_ca <PPS> <CA file>\n"
-	       "- dl_polupd_ca <PPS> <CA file>\n"
 	       "- dl_aaa_ca <PPS> <CA file>\n"
-	       "- browser <URL>\n"
-	       "- parse_cert <X.509 certificate (DER)>\n"
-	       "- osu_select <OSU info directory> [CA cert]\n");
+	       "- browser <URL>\n");
 }
 
 
@@ -3230,8 +1485,6 @@
 	struct hs20_osu_client ctx;
 	int c;
 	int ret = 0;
-	int no_prod_assoc = 0;
-	const char *friendly_name = NULL;
 	const char *wpa_debug_file_path = NULL;
 	extern char *wpas_ctrl_path;
 	extern int wpa_debug_level;
@@ -3242,7 +1495,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:");
+		c = getopt(argc, argv, "df:hKqr:s:S:tTw:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -3256,15 +1509,6 @@
 		case 'K':
 			wpa_debug_show_keys++;
 			break;
-		case 'N':
-			no_prod_assoc = 1;
-			break;
-		case 'o':
-			ctx.osu_ssid = optarg;
-			break;
-		case 'O':
-			friendly_name = optarg;
-			break;
 		case 'q':
 			wpa_debug_level++;
 			break;
@@ -3286,9 +1530,6 @@
 		case 'w':
 			wpas_ctrl_path = optarg;
 			break;
-		case 'x':
-			spp_xsd_fname = optarg;
-			break;
 		case 'h':
 		default:
 			usage();
@@ -3335,62 +1576,12 @@
 			exit(0);
 		}
 		cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
-	} else if (strcmp(argv[optind], "sub_rem") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		ret = cmd_sub_rem(&ctx, argv[optind + 1],
-				  argc > optind + 2 ? argv[optind + 2] : NULL,
-				  argc > optind + 3 ? argv[optind + 3] : NULL);
-	} else if (strcmp(argv[optind], "pol_upd") == 0) {
-		ret = cmd_pol_upd(&ctx,
-				  argc > optind + 1 ? argv[optind + 1] : NULL,
-				  argc > optind + 2 ? argv[optind + 2] : NULL,
-				  argc > optind + 3 ? argv[optind + 3] : NULL);
-	} else if (strcmp(argv[optind], "prov") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		ctx.ca_fname = argv[optind + 2];
-		wpa_printf(MSG_DEBUG, "Calling cmd_prov from main");
-		cmd_prov(&ctx, argv[optind + 1]);
-	} else if (strcmp(argv[optind], "sim_prov") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		ctx.ca_fname = argv[optind + 2];
-		cmd_sim_prov(&ctx, argv[optind + 1]);
-	} else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
-	} else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
 	} else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
 		if (argc - optind < 2) {
 			usage();
 			exit(0);
 		}
 		cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
-	} else if (strcmp(argv[optind], "osu_select") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
-		cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
-	} else if (strcmp(argv[optind], "signup") == 0) {
-		ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
-		ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
 	} else if (strcmp(argv[optind], "set_pps") == 0) {
 		if (argc - optind < 2) {
 			usage();
@@ -3403,42 +1594,6 @@
 			exit(0);
 		}
 		ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
-	} else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		ctx.ca_fname = argv[optind + 2];
-		cmd_oma_dm_prov(&ctx, argv[optind + 1]);
-	} else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		ctx.ca_fname = argv[optind + 2];
-		if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) {
-			write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
-			return -1;
-		}
-	} else if (strcmp(argv[optind], "oma_dm_add") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
-	} else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
-	} else if (strcmp(argv[optind], "est_csr") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		mkdir("Cert", S_IRWXU);
-		est_build_csr(&ctx, argv[optind + 1]);
 	} else if (strcmp(argv[optind], "browser") == 0) {
 		int ret;
 
@@ -3451,15 +1606,6 @@
 			   argv[optind + 1]);
 		ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls);
 		wpa_printf(MSG_INFO, "Web browser result: %d", ret);
-	} else if (strcmp(argv[optind], "parse_cert") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-
-		wpa_debug_level = MSG_MSGDUMP;
-		http_parse_x509_certificate(ctx.http, argv[optind + 1]);
-		wpa_debug_level = MSG_INFO;
 	} else {
 		wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
 	}
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
index 9b45b03..3bfbb0d 100644
--- a/hs20/client/osu_client.h
+++ b/hs20/client/osu_client.h
@@ -9,113 +9,16 @@
 #ifndef OSU_CLIENT_H
 #define OSU_CLIENT_H
 
-#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
-
-#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
-#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
-#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
-#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
-
-
-#define MAX_OSU_VALS 10
-
-struct osu_lang_text {
-	char lang[4];
-	char text[253];
-};
-
 struct hs20_osu_client {
 	struct xml_node_ctx *xml;
 	struct http_ctx *http;
-	int no_reconnect;
-	char pps_fname[300];
-	char *devid;
 	const char *result_file;
 	const char *summary_file;
 	const char *ifname;
-	const char *ca_fname;
-	int no_osu_cert_validation; /* for EST operations */
-	char *fqdn;
-	char *server_url;
-	struct osu_lang_text friendly_name[MAX_OSU_VALS];
-	size_t friendly_name_count;
-	size_t icon_count;
-	char icon_filename[MAX_OSU_VALS][256];
-	u8 icon_hash[MAX_OSU_VALS][32];
-	int pps_cred_set;
-	int pps_updated;
-	int client_cert_present;
-	char **server_dnsname;
-	size_t server_dnsname_count;
-	const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
 #define WORKAROUND_OCSP_OPTIONAL 0x00000001
 	unsigned long int workarounds;
 	int ignore_tls; /* whether to ignore TLS validation issues with HTTPS
 			 * server certificate */
 };
 
-
-/* osu_client.c */
-
-void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
-	__attribute__ ((format (printf, 2, 3)));
-void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
-	__attribute__ ((format (printf, 2, 3)));
-
-void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
-		     xml_node_t *node);
-int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
-int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
-		    xml_node_t *add_mo, char *fname, size_t fname_len);
-void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
-		 const char *alt_loc, char **user, char **pw);
-int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
-		    xml_node_t *pps);
-void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
-
-
-/* spp_client.c */
-
-void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
-		 const char *pps_fname,
-		 const char *client_cert, const char *client_key,
-		 const char *cred_username, const char *cred_password,
-		 xml_node_t *pps);
-void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
-		 const char *pps_fname,
-		 const char *client_cert, const char *client_key,
-		 const char *cred_username, const char *cred_password,
-		 xml_node_t *pps);
-int cmd_prov(struct hs20_osu_client *ctx, const char *url);
-int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
-
-
-/* oma_dm_client.c */
-
-int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
-int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
-void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
-		    const char *pps_fname,
-		    const char *client_cert, const char *client_key,
-		    const char *cred_username, const char *cred_password,
-		    xml_node_t *pps);
-void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
-		    const char *pps_fname,
-		    const char *client_cert, const char *client_key,
-		    const char *cred_username, const char *cred_password,
-		    xml_node_t *pps);
-void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
-			const char *pps_fname);
-void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
-		    const char *add_fname);
-void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
-			const char *replace_fname);
-
-/* est.c */
-
-int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
-int est_build_csr(struct hs20_osu_client *ctx, const char *url);
-int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
-		      const char *user, const char *pw);
-
 #endif /* OSU_CLIENT_H */
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
deleted file mode 100644
index 194518e..0000000
--- a/hs20/client/spp_client.c
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- * Hotspot 2.0 SPP client
- * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <sys/stat.h>
-
-#include "common.h"
-#include "browser.h"
-#include "wpa_ctrl.h"
-#include "wpa_helpers.h"
-#include "xml-utils.h"
-#include "http-utils.h"
-#include "utils/base64.h"
-#include "crypto/crypto.h"
-#include "crypto/sha256.h"
-#include "osu_client.h"
-
-
-extern const char *spp_xsd_fname;
-
-static int hs20_spp_update_response(struct hs20_osu_client *ctx,
-				    const char *session_id,
-				    const char *spp_status,
-				    const char *error_code);
-static void hs20_policy_update_complete(
-	struct hs20_osu_client *ctx, const char *pps_fname);
-
-
-static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
-				 char *attr_name)
-{
-	return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
-}
-
-
-static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
-			     const char *expected_name)
-{
-	struct xml_node_ctx *xctx = ctx->xml;
-	const char *name;
-	char *err;
-	int ret;
-
-	if (!xml_node_is_element(xctx, node))
-		return -1;
-
-	name = xml_node_get_localname(xctx, node);
-	if (name == NULL)
-		return -1;
-
-	if (strcmp(expected_name, name) != 0) {
-		wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
-			   name, expected_name);
-		write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
-			      name, expected_name);
-		return -1;
-	}
-
-	ret = xml_validate(xctx, node, spp_xsd_fname, &err);
-	if (ret < 0) {
-		wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
-		write_summary(ctx, "SPP XML schema validation failed");
-		os_free(err);
-	}
-	return ret;
-}
-
-
-static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
-			     xml_node_t *parent, const char *urn,
-			     const char *fname)
-{
-	xml_node_t *node;
-	xml_node_t *fnode, *tnds;
-	char *str;
-
-	errno = 0;
-	fnode = node_from_file(ctx, fname);
-	if (!fnode) {
-		wpa_printf(MSG_ERROR,
-			   "Failed to create XML node from file: %s, possible error: %s",
-			   fname, strerror(errno));
-		return;
-	}
-	tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
-	xml_node_free(ctx, fnode);
-	if (!tnds)
-		return;
-
-	str = xml_node_to_str(ctx, tnds);
-	xml_node_free(ctx, tnds);
-	if (str == NULL)
-		return;
-
-	node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
-	if (node)
-		xml_node_add_attr(ctx, node, ns, "moURN", urn);
-	os_free(str);
-}
-
-
-static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
-					    xml_namespace_t **ret_ns,
-					    const char *session_id,
-					    const char *reason)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node;
-
-	write_summary(ctx, "Building sppPostDevData requestReason='%s'",
-		      reason);
-	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
-					"sppPostDevData");
-	if (spp_node == NULL)
-		return NULL;
-	if (ret_ns)
-		*ret_ns = ns;
-
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
-	xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
-	if (session_id)
-		xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
-				  session_id);
-	xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
-			  "http://localhost:12345/");
-
-	xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
-			     "1.0");
-	xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
-			     URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
-			     URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
-
-	add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
-			 "devinfo.xml");
-	add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
-			 "devdetail.xml");
-
-	return spp_node;
-}
-
-
-static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
-			       xml_node_t *update)
-{
-	xml_node_t *node, *parent, *tnds, *unode;
-	char *str;
-	const char *name;
-	char *uri, *pos;
-	char *cdata, *cdata_end;
-	size_t fqdn_len;
-
-	wpa_printf(MSG_INFO, "Processing updateNode");
-	debug_dump_node(ctx, "updateNode", update);
-
-	uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
-	if (uri == NULL) {
-		wpa_printf(MSG_INFO, "No managementTreeURI present");
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
-
-	name = os_strrchr(uri, '/');
-	if (name == NULL) {
-		wpa_printf(MSG_INFO, "Unexpected URI");
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-	name++;
-	wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
-
-	str = xml_node_get_text(ctx->xml, update);
-	if (str == NULL) {
-		wpa_printf(MSG_INFO, "Could not extract MO text");
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
-	cdata = strstr(str, "<![CDATA[");
-	cdata_end = strstr(str, "]]>");
-	if (cdata && cdata_end && cdata_end > cdata &&
-	    cdata < strstr(str, "MgmtTree") &&
-	    cdata_end > strstr(str, "/MgmtTree")) {
-		char *tmp;
-		wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
-		tmp = strdup(cdata + 9);
-		if (tmp) {
-			cdata_end = strstr(tmp, "]]>");
-			if (cdata_end)
-				*cdata_end = '\0';
-			wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
-				   tmp);
-			tnds = xml_node_from_buf(ctx->xml, tmp);
-			free(tmp);
-		} else
-			tnds = NULL;
-	} else
-		tnds = xml_node_from_buf(ctx->xml, str);
-	xml_node_get_text_free(ctx->xml, str);
-	if (tnds == NULL) {
-		wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-
-	unode = tnds_to_mo(ctx->xml, tnds);
-	xml_node_free(ctx->xml, tnds);
-	if (unode == NULL) {
-		wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-
-	debug_dump_node(ctx, "Parsed TNDS", unode);
-
-	if (get_node_uri(ctx->xml, unode, name) == NULL) {
-		wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
-		xml_node_free(ctx->xml, unode);
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-
-	if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
-		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
-		xml_node_free(ctx->xml, unode);
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-	pos = uri + 8;
-
-	if (ctx->fqdn == NULL) {
-		wpa_printf(MSG_INFO, "FQDN not known");
-		xml_node_free(ctx->xml, unode);
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-	fqdn_len = os_strlen(ctx->fqdn);
-	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
-	    pos[fqdn_len] != '/') {
-		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
-			   ctx->fqdn);
-		xml_node_free(ctx->xml, unode);
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-	pos += fqdn_len + 1;
-
-	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
-		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
-			   ctx->fqdn);
-		xml_node_free(ctx->xml, unode);
-		xml_node_get_attr_value_free(ctx->xml, uri);
-		return -1;
-	}
-	pos += 24;
-
-	wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
-
-	node = get_node(ctx->xml, pps, pos);
-	if (node) {
-		parent = xml_node_get_parent(ctx->xml, node);
-		xml_node_detach(ctx->xml, node);
-		wpa_printf(MSG_INFO, "Replace '%s' node", name);
-	} else {
-		char *pos2;
-		pos2 = os_strrchr(pos, '/');
-		if (pos2 == NULL) {
-			parent = pps;
-		} else {
-			*pos2 = '\0';
-			parent = get_node(ctx->xml, pps, pos);
-		}
-		if (parent == NULL) {
-			wpa_printf(MSG_INFO, "Could not find parent %s", pos);
-			xml_node_free(ctx->xml, unode);
-			xml_node_get_attr_value_free(ctx->xml, uri);
-			return -1;
-		}
-		wpa_printf(MSG_INFO, "Add '%s' node", name);
-	}
-	xml_node_add_child(ctx->xml, parent, unode);
-
-	xml_node_get_attr_value_free(ctx->xml, uri);
-
-	return 0;
-}
-
-
-static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
-		      const char *pps_fname, xml_node_t *pps)
-{
-	wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
-	xml_node_for_each_sibling(ctx->xml, update) {
-		xml_node_for_each_check(ctx->xml, update);
-		if (process_update_node(ctx, pps, update) < 0)
-			return -1;
-	}
-
-	return update_pps_file(ctx, pps_fname, pps);
-}
-
-
-static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
-				  const char *pps_fname)
-{
-	/*
-	 * Update wpa_supplicant credentials and reconnect using updated
-	 * information.
-	 */
-	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
-	cmd_set_pps(ctx, pps_fname);
-
-	if (ctx->no_reconnect)
-		return;
-
-	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
-	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
-		wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
-}
-
-
-static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
-				       xml_node_t *cmd,
-				       const char *session_id,
-				       const char *pps_fname)
-{
-	xml_namespace_t *ns;
-	xml_node_t *node, *ret_node;
-	char *urn;
-
-	urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
-	if (!urn) {
-		wpa_printf(MSG_INFO, "No URN included");
-		return NULL;
-	}
-	wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
-	if (strcasecmp(urn, URN_HS20_PPS) != 0) {
-		wpa_printf(MSG_INFO, "Unsupported moURN");
-		xml_node_get_attr_value_free(ctx->xml, urn);
-		return NULL;
-	}
-	xml_node_get_attr_value_free(ctx->xml, urn);
-
-	if (!pps_fname) {
-		wpa_printf(MSG_INFO, "PPS file name no known");
-		return NULL;
-	}
-
-	node = build_spp_post_dev_data(ctx, &ns, session_id,
-				       "MO upload");
-	if (node == NULL)
-		return NULL;
-	add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
-
-	ret_node = soap_send_receive(ctx->http, node);
-	if (ret_node == NULL)
-		return NULL;
-
-	debug_dump_node(ctx, "Received response to MO upload", ret_node);
-
-	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
-		wpa_printf(MSG_INFO, "SPP validation failed");
-		xml_node_free(ctx->xml, ret_node);
-		return NULL;
-	}
-
-	return ret_node;
-}
-
-
-static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
-		       char *fname, size_t fname_len)
-{
-	char *uri, *urn;
-	int ret;
-
-	debug_dump_node(ctx, "Received addMO", add_mo);
-
-	urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
-	if (urn == NULL) {
-		wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
-	if (strcasecmp(urn, URN_HS20_PPS) != 0) {
-		wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
-		xml_node_get_attr_value_free(ctx->xml, urn);
-		return -1;
-	}
-	xml_node_get_attr_value_free(ctx->xml, urn);
-
-	uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
-	if (uri == NULL) {
-		wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
-		return -1;
-	}
-	wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
-
-	ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
-	xml_node_get_attr_value_free(ctx->xml, uri);
-	return ret;
-}
-
-
-static int process_spp_user_input_response(struct hs20_osu_client *ctx,
-					   const char *session_id,
-					   xml_node_t *add_mo)
-{
-	int ret;
-	char fname[300];
-
-	debug_dump_node(ctx, "addMO", add_mo);
-
-	wpa_printf(MSG_INFO, "Subscription registration completed");
-
-	if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
-		wpa_printf(MSG_INFO, "Could not add MO");
-		ret = hs20_spp_update_response(
-			ctx, session_id,
-			"Error occurred",
-			"MO addition or update failed");
-		return 0;
-	}
-
-	ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
-	if (ret == 0)
-		hs20_sub_rem_complete(ctx, fname);
-
-	return 0;
-}
-
-
-static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
-						    const char *session_id)
-{
-	xml_node_t *node, *ret_node;
-
-	node = build_spp_post_dev_data(ctx, NULL, session_id,
-				       "User input completed");
-	if (node == NULL)
-		return NULL;
-
-	ret_node = soap_send_receive(ctx->http, node);
-	if (!ret_node) {
-		if (soap_reinit_client(ctx->http) < 0)
-			return NULL;
-		wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
-		node = build_spp_post_dev_data(ctx, NULL, session_id,
-					       "User input completed");
-		if (node == NULL)
-			return NULL;
-		ret_node = soap_send_receive(ctx->http, node);
-		if (ret_node == NULL)
-			return NULL;
-		wpa_printf(MSG_INFO, "Continue with new connection");
-	}
-
-	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
-		wpa_printf(MSG_INFO, "SPP validation failed");
-		xml_node_free(ctx->xml, ret_node);
-		return NULL;
-	}
-
-	return ret_node;
-}
-
-
-static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
-					     xml_node_t *cmd,
-					     const char *session_id,
-					     const char *pps_fname)
-{
-	xml_namespace_t *ns;
-	xml_node_t *node, *ret_node;
-	int res;
-
-	wpa_printf(MSG_INFO, "Client certificate enrollment");
-
-	res = osu_get_certificate(ctx, cmd);
-	if (res < 0)
-		wpa_printf(MSG_INFO, "EST simpleEnroll failed");
-
-	node = build_spp_post_dev_data(ctx, &ns, session_id,
-				       res == 0 ?
-				       "Certificate enrollment completed" :
-				       "Certificate enrollment failed");
-	if (node == NULL)
-		return NULL;
-
-	ret_node = soap_send_receive(ctx->http, node);
-	if (ret_node == NULL)
-		return NULL;
-
-	debug_dump_node(ctx, "Received response to certificate enrollment "
-			"completed", ret_node);
-
-	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
-		wpa_printf(MSG_INFO, "SPP validation failed");
-		xml_node_free(ctx->xml, ret_node);
-		return NULL;
-	}
-
-	return ret_node;
-}
-
-
-static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
-			 const char *session_id, const char *pps_fname,
-			 xml_node_t *pps, xml_node_t **ret_node)
-{
-	xml_node_t *cmd;
-	const char *name;
-	char *uri;
-	char *id = strdup(session_id);
-
-	if (id == NULL)
-		return -1;
-
-	*ret_node = NULL;
-
-	debug_dump_node(ctx, "exec", exec);
-
-	xml_node_for_each_child(ctx->xml, cmd, exec) {
-		xml_node_for_each_check(ctx->xml, cmd);
-		break;
-	}
-	if (!cmd) {
-		wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
-			   cmd);
-		free(id);
-		return -1;
-	}
-
-	name = xml_node_get_localname(ctx->xml, cmd);
-
-	if (strcasecmp(name, "launchBrowserToURI") == 0) {
-		int res;
-		uri = xml_node_get_text(ctx->xml, cmd);
-		if (!uri) {
-			wpa_printf(MSG_INFO, "No URI found");
-			free(id);
-			return -1;
-		}
-		wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
-		write_summary(ctx, "Launch browser to URI '%s'", uri);
-		res = hs20_web_browser(uri, 1);
-		xml_node_get_text_free(ctx->xml, uri);
-		if (res > 0) {
-			wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
-				   id);
-			write_summary(ctx, "User response in browser completed successfully");
-			*ret_node = hs20_spp_user_input_completed(ctx, id);
-			free(id);
-			return *ret_node ? 0 : -1;
-		} else {
-			wpa_printf(MSG_INFO, "Failed to receive user response");
-			write_summary(ctx, "Failed to receive user response");
-			hs20_spp_update_response(
-				ctx, id, "Error occurred", "Other");
-			free(id);
-			return -1;
-		}
-	}
-
-	if (strcasecmp(name, "uploadMO") == 0) {
-		if (pps_fname == NULL)
-			return -1;
-		*ret_node = hs20_spp_upload_mo(ctx, cmd, id,
-					       pps_fname);
-		free(id);
-		return *ret_node ? 0 : -1;
-	}
-
-	if (strcasecmp(name, "getCertificate") == 0) {
-		*ret_node = hs20_spp_get_certificate(ctx, cmd, id,
-						     pps_fname);
-		free(id);
-		return *ret_node ? 0 : -1;
-	}
-
-	wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
-	free(id);
-	return -1;
-}
-
-
-enum spp_post_dev_data_use {
-	SPP_SUBSCRIPTION_REMEDIATION,
-	SPP_POLICY_UPDATE,
-	SPP_SUBSCRIPTION_REGISTRATION,
-};
-
-static void process_spp_post_dev_data_response(
-	struct hs20_osu_client *ctx,
-	enum spp_post_dev_data_use use, xml_node_t *node,
-	const char *pps_fname, xml_node_t *pps)
-{
-	xml_node_t *child;
-	char *status = NULL;
-	xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
-	char *session_id = NULL;
-
-	debug_dump_node(ctx, "sppPostDevDataResponse node", node);
-
-	status = get_spp_attr_value(ctx->xml, node, "sppStatus");
-	if (status == NULL) {
-		wpa_printf(MSG_INFO, "No sppStatus attribute");
-		goto out;
-	}
-	write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
-		      status);
-
-	session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
-	if (session_id == NULL) {
-		wpa_printf(MSG_INFO, "No sessionID attribute");
-		goto out;
-	}
-
-	wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s'  sessionID: '%s'",
-		   status, session_id);
-
-	xml_node_for_each_child(ctx->xml, child, node) {
-		const char *name;
-		xml_node_for_each_check(ctx->xml, child);
-		debug_dump_node(ctx, "child", child);
-		name = xml_node_get_localname(ctx->xml, child);
-		wpa_printf(MSG_INFO, "localname: '%s'", name);
-		if (!update && strcasecmp(name, "updateNode") == 0)
-			update = child;
-		if (!exec && strcasecmp(name, "exec") == 0)
-			exec = child;
-		if (!add_mo && strcasecmp(name, "addMO") == 0)
-			add_mo = child;
-		if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
-			no_mo = child;
-	}
-
-	if (use == SPP_SUBSCRIPTION_REMEDIATION &&
-	    strcasecmp(status,
-		       "Remediation complete, request sppUpdateResponse") == 0)
-	{
-		int res, ret;
-		if (!update && !no_mo) {
-			wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
-			goto out;
-		}
-		wpa_printf(MSG_INFO, "Subscription remediation completed");
-		res = update_pps(ctx, update, pps_fname, pps);
-		if (res < 0)
-			wpa_printf(MSG_INFO, "Failed to update PPS MO");
-		ret = hs20_spp_update_response(
-			ctx, session_id,
-			res < 0 ? "Error occurred" : "OK",
-			res < 0 ? "MO addition or update failed" : NULL);
-		if (res == 0 && ret == 0)
-			hs20_sub_rem_complete(ctx, pps_fname);
-		goto out;
-	}
-
-	if (use == SPP_SUBSCRIPTION_REMEDIATION &&
-	    strcasecmp(status, "Exchange complete, release TLS connection") ==
-	    0) {
-		if (!no_mo) {
-			wpa_printf(MSG_INFO, "No noMOUpdate element");
-			goto out;
-		}
-		wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
-		goto out;
-	}
-
-	if (use == SPP_POLICY_UPDATE &&
-	    strcasecmp(status, "Update complete, request sppUpdateResponse") ==
-	    0) {
-		int res, ret;
-		wpa_printf(MSG_INFO, "Policy update received - update PPS");
-		res = update_pps(ctx, update, pps_fname, pps);
-		ret = hs20_spp_update_response(
-			ctx, session_id,
-			res < 0 ? "Error occurred" : "OK",
-			res < 0 ? "MO addition or update failed" : NULL);
-		if (res == 0 && ret == 0)
-			hs20_policy_update_complete(ctx, pps_fname);
-		goto out;
-	}
-
-	if (use == SPP_SUBSCRIPTION_REGISTRATION &&
-	    strcasecmp(status, "Provisioning complete, request "
-		       "sppUpdateResponse")  == 0) {
-		if (!add_mo) {
-			wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
-			goto out;
-		}
-		process_spp_user_input_response(ctx, session_id, add_mo);
-		node = NULL;
-		goto out;
-	}
-
-	if (strcasecmp(status, "No update available at this time") == 0) {
-		wpa_printf(MSG_INFO, "No update available at this time");
-		goto out;
-	}
-
-	if (strcasecmp(status, "OK") == 0) {
-		int res;
-		xml_node_t *ret;
-
-		if (!exec) {
-			wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
-			goto out;
-		}
-		res = hs20_spp_exec(ctx, exec, session_id,
-				    pps_fname, pps, &ret);
-		/* xml_node_free(ctx->xml, node); */
-		node = NULL;
-		if (res == 0 && ret)
-			process_spp_post_dev_data_response(ctx, use,
-							   ret, pps_fname, pps);
-		goto out;
-	}
-
-	if (strcasecmp(status, "Error occurred") == 0) {
-		xml_node_t *err;
-		char *code = NULL;
-		err = get_node(ctx->xml, node, "sppError");
-		if (err)
-			code = xml_node_get_attr_value(ctx->xml, err,
-						       "errorCode");
-		wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
-			   code ? code : "N/A");
-		xml_node_get_attr_value_free(ctx->xml, code);
-		goto out;
-	}
-
-	wpa_printf(MSG_INFO,
-		   "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
-		   status);
-out:
-	xml_node_get_attr_value_free(ctx->xml, status);
-	xml_node_get_attr_value_free(ctx->xml, session_id);
-	xml_node_free(ctx->xml, node);
-}
-
-
-static int spp_post_dev_data(struct hs20_osu_client *ctx,
-			     enum spp_post_dev_data_use use,
-			     const char *reason,
-			     const char *pps_fname, xml_node_t *pps)
-{
-	xml_node_t *payload;
-	xml_node_t *ret_node;
-
-	payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
-	if (payload == NULL)
-		return -1;
-
-	ret_node = soap_send_receive(ctx->http, payload);
-	if (!ret_node) {
-		const char *err = http_get_err(ctx->http);
-		if (err) {
-			wpa_printf(MSG_INFO, "HTTP error: %s", err);
-			write_result(ctx, "HTTP error: %s", err);
-		} else {
-			write_summary(ctx, "Failed to send SOAP message");
-		}
-		return -1;
-	}
-
-	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
-		wpa_printf(MSG_INFO, "SPP validation failed");
-		xml_node_free(ctx->xml, ret_node);
-		return -1;
-	}
-
-	process_spp_post_dev_data_response(ctx, use, ret_node,
-					   pps_fname, pps);
-	return 0;
-}
-
-
-void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
-		 const char *pps_fname,
-		 const char *client_cert, const char *client_key,
-		 const char *cred_username, const char *cred_password,
-		 xml_node_t *pps)
-{
-	wpa_printf(MSG_INFO, "SPP subscription remediation");
-	write_summary(ctx, "SPP subscription remediation");
-
-	os_free(ctx->server_url);
-	ctx->server_url = os_strdup(address);
-
-	if (soap_init_client(ctx->http, address, ctx->ca_fname,
-			     cred_username, cred_password, client_cert,
-			     client_key) == 0) {
-		spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
-				  "Subscription remediation", pps_fname, pps);
-	}
-}
-
-
-static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
-					const char *pps_fname)
-{
-	wpa_printf(MSG_INFO, "Policy update completed");
-
-	/*
-	 * Update wpa_supplicant credentials and reconnect using updated
-	 * information.
-	 */
-	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
-	cmd_set_pps(ctx, pps_fname);
-
-	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
-	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
-		wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
-}
-
-
-static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
-					 xml_node_t *node)
-{
-	char *status, *session_id;
-
-	debug_dump_node(ctx, "sppExchangeComplete", node);
-
-	status = get_spp_attr_value(ctx->xml, node, "sppStatus");
-	if (status == NULL) {
-		wpa_printf(MSG_INFO, "No sppStatus attribute");
-		return -1;
-	}
-	write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
-		      status);
-
-	session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
-	if (session_id == NULL) {
-		wpa_printf(MSG_INFO, "No sessionID attribute");
-		xml_node_get_attr_value_free(ctx->xml, status);
-		return -1;
-	}
-
-	wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s'  sessionID: '%s'",
-		   status, session_id);
-	xml_node_get_attr_value_free(ctx->xml, session_id);
-
-	if (strcasecmp(status, "Exchange complete, release TLS connection") ==
-	    0) {
-		xml_node_get_attr_value_free(ctx->xml, status);
-		return 0;
-	}
-
-	wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
-	write_summary(ctx, "Unexpected sppStatus '%s'", status);
-	xml_node_get_attr_value_free(ctx->xml, status);
-	return -1;
-}
-
-
-static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
-					      const char *session_id,
-					      const char *spp_status,
-					      const char *error_code)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *node;
-
-	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
-					"sppUpdateResponse");
-	if (spp_node == NULL)
-		return NULL;
-
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
-
-	if (error_code) {
-		node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
-		if (node)
-			xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
-					  error_code);
-	}
-
-	return spp_node;
-}
-
-
-static int hs20_spp_update_response(struct hs20_osu_client *ctx,
-				    const char *session_id,
-				    const char *spp_status,
-				    const char *error_code)
-{
-	xml_node_t *node, *ret_node;
-	int ret;
-
-	write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
-		      spp_status, error_code);
-	node = build_spp_update_response(ctx, session_id, spp_status,
-					 error_code);
-	if (node == NULL)
-		return -1;
-	ret_node = soap_send_receive(ctx->http, node);
-	if (!ret_node) {
-		if (soap_reinit_client(ctx->http) < 0)
-			return -1;
-		wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
-		node = build_spp_update_response(ctx, session_id, spp_status,
-						 error_code);
-		if (node == NULL)
-			return -1;
-		ret_node = soap_send_receive(ctx->http, node);
-		if (ret_node == NULL)
-			return -1;
-		wpa_printf(MSG_INFO, "Continue with new connection");
-	}
-
-	if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
-		wpa_printf(MSG_INFO, "SPP validation failed");
-		xml_node_free(ctx->xml, ret_node);
-		return -1;
-	}
-
-	ret = process_spp_exchange_complete(ctx, ret_node);
-	xml_node_free(ctx->xml, ret_node);
-	return ret;
-}
-
-
-void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
-		 const char *pps_fname,
-		 const char *client_cert, const char *client_key,
-		 const char *cred_username, const char *cred_password,
-		 xml_node_t *pps)
-{
-	wpa_printf(MSG_INFO, "SPP policy update");
-	write_summary(ctx, "SPP policy update");
-
-	os_free(ctx->server_url);
-	ctx->server_url = os_strdup(address);
-
-	if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
-			     cred_password, client_cert, client_key) == 0) {
-		spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
-				  pps_fname, pps);
-	}
-}
-
-
-int cmd_prov(struct hs20_osu_client *ctx, const char *url)
-{
-	unlink("Cert/est_cert.der");
-	unlink("Cert/est_cert.pem");
-
-	if (url == NULL) {
-		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
-		return -1;
-	}
-
-	wpa_printf(MSG_INFO,
-		   "Credential provisioning requested - URL: %s ca_fname: %s",
-		   url, ctx->ca_fname ? ctx->ca_fname : "N/A");
-
-	os_free(ctx->server_url);
-	ctx->server_url = os_strdup(url);
-
-	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
-			     NULL) < 0)
-		return -1;
-	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
-			  "Subscription registration", NULL, NULL);
-
-	return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
-{
-	if (url == NULL) {
-		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
-		return -1;
-	}
-
-	wpa_printf(MSG_INFO, "SIM provisioning requested");
-
-	os_free(ctx->server_url);
-	ctx->server_url = os_strdup(url);
-
-	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
-
-	if (wait_ip_addr(ctx->ifname, 15) < 0) {
-		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
-	}
-
-	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
-			     NULL) < 0)
-		return -1;
-	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
-			  "Subscription provisioning", NULL, NULL);
-
-	return ctx->pps_cred_set ? 0 : -1;
-}
diff --git a/hs20/server/.gitignore b/hs20/server/.gitignore
deleted file mode 100644
index fecb096..0000000
--- a/hs20/server/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-hs20_spp_server
diff --git a/hs20/server/Makefile b/hs20/server/Makefile
deleted file mode 100644
index 0cab6d6..0000000
--- a/hs20/server/Makefile
+++ /dev/null
@@ -1,42 +0,0 @@
-ALL=hs20_spp_server
-
-include ../../src/build.rules
-
-CFLAGS += -I../../src
-CFLAGS += -I../../src/utils
-CFLAGS += -I../../src/crypto
-
-LIBS += -lsqlite3
-
-# Using glibc < 2.17 requires -lrt for clock_gettime()
-LIBS += -lrt
-
-ifndef CONFIG_NO_GITVER
-# Add VERSION_STR postfix for builds from a git repository
-ifeq ($(wildcard ../../.git),../../.git)
-GITVER := $(shell git describe --dirty=+)
-ifneq ($(GITVER),)
-CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
-endif
-endif
-endif
-
-OBJS=spp_server.o
-OBJS += hs20_spp_server.o
-OBJS += ../../src/utils/xml-utils.o
-OBJS += ../../src/utils/base64.o
-OBJS += ../../src/utils/common.o
-OBJS += ../../src/utils/os_unix.o
-OBJS += ../../src/utils/wpa_debug.o
-OBJS += ../../src/crypto/md5-internal.o
-CFLAGS += $(shell xml2-config --cflags)
-LIBS += $(shell xml2-config --libs)
-OBJS += ../../src/utils/xml_libxml2.o
-
-_OBJS_VAR := OBJS
-include ../../src/objs.mk
-hs20_spp_server: $(OBJS)
-	$(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
-
-clean: common-clean
-	rm -f core *~
diff --git a/hs20/server/ca/clean.sh b/hs20/server/ca/clean.sh
deleted file mode 100755
index c72dcbd..0000000
--- a/hs20/server/ca/clean.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-for i in server-client server server-revoked user ocsp; do
-    rm -f $i.csr $i.key $i.pem
-done
-
-rm -f openssl.cnf.tmp
-if [ -d demoCA ]; then
-    rm -r demoCA
-fi
-rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
-rm -f my-openssl.cnf my-openssl-root.cnf
-#rm -r rootCA
diff --git a/hs20/server/ca/est-csrattrs.cnf b/hs20/server/ca/est-csrattrs.cnf
deleted file mode 100644
index b50ea00..0000000
--- a/hs20/server/ca/est-csrattrs.cnf
+++ /dev/null
@@ -1,17 +0,0 @@
-asn1 = SEQUENCE:attrs
-
-[attrs]
-#oid1 = OID:challengePassword
-attr1 = SEQUENCE:extreq
-oid2 = OID:sha256WithRSAEncryption
-
-[extreq]
-oid = OID:extensionRequest
-vals = SET:extreqvals
-
-[extreqvals]
-
-oid1 = OID:macAddress
-#oid2 = OID:imei
-#oid3 = OID:meid
-#oid4 = OID:DevId
diff --git a/hs20/server/ca/est-csrattrs.sh b/hs20/server/ca/est-csrattrs.sh
deleted file mode 100644
index 0b73a04..0000000
--- a/hs20/server/ca/est-csrattrs.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-openssl asn1parse -genconf est-csrattrs.cnf -out est-csrattrs.der -oid hs20.oid
-base64 est-csrattrs.der > est-attrs.b64
diff --git a/hs20/server/ca/hs20.oid b/hs20/server/ca/hs20.oid
deleted file mode 100644
index a829ff2..0000000
--- a/hs20/server/ca/hs20.oid
+++ /dev/null
@@ -1,7 +0,0 @@
-1.3.6.1.1.1.1.22 macAddress
-1.2.840.113549.1.9.14 extensionRequest
-1.3.6.1.4.1.40808.1.1.1 id-wfa-hotspot-friendlyName
-1.3.6.1.4.1.40808.1.1.2 id-kp-HS2.0Auth
-1.3.6.1.4.1.40808.1.1.3 imei
-1.3.6.1.4.1.40808.1.1.4 meid
-1.3.6.1.4.1.40808.1.1.5 DevId
diff --git a/hs20/server/ca/ocsp-req.sh b/hs20/server/ca/ocsp-req.sh
deleted file mode 100644
index 931a206..0000000
--- a/hs20/server/ca/ocsp-req.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-for i in *.pem; do
-    echo "===[ $i ]==================="
-    openssl ocsp -text -CAfile ca.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
-
-#    openssl ocsp -text -CAfile rootCA/cacert.pem -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
-
-#    openssl ocsp -text -CAfile rootCA/cacert.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
-#    openssl ocsp -text -CAfile rootCA/cacert.pem -VAfile ca.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
-done
diff --git a/hs20/server/ca/ocsp-responder-ica.sh b/hs20/server/ca/ocsp-responder-ica.sh
deleted file mode 100644
index 116c6e1..0000000
--- a/hs20/server/ca/ocsp-responder-ica.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner demoCA/cacert.pem -rkey demoCA/private/cakey-plain.pem -CA demoCA/cacert.pem -resp_no_certs -text
diff --git a/hs20/server/ca/ocsp-responder.sh b/hs20/server/ca/ocsp-responder.sh
deleted file mode 100644
index 620947d..0000000
--- a/hs20/server/ca/ocsp-responder.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner ocsp.pem -rkey ocsp.key -CA demoCA/cacert.pem -text -ignore_err
diff --git a/hs20/server/ca/ocsp-update-cache.sh b/hs20/server/ca/ocsp-update-cache.sh
deleted file mode 100644
index f2b2325..0000000
--- a/hs20/server/ca/ocsp-update-cache.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-# NOTE: You may need to replace 'localhost' with your OCSP server hostname.
-openssl ocsp \
-	-no_nonce \
-	-CAfile ca.pem \
-	-verify_other demoCA/cacert.pem \
-	-issuer demoCA/cacert.pem \
-	-cert server.pem \
-	-url http://localhost:8888/ \
-	-respout ocsp-server-cache.der
diff --git a/hs20/server/ca/openssl-root.cnf b/hs20/server/ca/openssl-root.cnf
deleted file mode 100644
index 5bc50be..0000000
--- a/hs20/server/ca/openssl-root.cnf
+++ /dev/null
@@ -1,125 +0,0 @@
-# OpenSSL configuration file for Hotspot 2.0 PKI (Root CA)
-
-HOME			= .
-RANDFILE		= $ENV::HOME/.rnd
-oid_section		= new_oids
-
-[ new_oids ]
-
-#logotypeoid=1.3.6.1.5.5.7.1.12
-
-####################################################################
-[ ca ]
-default_ca	= CA_default		# The default ca section
-
-####################################################################
-[ CA_default ]
-
-dir		= ./rootCA		# Where everything is kept
-certs		= $dir/certs		# Where the issued certs are kept
-crl_dir		= $dir/crl		# Where the issued crl are kept
-database	= $dir/index.txt	# database index file.
-#unique_subject	= no			# Set to 'no' to allow creation of
-					# several certificates with same subject
-new_certs_dir	= $dir/newcerts		# default place for new certs.
-
-certificate	= $dir/cacert.pem 	# The CA certificate
-serial		= $dir/serial 		# The current serial number
-crlnumber	= $dir/crlnumber	# the current crl number
-					# must be commented out to leave a V1 CRL
-crl		= $dir/crl.pem 		# The current CRL
-private_key	= $dir/private/cakey.pem# The private key
-RANDFILE	= $dir/private/.rand	# private random number file
-
-x509_extensions	= usr_cert		# The extentions to add to the cert
-
-name_opt 	= ca_default		# Subject Name options
-cert_opt 	= ca_default		# Certificate field options
-
-default_days	= 365			# how long to certify for
-default_crl_days= 30			# how long before next CRL
-default_md	= default		# use public key default MD
-preserve	= no			# keep passed DN ordering
-
-policy		= policy_match
-
-# For the CA policy
-[ policy_match ]
-countryName		= match
-stateOrProvinceName	= optional
-organizationName	= match
-organizationalUnitName	= optional
-commonName		= supplied
-emailAddress		= optional
-
-[ policy_anything ]
-countryName		= optional
-stateOrProvinceName	= optional
-localityName		= optional
-organizationName	= optional
-organizationalUnitName	= optional
-commonName		= supplied
-emailAddress		= optional
-
-####################################################################
-[ req ]
-default_bits		= 2048
-default_keyfile 	= privkey.pem
-distinguished_name	= req_distinguished_name
-attributes		= req_attributes
-x509_extensions	= v3_ca	# The extentions to add to the self signed cert
-
-input_password = @PASSWORD@
-output_password = @PASSWORD@
-
-string_mask = utf8only
-
-[ req_distinguished_name ]
-countryName			= Country Name (2 letter code)
-countryName_default		= US
-countryName_min			= 2
-countryName_max			= 2
-
-localityName			= Locality Name (eg, city)
-localityName_default		= Tuusula
-
-0.organizationName		= Organization Name (eg, company)
-0.organizationName_default	= WFA Hotspot 2.0
-
-##organizationalUnitName		= Organizational Unit Name (eg, section)
-#organizationalUnitName_default	=
-#@OU@
-
-commonName			= Common Name (e.g. server FQDN or YOUR name)
-#@CN@
-commonName_max			= 64
-
-emailAddress			= Email Address
-emailAddress_max		= 64
-
-[ req_attributes ]
-
-[ v3_req ]
-
-# Extensions to add to a certificate request
-basicConstraints = CA:FALSE
-keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-subjectAltName=DNS:example.com,DNS:another.example.com
-
-[ v3_ca ]
-
-# Hotspot 2.0 PKI requirements
-subjectKeyIdentifier=hash
-basicConstraints = critical,CA:true
-keyUsage = critical, cRLSign, keyCertSign
-
-[ crl_ext ]
-
-# issuerAltName=issuer:copy
-authorityKeyIdentifier=keyid:always
-
-[ v3_OCSP ]
-
-basicConstraints = CA:FALSE
-keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-extendedKeyUsage = OCSPSigning
diff --git a/hs20/server/ca/openssl.cnf b/hs20/server/ca/openssl.cnf
deleted file mode 100644
index 6141013..0000000
--- a/hs20/server/ca/openssl.cnf
+++ /dev/null
@@ -1,200 +0,0 @@
-# OpenSSL configuration file for Hotspot 2.0 PKI (Intermediate CA)
-
-HOME			= .
-RANDFILE		= $ENV::HOME/.rnd
-oid_section		= new_oids
-
-[ new_oids ]
-
-#logotypeoid=1.3.6.1.5.5.7.1.12
-
-####################################################################
-[ ca ]
-default_ca	= CA_default		# The default ca section
-
-####################################################################
-[ CA_default ]
-
-dir		= ./demoCA		# Where everything is kept
-certs		= $dir/certs		# Where the issued certs are kept
-crl_dir		= $dir/crl		# Where the issued crl are kept
-database	= $dir/index.txt	# database index file.
-#unique_subject	= no			# Set to 'no' to allow creation of
-					# several certificates with same subject
-new_certs_dir	= $dir/newcerts		# default place for new certs.
-
-certificate	= $dir/cacert.pem 	# The CA certificate
-serial		= $dir/serial 		# The current serial number
-crlnumber	= $dir/crlnumber	# the current crl number
-					# must be commented out to leave a V1 CRL
-crl		= $dir/crl.pem 		# The current CRL
-private_key	= $dir/private/cakey.pem# The private key
-RANDFILE	= $dir/private/.rand	# private random number file
-
-x509_extensions	= ext_client		# The extentions to add to the cert
-
-name_opt 	= ca_default		# Subject Name options
-cert_opt 	= ca_default		# Certificate field options
-
-# Extension copying option: use with caution.
-copy_extensions = copy
-
-default_days	= 365			# how long to certify for
-default_crl_days= 30			# how long before next CRL
-default_md	= default		# use public key default MD
-preserve	= no			# keep passed DN ordering
-
-policy		= policy_match
-
-# For the CA policy
-[ policy_match ]
-countryName		= supplied
-stateOrProvinceName	= optional
-organizationName	= supplied
-organizationalUnitName	= optional
-commonName		= supplied
-emailAddress		= optional
-
-[ policy_osu_server ]
-countryName		= match
-stateOrProvinceName	= optional
-organizationName	= match
-organizationalUnitName	= supplied
-commonName		= supplied
-emailAddress		= optional
-
-[ policy_anything ]
-countryName		= optional
-stateOrProvinceName	= optional
-localityName		= optional
-organizationName	= optional
-organizationalUnitName	= optional
-commonName		= supplied
-emailAddress		= optional
-
-####################################################################
-[ req ]
-default_bits		= 2048
-default_keyfile 	= privkey.pem
-distinguished_name	= req_distinguished_name
-attributes		= req_attributes
-x509_extensions	= v3_ca	# The extentions to add to the self signed cert
-
-input_password = @PASSWORD@
-output_password = @PASSWORD@
-
-string_mask = utf8only
-
-[ req_distinguished_name ]
-countryName			= Country Name (2 letter code)
-countryName_default		= FI
-countryName_min			= 2
-countryName_max			= 2
-
-localityName			= Locality Name (eg, city)
-localityName_default		= Tuusula
-
-0.organizationName		= Organization Name (eg, company)
-0.organizationName_default	= @DOMAIN@
-
-##organizationalUnitName		= Organizational Unit Name (eg, section)
-#organizationalUnitName_default	=
-#@OU@
-
-commonName			= Common Name (e.g. server FQDN or YOUR name)
-#@CN@
-commonName_max			= 64
-
-emailAddress			= Email Address
-emailAddress_max		= 64
-
-[ req_attributes ]
-
-[ v3_ca ]
-
-# Hotspot 2.0 PKI requirements
-subjectKeyIdentifier=hash
-authorityKeyIdentifier=keyid:always,issuer
-basicConstraints = critical, CA:true, pathlen:0
-keyUsage = critical, cRLSign, keyCertSign
-authorityInfoAccess = OCSP;URI:@OCSP_URI@
-# For SP intermediate CA
-#subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
-#nameConstraints=permitted;DNS:.@DOMAIN@
-#1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
-
-[ v3_osu_server ]
-
-basicConstraints = critical, CA:true, pathlen:0
-keyUsage = critical, keyEncipherment
-#@ALTNAME@
-
-#logotypeoid=ASN1:SEQUENCE:LogotypeExtn
-1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
-[LogotypeExtn]
-communityLogos=EXP:0,SEQUENCE:LogotypeInfo
-[LogotypeInfo]
-# note: implicit tag converted to explicit for CHOICE
-direct=EXP:0,SEQUENCE:LogotypeData
-[LogotypeData]
-image=SEQUENCE:LogotypeImage
-[LogotypeImage]
-imageDetails=SEQUENCE:LogotypeDetails
-imageInfo=SEQUENCE:LogotypeImageInfo
-[LogotypeDetails]
-mediaType=IA5STRING:image/png
-logotypeHash=SEQUENCE:HashAlgAndValues
-logotypeURI=SEQUENCE:URI
-[HashAlgAndValues]
-value1=SEQUENCE:HashAlgAndValueSHA256
-#value2=SEQUENCE:HashAlgAndValueSHA1
-[HashAlgAndValueSHA256]
-hashAlg=SEQUENCE:sha256_alg
-hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH256@
-[HashAlgAndValueSHA1]
-hashAlg=SEQUENCE:sha1_alg
-hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH1@
-[sha256_alg]
-algorithm=OID:sha256
-[sha1_alg]
-algorithm=OID:sha1
-[URI]
-uri=IA5STRING:@LOGO_URI@
-[LogotypeImageInfo]
-# default value color(1), component optional
-#type=IMP:0,INTEGER:1
-fileSize=INTEGER:7549
-xSize=INTEGER:128
-ySize=INTEGER:80
-language=IMP:4,IA5STRING:zxx
-
-[ crl_ext ]
-
-# issuerAltName=issuer:copy
-authorityKeyIdentifier=keyid:always
-
-[ v3_OCSP ]
-
-basicConstraints = CA:FALSE
-keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-extendedKeyUsage = OCSPSigning
-
-[ ext_client ]
-
-basicConstraints=CA:FALSE
-subjectKeyIdentifier=hash
-authorityKeyIdentifier=keyid,issuer
-authorityInfoAccess = OCSP;URI:@OCSP_URI@
-#@ALTNAME@
-extendedKeyUsage = clientAuth
-
-[ ext_server ]
-
-# Hotspot 2.0 PKI requirements
-basicConstraints=critical, CA:FALSE
-subjectKeyIdentifier=hash
-authorityKeyIdentifier=keyid,issuer
-authorityInfoAccess = OCSP;URI:@OCSP_URI@
-#@ALTNAME@
-extendedKeyUsage = critical, serverAuth
-keyUsage = critical, keyEncipherment
diff --git a/hs20/server/ca/setup.sh b/hs20/server/ca/setup.sh
deleted file mode 100755
index 78abccc..0000000
--- a/hs20/server/ca/setup.sh
+++ /dev/null
@@ -1,209 +0,0 @@
-#!/bin/sh
-
-if [ -z "$OPENSSL" ]; then
-    OPENSSL=openssl
-fi
-export OPENSSL_CONF=$PWD/openssl.cnf
-PASS=whatever
-if [ -z "$DOMAIN" ]; then
-    DOMAIN=w1.fi
-fi
-COMPANY=w1.fi
-OPER_ENG="engw1.fi TESTING USE"
-OPER_FI="finw1.fi TESTIKÄYTTÖ"
-CNR="Hotspot 2.0 Trust Root CA - 99"
-CNO="ocsp.$DOMAIN"
-CNV="osu-revoked.$DOMAIN"
-CNOC="osu-client.$DOMAIN"
-OSU_SERVER_HOSTNAME="osu.$DOMAIN"
-DEBUG=0
-OCSP_URI="http://$CNO:8888/"
-LOGO_URI="http://osu.w1.fi/w1fi_logo.png"
-LOGO_HASH256="4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d"
-LOGO_HASH1="5e1d5085676eede6b02da14d31c523ec20ffba0b"
-
-# Command line overrides
-USAGE=$( cat <<EOF
-Usage:\n
-# -c:  Company name, used to generate Subject name CN for Intermediate CA\n
-# -C:  Subject name CN of the Root CA ($CNR)\n
-# -D:  Enable debugging (set -x, etc)\n
-# -g:  Logo sha1 hash ($LOGO_HASH1)\n
-# -G:  Logo sha256 hash ($LOGO_HASH256)\n
-# -h:  Show this help message\n
-# -l:  Logo URI ($LOGO_URI)\n
-# -m:  Domain ($DOMAIN)\n
-# -o:  Subject name CN for OSU-Client Server ($CNOC)\n
-# -O:  Subject name CN for OCSP Server ($CNO)\n
-# -p:  passphrase for private keys ($PASS)\n
-# -r:  Operator-english ($OPER_ENG)\n
-# -R:  Operator-finish ($OPER_FI)\n
-# -S:  OSU Server name ($OSU_SERVER_HOSTNAME)\n
-# -u:  OCSP-URI ($OCSP_URI)\n
-# -V:  Subject name CN for OSU-Revoked Server ($CNV)\n
-EOF
-)
-
-while getopts "c:C:Dg:G:l:m:o:O:p:r:R:S:u:V:h" flag
-  do
-  case $flag in
-      c) COMPANY=$OPTARG;;
-      C) CNR=$OPTARG;;
-      D) DEBUG=1;;
-      g) LOGO_HASH1=$OPTARG;;
-      G) LOGO_HASH256=$OPTARG;;
-      h) echo -e $USAGE; exit 0;;
-      l) LOGO_URI=$OPTARG;;
-      m) DOMAIN=$OPTARG;;
-      o) CNOC=$OPTARG;;
-      O) CNO=$OPTARG;;
-      p) PASS=$OPTARG;;
-      r) OPER_ENG=$OPTARG;;
-      R) OPER_FI=$OPTARG;;
-      S) OSU_SERVER_HOSTNAME=$OPTARG;;
-      u) OCSP_URI=$OPTARG;;
-      V) CNV=$OPTARG;;
-      *) echo "Unknown flag: $flag"; echo -e $USAGE; exit 1;;
-  esac
-done
-
-fail()
-{
-    echo "$*"
-    exit 1
-}
-
-echo
-echo "---[ Root CA ]----------------------------------------------------------"
-echo
-
-if [ $DEBUG = 1 ]
-then
-    set -x
-fi
-
-# Set the passphrase and some other common config accordingly.
-cat openssl-root.cnf | sed "s/@PASSWORD@/$PASS/" \
- > my-openssl-root.cnf
-
-cat openssl.cnf | sed "s/@PASSWORD@/$PASS/" |
-sed "s,@OCSP_URI@,$OCSP_URI," |
-sed "s,@LOGO_URI@,$LOGO_URI," |
-sed "s,@LOGO_HASH1@,$LOGO_HASH1," |
-sed "s,@LOGO_HASH256@,$LOGO_HASH256," |
-sed "s/@DOMAIN@/$DOMAIN/" \
- > my-openssl.cnf
-
-
-cat my-openssl-root.cnf | sed "s/#@CN@/commonName_default = $CNR/" > openssl.cnf.tmp
-mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
-touch rootCA/index.txt
-if [ -e rootCA/private/cakey.pem ]; then
-    echo " * Use existing Root CA"
-else
-    echo " * Generate Root CA private key"
-    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
-    echo " * Sign Root CA certificate"
-    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
-    $OPENSSL x509 -in rootCA/cacert.pem -out rootCA/cacert.der -outform DER || fail "Failed to create rootCA DER"
-    sha256sum rootCA/cacert.der > rootCA/cacert.fingerprint || fail "Failed to create rootCA fingerprint"
-fi
-if [ ! -e rootCA/crlnumber ]; then
-    echo 00 > rootCA/crlnumber
-fi
-
-echo
-echo "---[ Intermediate CA ]--------------------------------------------------"
-echo
-
-cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $COMPANY Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
-mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
-touch demoCA/index.txt
-if [ -e demoCA/private/cakey.pem ]; then
-    echo " * Use existing Intermediate CA"
-else
-    echo " * Generate Intermediate CA private key"
-    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
-    echo " * Sign Intermediate CA certificate"
-    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
-    # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
-    openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
-    $OPENSSL x509 -in demoCA/cacert.pem -out demoCA/cacert.der -outform DER || fail "Failed to create demoCA DER."
-    sha256sum demoCA/cacert.der > demoCA/cacert.fingerprint || fail "Failed to create demoCA fingerprint"
-fi
-if [ ! -e demoCA/crlnumber ]; then
-    echo 00 > demoCA/crlnumber
-fi
-
-echo
-echo "OCSP responder"
-echo
-
-cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNO/" > openssl.cnf.tmp
-$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP || fail "Could not generate ocsp.pem"
-
-echo
-echo "---[ Server - to be revoked ] ------------------------------------------"
-echo
-
-cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNV/" > openssl.cnf.tmp
-$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
-$OPENSSL ca -revoke server-revoked.pem -key $PASS
-
-echo
-echo "---[ Server - with client ext key use ] ---------------------------------"
-echo "---[ Only used for negative-testing for OSU-client implementation ] -----"
-echo
-
-cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNOC/" > openssl.cnf.tmp
-$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key || fail "Could not create server-client.key"
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create server-client.pem"
-
-echo
-echo "---[ User ]-------------------------------------------------------------"
-echo
-
-cat my-openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
-$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key || fail "Could not create user.key"
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create user.pem"
-
-echo
-echo "---[ Server ]-----------------------------------------------------------"
-echo
-
-ALT="DNS:$OSU_SERVER_HOSTNAME"
-ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_ENG"
-ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_FI"
-
-cat my-openssl.cnf |
-	sed "s/#@CN@/commonName_default = $OSU_SERVER_HOSTNAME/" |
-	sed "s/^##organizationalUnitName/organizationalUnitName/" |
-	sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
-	sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
-	> openssl.cnf.tmp
-echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
-$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
-
-#dump logotype details for debugging
-$OPENSSL x509 -in server.pem -out server.der -outform DER
-openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
-openssl asn1parse -in logo.der -inform DER > logo.asn1
-
-
-echo
-echo "---[ CRL ]---------------------------------------------------------------"
-echo
-
-$OPENSSL ca -config $PWD/my-openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
-
-echo
-echo "---[ Verify ]------------------------------------------------------------"
-echo
-
-$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
-$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
-
-cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
diff --git a/hs20/server/ca/w1fi_logo.png b/hs20/server/ca/w1fi_logo.png
deleted file mode 100644
index ac7c259..0000000
--- a/hs20/server/ca/w1fi_logo.png
+++ /dev/null
Binary files differ
diff --git a/hs20/server/hs20-osu-server.txt b/hs20/server/hs20-osu-server.txt
deleted file mode 100644
index 22478ad..0000000
--- a/hs20/server/hs20-osu-server.txt
+++ /dev/null
@@ -1,262 +0,0 @@
-Hotspot 2.0 OSU server
-======================
-
-The information in this document is based on the assumption that Ubuntu
-16.04 server (64-bit) distribution is used and the web server is
-Apache2. Neither of these are requirements for the installation, but if
-other combinations are used, the package names and configuration
-parameters may need to be adjusted.
-
-NOTE: This implementation and the example configuration here is meant
-only for testing purposes in a lab environment. This design is not
-secure to be installed in a publicly available Internet server without
-considerable amount of modification and review for security issues.
-
-
-Build dependencies
-------------------
-
-Ubuntu 16.04 server
-- default installation
-- upgraded to latest package versions
-  sudo apt-get update
-  sudo apt-get upgrade
-
-Packages needed for running the service:
-  sudo apt-get install sqlite3
-  sudo apt-get install apache2
-  sudo apt-get install php-sqlite3 php-xml libapache2-mod-php
-
-Additional packages needed for building the components:
-  sudo apt-get install build-essential
-  sudo apt-get install libsqlite3-dev
-  sudo apt-get install libssl-dev
-  sudo apt-get install libxml2-dev
-
-
-Installation location
----------------------
-
-Select a location for the installation root directory. The example here
-assumes /home/user/hs20-server to be used, but this can be changed by
-editing couple of files as indicated below.
-
-sudo mkdir -p /home/user/hs20-server
-sudo chown $USER /home/user/hs20-server
-mkdir -p /home/user/hs20-server/spp
-mkdir -p /home/user/hs20-server/AS
-
-
-Build
------
-
-# hostapd as RADIUS server
-cd hostapd
-
-#example build configuration
-cat > .config <<EOF
-CONFIG_DRIVER_NONE=y
-CONFIG_PKCS12=y
-CONFIG_RADIUS_SERVER=y
-CONFIG_EAP=y
-CONFIG_EAP_TLS=y
-CONFIG_EAP_MSCHAPV2=y
-CONFIG_EAP_PEAP=y
-CONFIG_EAP_GTC=y
-CONFIG_EAP_TTLS=y
-CONFIG_EAP_SIM=y
-CONFIG_EAP_AKA=y
-CONFIG_EAP_AKA_PRIME=y
-CONFIG_SQLITE=y
-CONFIG_HS20=y
-EOF
-
-make hostapd hlr_auc_gw
-cp hostapd hlr_auc_gw /home/user/hs20-server/AS
-
-# build hs20_spp_server
-cd ../hs20/server
-make clean
-make
-cp hs20_spp_server /home/user/hs20-server/spp
-# prepare database (web server user/group needs to have write access)
-mkdir -p /home/user/hs20-server/AS/DB
-sudo chgrp www-data /home/user/hs20-server/AS/DB
-sudo chmod g+w /home/user/hs20-server/AS/DB
-sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql.txt
-sudo chgrp www-data /home/user/hs20-server/AS/DB/eap_user.db
-sudo chmod g+w /home/user/hs20-server/AS/DB/eap_user.db
-# add example configuration (note: need to update URLs to match the system)
-sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql-example.txt
-
-# copy PHP scripts
-# Modify config.php if different installation directory is used.
-# Modify PHP scripts to get the desired behavior for user interaction (or use
-# the examples as-is for initial testing).
-cp -r www /home/user/hs20-server
-
-# Create /home/user/hs20-server/terms-and-conditions file (HTML segment to be
-# inserted within the BODY section of the page).
-cat > /home/user/hs20-server/terms-and-conditions <<EOF
-<P>Terms and conditions..</P>
-EOF
-
-# Build local keys and certs
-cd ca
-# Display help options.
-./setup.sh -h
-
-# Remove old keys, fill in appropriate values, and generate your keys.
-# For instance:
-./clean.sh
-rm -fr rootCA"
-old_hostname=myserver.local
-./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" \
-   -o $old_hostname-osu-client \
-   -O $old_hostname-oscp -p lanforge -S $old_hostname \
-   -V $old_hostname-osu-revoked \
-   -m local -u http://$old_hostname:8888/
-
-# Configure subscription policies
-mkdir -p /home/user/hs20-server/spp/policy
-cat > /home/user/hs20-server/spp/policy/default.xml <<EOF
-<Policy>
-	<PolicyUpdate>
-		<UpdateInterval>30</UpdateInterval>
-		<UpdateMethod>ClientInitiated</UpdateMethod>
-		<Restriction>Unrestricted</Restriction>
-		<URI>https://policy-server.osu.example.com/hs20/spp.php</URI>
-	</PolicyUpdate>
-</Policy>
-EOF
-
-
-# Install Hotspot 2.0 SPP and OMA DM XML schema/DTD files
-
-# XML schema for SPP
-# Copy the latest XML schema into /home/user/hs20-server/spp/spp.xsd
-
-# OMA DM Device Description Framework DTD
-# Copy into /home/user/hs20-server/spp/dm_ddf-v1_2.dtd
-# http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd
-
-
-# Configure RADIUS authentication service
-# Note: Change the URL to match the setup
-# Note: Install AAA server key/certificate and root CA in Key directory
-
-cat > /home/user/hs20-server/AS/as-sql.conf <<EOF
-driver=none
-radius_server_clients=as.radius_clients
-eap_server=1
-eap_user_file=sqlite:DB/eap_user.db
-ca_cert=Key/ca.pem
-server_cert=Key/server.pem
-private_key=Key/server.key
-private_key_passwd=passphrase
-eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=eap_sim.db
-subscr_remediation_url=https://subscription-server.osu.example.com/hs20/spp.php
-EOF
-
-# Set RADIUS passphrase for the APs
-# Note: Modify to match the setup
-cat > /home/user/hs20-server/AS/as.radius_clients <<EOF
-0.0.0.0/0	radius
-EOF
-
-
-Start RADIUS authentication server
-----------------------------------
-
-cd /home/user/hs20-server/AS
-./hostapd -B as-sql.conf
-
-
-OSEN RADIUS server configuration notes
-
-The OSEN RADIUS server config file should have the 'ocsp_stapling_response'
-configuration in it. For example:
-
-# hostapd-radius config for the radius used by the OSEN AP
-interface=eth0#0
-driver=none
-logger_syslog=-1
-logger_syslog_level=2
-logger_stdout=-1
-logger_stdout_level=2
-ctrl_interface=/var/run/hostapd
-ctrl_interface_group=0
-eap_server=1
-eap_user_file=/home/user/hs20-server/AS/hostapd-osen.eap_user
-server_id=ben-ota-2-osen
-radius_server_auth_port=1811
-radius_server_clients=/home/user/hs20-server/AS/hostap.radius_clients
-
-ca_cert=/home/user/hs20-server/ca/ca.pem
-server_cert=/home/user/hs20-server/ca/server.pem
-private_key=/home/user/hs20-server/ca/server.key
-private_key_passwd=whatever
-
-ocsp_stapling_response=/home/user/hs20-server/ca/ocsp-server-cache.der
-
-The /home/user/hs20-server/AS/hostapd-osen.eap_user file should look
-similar to this, and should coorelate with the osu_nai entry in
-the non-OSEN VAP config file.  For instance:
-
-# cat hostapd-osen.eap_user
-# For OSEN authentication (Hotspot 2.0 Release 2)
-"osen@w1.fi"      WFA-UNAUTH-TLS
-
-
-# Run OCSP server:
-cd /home/user/hs20-server/ca
-./ocsp-responder.sh&
-
-# Update cache (This should be run periodically)
-./ocsp-update-cache.sh
-
-
-Configure web server
---------------------
-
-Edit /etc/apache2/sites-available/default-ssl
-
-Add following block just before "SSL Engine Switch" line":
-
-        Alias /hs20/ "/home/user/hs20-server/www/"
-        <Directory "/home/user/hs20-server/www/">
-                Options Indexes MultiViews FollowSymLinks
-                AllowOverride None
-		Require all granted
-		SSLOptions +StdEnvVars
-        </Directory>
-
-Update SSL configuration to use the OSU server certificate/key.
-They keys and certs are called 'server.key' and 'server.pem' from
-ca/setup.sh.
-
-To support subscription remediation using client certificates, set
-"SSLVerifyClient optional" and configure the trust root CA(s) for the
-client certificates with SSLCACertificateFile.
-
-Enable default-ssl site and restart Apache2:
-  sudo a2ensite default-ssl
-  sudo a2enmod ssl
-  sudo service apache2 restart
-
-
-Management UI
--------------
-
-The sample PHP scripts include a management UI for testing
-purposes. That is available at https://<server>/hs20/users.php
-
-
-AP configuration
-----------------
-
-APs can now be configured to use the OSU server as the RADIUS
-authentication server. In addition, the OSU Provider List ANQP element
-should be configured to use the SPP (SOAP+XML) option and with the
-following Server URL:
-https://<server>/hs20/spp.php/signup?realm=example.com
diff --git a/hs20/server/hs20_spp_server.c b/hs20/server/hs20_spp_server.c
deleted file mode 100644
index 347c40a..0000000
--- a/hs20/server/hs20_spp_server.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Hotspot 2.0 SPP server - standalone version
- * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <time.h>
-#include <sqlite3.h>
-
-#include "common.h"
-#include "common/version.h"
-#include "xml-utils.h"
-#include "spp_server.h"
-
-
-static void write_timestamp(FILE *f)
-{
-	time_t t;
-	struct tm *tm;
-
-	time(&t);
-	tm = localtime(&t);
-
-	fprintf(f, "%04u-%02u-%02u %02u:%02u:%02u ",
-		tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
-		tm->tm_hour, tm->tm_min, tm->tm_sec);
-}
-
-
-void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
-{
-	va_list ap;
-
-	if (ctx->debug_log == NULL)
-		return;
-
-	write_timestamp(ctx->debug_log);
-	va_start(ap, fmt);
-	vfprintf(ctx->debug_log, fmt, ap);
-	va_end(ap);
-
-	fprintf(ctx->debug_log, "\n");
-}
-
-
-void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node)
-{
-	char *str;
-
-	if (ctx->debug_log == NULL)
-		return;
-	str = xml_node_to_str(ctx->xml, node);
-	if (str == NULL)
-		return;
-
-	write_timestamp(ctx->debug_log);
-	fprintf(ctx->debug_log, "%s: '%s'\n", title, str);
-	os_free(str);
-}
-
-
-static int process(struct hs20_svc *ctx)
-{
-	int dmacc = 0;
-	xml_node_t *soap, *spp, *resp;
-	char *user, *realm, *post, *str;
-
-	ctx->addr = getenv("HS20ADDR");
-	if (ctx->addr)
-		debug_print(ctx, 1, "Connection from %s", ctx->addr);
-	ctx->test = getenv("HS20TEST");
-	if (ctx->test)
-		debug_print(ctx, 1, "Requested test functionality: %s",
-			    ctx->test);
-
-	user = getenv("HS20USER");
-	if (user && strlen(user) == 0)
-		user = NULL;
-	realm = getenv("HS20REALM");
-	if (realm == NULL) {
-		debug_print(ctx, 1, "HS20REALM not set");
-		return -1;
-	}
-	post = getenv("HS20POST");
-	if (post == NULL) {
-		debug_print(ctx, 1, "HS20POST not set");
-		return -1;
-	}
-
-	ctx->imsi = getenv("HS20IMSI");
-	if (ctx->imsi)
-		debug_print(ctx, 1, "IMSI %s", ctx->imsi);
-
-	ctx->eap_method = getenv("HS20EAPMETHOD");
-	if (ctx->eap_method)
-		debug_print(ctx, 1, "EAP method %s", ctx->eap_method);
-
-	ctx->id_hash = getenv("HS20IDHASH");
-	if (ctx->id_hash)
-		debug_print(ctx, 1, "ID-HASH %s", ctx->id_hash);
-
-	soap = xml_node_from_buf(ctx->xml, post);
-	if (soap == NULL) {
-		debug_print(ctx, 1, "Could not parse SOAP data");
-		return -1;
-	}
-	debug_dump_node(ctx, "Received SOAP message", soap);
-	spp = soap_get_body(ctx->xml, soap);
-	if (spp == NULL) {
-		debug_print(ctx, 1, "Could not get SPP message");
-		xml_node_free(ctx->xml, soap);
-		return -1;
-	}
-	debug_dump_node(ctx, "Received SPP message", spp);
-
-	resp = hs20_spp_server_process(ctx, spp, user, realm, dmacc);
-	xml_node_free(ctx->xml, soap);
-	if (resp == NULL && user == NULL) {
-		debug_print(ctx, 1, "Request HTTP authentication");
-		return 2; /* Request authentication */
-	}
-	if (resp == NULL) {
-		debug_print(ctx, 1, "No response");
-		return -1;
-	}
-
-	soap = soap_build_envelope(ctx->xml, resp);
-	if (soap == NULL) {
-		debug_print(ctx, 1, "SOAP envelope building failed");
-		return -1;
-	}
-	str = xml_node_to_str(ctx->xml, soap);
-	xml_node_free(ctx->xml, soap);
-	if (str == NULL) {
-		debug_print(ctx, 1, "Could not get node string");
-		return -1;
-	}
-	printf("%s", str);
-	free(str);
-
-	return 0;
-}
-
-
-static void usage(void)
-{
-	printf("usage:\n"
-	       "hs20_spp_server -r<root directory> [-f<debug log>]\n");
-}
-
-
-int main(int argc, char *argv[])
-{
-	struct hs20_svc ctx;
-	int ret;
-
-	os_memset(&ctx, 0, sizeof(ctx));
-	for (;;) {
-		int c = getopt(argc, argv, "f:r:v");
-		if (c < 0)
-			break;
-		switch (c) {
-		case 'f':
-			if (ctx.debug_log)
-				break;
-			ctx.debug_log = fopen(optarg, "a");
-			if (ctx.debug_log == NULL) {
-				printf("Could not write to %s\n", optarg);
-				return -1;
-			}
-			break;
-		case 'r':
-			ctx.root_dir = optarg;
-			break;
-		case 'v':
-			printf("hs20_spp_server v%s\n", VERSION_STR);
-			return 0;
-		default:
-			usage();
-			return -1;
-		}
-	}
-	if (ctx.root_dir == NULL) {
-		usage();
-		return -1;
-	}
-	ctx.xml = xml_node_init_ctx(&ctx, NULL);
-	if (ctx.xml == NULL)
-		return -1;
-	if (hs20_spp_server_init(&ctx) < 0) {
-		xml_node_deinit_ctx(ctx.xml);
-		return -1;
-	}
-
-	ret = process(&ctx);
-	debug_print(&ctx, 1, "process() --> %d", ret);
-
-	xml_node_deinit_ctx(ctx.xml);
-	hs20_spp_server_deinit(&ctx);
-	if (ctx.debug_log)
-		fclose(ctx.debug_log);
-
-	return ret;
-}
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
deleted file mode 100644
index 72694be..0000000
--- a/hs20/server/spp_server.c
+++ /dev/null
@@ -1,2936 +0,0 @@
-/*
- * Hotspot 2.0 SPP server
- * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#include <errno.h>
-#include <sqlite3.h>
-
-#include "common.h"
-#include "base64.h"
-#include "md5_i.h"
-#include "xml-utils.h"
-#include "spp_server.h"
-
-
-#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
-
-#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
-#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
-#define URN_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
-#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
-
-
-/* TODO: timeout to expire sessions */
-
-enum hs20_session_operation {
-	NO_OPERATION,
-	UPDATE_PASSWORD,
-	CONTINUE_SUBSCRIPTION_REMEDIATION,
-	CONTINUE_POLICY_UPDATE,
-	USER_REMEDIATION,
-	SUBSCRIPTION_REGISTRATION,
-	POLICY_REMEDIATION,
-	POLICY_UPDATE,
-	FREE_REMEDIATION,
-	CLEAR_REMEDIATION,
-	CERT_REENROLL,
-};
-
-
-static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
-				 const char *realm, const char *session_id,
-				 const char *field);
-static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
-				    const char *field);
-static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
-				 const char *realm, int use_dmacc);
-static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
-					     const char *session_id,
-					     const char *user,
-					     const char *realm,
-					     int add_est_user);
-
-
-static int db_add_session(struct hs20_svc *ctx,
-			  const char *user, const char *realm,
-			  const char *sessionid, const char *pw,
-			  const char *redirect_uri,
-			  enum hs20_session_operation operation,
-			  const u8 *mac_addr)
-{
-	char *sql;
-	int ret = 0;
-	char addr[20];
-
-	if (mac_addr)
-		snprintf(addr, sizeof(addr), MACSTR, MAC2STR(mac_addr));
-	else
-		addr[0] = '\0';
-	sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
-			      "operation,password,redirect_uri,mac_addr,test) "
-			      "VALUES "
-			      "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
-			      "%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
-			      sessionid, user ? user : "", realm ? realm : "",
-			      operation, pw ? pw : "",
-			      redirect_uri ? redirect_uri : "",
-			      addr, ctx->test);
-	if (sql == NULL)
-		return -1;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add session entry into sqlite "
-			    "database: %s", sqlite3_errmsg(ctx->db));
-		ret = -1;
-	}
-	sqlite3_free(sql);
-	return ret;
-}
-
-
-static void db_update_session_password(struct hs20_svc *ctx, const char *user,
-				       const char *realm, const char *sessionid,
-				       const char *pw)
-{
-	char *sql;
-
-	sql = sqlite3_mprintf("UPDATE sessions SET password=%Q WHERE id=%Q AND "
-			      "user=%Q AND realm=%Q",
-			      pw, sessionid, user, realm);
-	if (sql == NULL)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to update session password: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_update_session_machine_managed(struct hs20_svc *ctx,
-					      const char *user,
-					      const char *realm,
-					      const char *sessionid,
-					      const int pw_mm)
-{
-	char *sql;
-
-	sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
-			      pw_mm ? "1" : "0", sessionid, user, realm);
-	if (sql == NULL)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1,
-			    "Failed to update session machine_managed: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
-			       const char *realm, const char *sessionid,
-			       xml_node_t *node)
-{
-	char *str;
-	char *sql;
-
-	str = xml_node_to_str(ctx->xml, node);
-	if (str == NULL)
-		return;
-	sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
-			      "user=%Q AND realm=%Q",
-			      str, sessionid, user, realm);
-	free(str);
-	if (sql == NULL)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add session pps: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
-				   xml_node_t *node)
-{
-	char *str;
-	char *sql;
-
-	str = xml_node_to_str(ctx->xml, node);
-	if (str == NULL)
-		return;
-	sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
-			      str, sessionid);
-	free(str);
-	if (sql == NULL)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add session devinfo: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_add_session_devdetail(struct hs20_svc *ctx,
-				     const char *sessionid,
-				     xml_node_t *node)
-{
-	char *str;
-	char *sql;
-
-	str = xml_node_to_str(ctx->xml, node);
-	if (str == NULL)
-		return;
-	sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
-			      str, sessionid);
-	free(str);
-	if (sql == NULL)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add session devdetail: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_add_session_dmacc(struct hs20_svc *ctx, const char *sessionid,
-				 const char *username, const char *password)
-{
-	char *sql;
-
-	sql = sqlite3_mprintf("UPDATE sessions SET osu_user=%Q, osu_password=%Q WHERE id=%Q",
-			      username, password, sessionid);
-	if (!sql)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add session DMAcc: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_add_session_eap_method(struct hs20_svc *ctx,
-				      const char *sessionid,
-				      const char *method)
-{
-	char *sql;
-
-	sql = sqlite3_mprintf("UPDATE sessions SET eap_method=%Q WHERE id=%Q",
-			      method, sessionid);
-	if (!sql)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add session EAP method: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_add_session_id_hash(struct hs20_svc *ctx, const char *sessionid,
-				   const char *id_hash)
-{
-	char *sql;
-
-	sql = sqlite3_mprintf("UPDATE sessions SET mobile_identifier_hash=%Q WHERE id=%Q",
-			      id_hash, sessionid);
-	if (!sql)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add session ID hash: %s",
-			    sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_remove_session(struct hs20_svc *ctx,
-			      const char *user, const char *realm,
-			      const char *sessionid)
-{
-	char *sql;
-
-	if (user == NULL || realm == NULL) {
-		sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
-				      "id=%Q", sessionid);
-	} else {
-		sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
-				      "user=%Q AND realm=%Q AND id=%Q",
-				      user, realm, sessionid);
-	}
-	if (sql == NULL)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to delete session entry from "
-			    "sqlite database: %s", sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void hs20_eventlog(struct hs20_svc *ctx,
-			  const char *user, const char *realm,
-			  const char *sessionid, const char *notes,
-			  const char *dump)
-{
-	char *sql;
-	char *user_buf = NULL, *realm_buf = NULL;
-
-	debug_print(ctx, 1, "eventlog: %s", notes);
-
-	if (user == NULL) {
-		user_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
-					      "user");
-		user = user_buf;
-		realm_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
-					       "realm");
-		realm = realm_buf;
-	}
-
-	sql = sqlite3_mprintf("INSERT INTO eventlog"
-			      "(user,realm,sessionid,timestamp,notes,dump,addr)"
-			      " VALUES (%Q,%Q,%Q,"
-			      "strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
-			      "%Q,%Q,%Q)",
-			      user, realm, sessionid, notes,
-			      dump ? dump : "", ctx->addr ? ctx->addr : "");
-	free(user_buf);
-	free(realm_buf);
-	if (sql == NULL)
-		return;
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add eventlog entry into sqlite "
-			    "database: %s", sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void hs20_eventlog_node(struct hs20_svc *ctx,
-			       const char *user, const char *realm,
-			       const char *sessionid, const char *notes,
-			       xml_node_t *node)
-{
-	char *str;
-
-	if (node)
-		str = xml_node_to_str(ctx->xml, node);
-	else
-		str = NULL;
-	hs20_eventlog(ctx, user, realm, sessionid, notes, str);
-	free(str);
-}
-
-
-static void db_update_mo_str(struct hs20_svc *ctx, const char *user,
-			     const char *realm, const char *name,
-			     const char *str)
-{
-	char *sql;
-	if (user == NULL || realm == NULL || name == NULL)
-		return;
-	sql = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
-			      name, str, user, realm);
-	if (sql == NULL)
-		return;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to update user MO entry in sqlite "
-			    "database: %s", sqlite3_errmsg(ctx->db));
-	}
-	sqlite3_free(sql);
-}
-
-
-static void db_update_mo(struct hs20_svc *ctx, const char *user,
-			 const char *realm, const char *name, xml_node_t *mo)
-{
-	char *str;
-
-	str = xml_node_to_str(ctx->xml, mo);
-	if (str == NULL)
-		return;
-
-	db_update_mo_str(ctx, user, realm, name, str);
-	free(str);
-}
-
-
-static void add_text_node(struct hs20_svc *ctx, xml_node_t *parent,
-			  const char *name, const char *value)
-{
-	xml_node_create_text(ctx->xml, parent, NULL, name, value ? value : "");
-}
-
-
-static void add_text_node_conf(struct hs20_svc *ctx, const char *realm,
-			       xml_node_t *parent, const char *name,
-			       const char *field)
-{
-	char *val;
-	val = db_get_osu_config_val(ctx, realm, field);
-	xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
-	os_free(val);
-}
-
-
-static void add_text_node_conf_corrupt(struct hs20_svc *ctx, const char *realm,
-				       xml_node_t *parent, const char *name,
-				       const char *field)
-{
-	char *val;
-
-	val = db_get_osu_config_val(ctx, realm, field);
-	if (val) {
-		size_t len;
-
-		len = os_strlen(val);
-		if (len > 0) {
-			if (val[len - 1] == '0')
-				val[len - 1] = '1';
-			else
-				val[len - 1] = '0';
-		}
-	}
-	xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
-	os_free(val);
-}
-
-
-static int new_password(char *buf, int buflen)
-{
-	int i;
-
-	if (buflen < 1)
-		return -1;
-	buf[buflen - 1] = '\0';
-	if (os_get_random((unsigned char *) buf, buflen - 1) < 0)
-		return -1;
-
-	for (i = 0; i < buflen - 1; i++) {
-		unsigned char val = buf[i];
-		val %= 2 * 26 + 10;
-		if (val < 26)
-			buf[i] = 'a' + val;
-		else if (val < 2 * 26)
-			buf[i] = 'A' + val - 26;
-		else
-			buf[i] = '0' + val - 2 * 26;
-	}
-
-	return 0;
-}
-
-
-struct get_db_field_data {
-	const char *field;
-	char *value;
-};
-
-
-static int get_db_field(void *ctx, int argc, char *argv[], char *col[])
-{
-	struct get_db_field_data *data = ctx;
-	int i;
-
-	for (i = 0; i < argc; i++) {
-		if (os_strcmp(col[i], data->field) == 0 && argv[i]) {
-			os_free(data->value);
-			data->value = os_strdup(argv[i]);
-			break;
-		}
-	}
-
-	return 0;
-}
-
-
-static char * db_get_val(struct hs20_svc *ctx, const char *user,
-			 const char *realm, const char *field, int dmacc)
-{
-	char *cmd;
-	struct get_db_field_data data;
-
-	cmd = sqlite3_mprintf("SELECT %s FROM users WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
-			      field, dmacc ? "osu_user" : "identity",
-			      user, realm);
-	if (cmd == NULL)
-		return NULL;
-	memset(&data, 0, sizeof(data));
-	data.field = field;
-	if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
-	{
-		debug_print(ctx, 1, "Could not find user '%s'", user);
-		sqlite3_free(cmd);
-		return NULL;
-	}
-	sqlite3_free(cmd);
-
-	debug_print(ctx, 1, "DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
-		    "value='%s'", user, realm, field, dmacc, data.value);
-
-	return data.value;
-}
-
-
-static int db_update_val(struct hs20_svc *ctx, const char *user,
-			 const char *realm, const char *field,
-			 const char *val, int dmacc)
-{
-	char *cmd;
-	int ret;
-
-	cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
-			      field, val, dmacc ? "osu_user" : "identity", user,
-			      realm);
-	if (cmd == NULL)
-		return -1;
-	debug_print(ctx, 1, "DB: %s", cmd);
-	if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1,
-			    "Failed to update user in sqlite database: %s",
-			    sqlite3_errmsg(ctx->db));
-		ret = -1;
-	} else {
-		debug_print(ctx, 1,
-			    "DB: user='%s' realm='%s' field='%s' set to '%s'",
-			    user, realm, field, val);
-		ret = 0;
-	}
-	sqlite3_free(cmd);
-
-	return ret;
-}
-
-
-static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
-				 const char *realm, const char *session_id,
-				 const char *field)
-{
-	char *cmd;
-	struct get_db_field_data data;
-
-	if (user == NULL || realm == NULL) {
-		cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
-				      "id=%Q", field, session_id);
-	} else {
-		cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
-				      "user=%Q AND realm=%Q AND id=%Q",
-				      field, user, realm, session_id);
-	}
-	if (cmd == NULL)
-		return NULL;
-	debug_print(ctx, 1, "DB: %s", cmd);
-	memset(&data, 0, sizeof(data));
-	data.field = field;
-	if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
-	{
-		debug_print(ctx, 1, "DB: Could not find session %s: %s",
-			    session_id, sqlite3_errmsg(ctx->db));
-		sqlite3_free(cmd);
-		return NULL;
-	}
-	sqlite3_free(cmd);
-
-	debug_print(ctx, 1, "DB: return '%s'", data.value);
-	return data.value;
-}
-
-
-static int update_password(struct hs20_svc *ctx, const char *user,
-			   const char *realm, const char *pw, int dmacc)
-{
-	char *cmd;
-
-	cmd = sqlite3_mprintf("UPDATE users SET password=%Q, "
-			      "remediation='' "
-			      "WHERE %s=%Q AND phase2=1",
-			      pw, dmacc ? "osu_user" : "identity",
-			      user);
-	if (cmd == NULL)
-		return -1;
-	debug_print(ctx, 1, "DB: %s", cmd);
-	if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to update database for user '%s'",
-			    user);
-	}
-	sqlite3_free(cmd);
-
-	return 0;
-}
-
-
-static int clear_remediation(struct hs20_svc *ctx, const char *user,
-			     const char *realm, int dmacc)
-{
-	char *cmd;
-
-	cmd = sqlite3_mprintf("UPDATE users SET remediation='' WHERE %s=%Q",
-			      dmacc ? "osu_user" : "identity",
-			      user);
-	if (cmd == NULL)
-		return -1;
-	debug_print(ctx, 1, "DB: %s", cmd);
-	if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to update database for user '%s'",
-			    user);
-	}
-	sqlite3_free(cmd);
-
-	return 0;
-}
-
-
-static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent)
-{
-	xml_node_t *node;
-
-	node = xml_node_create(ctx->xml, parent, NULL, "EAPMethod");
-	if (node == NULL)
-		return -1;
-
-	add_text_node(ctx, node, "EAPType", "21");
-	add_text_node(ctx, node, "InnerMethod", "MS-CHAP-V2");
-
-	return 0;
-}
-
-
-static xml_node_t * build_username_password(struct hs20_svc *ctx,
-					    xml_node_t *parent,
-					    const char *user, const char *pw)
-{
-	xml_node_t *node;
-	char *b64;
-	size_t len;
-
-	node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
-	if (node == NULL)
-		return NULL;
-
-	add_text_node(ctx, node, "Username", user);
-
-	b64 = base64_encode(pw, strlen(pw), NULL);
-	if (b64 == NULL)
-		return NULL;
-	len = os_strlen(b64);
-	if (len > 0 && b64[len - 1] == '\n')
-		b64[len - 1] = '\0';
-	add_text_node(ctx, node, "Password", b64);
-	free(b64);
-
-	return node;
-}
-
-
-static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
-				 const char *user, const char *pw,
-				 int machine_managed)
-{
-	xml_node_t *node;
-
-	node = build_username_password(ctx, cred, user, pw);
-	if (node == NULL)
-		return -1;
-
-	add_text_node(ctx, node, "MachineManaged",
-		      machine_managed ? "TRUE" : "FALSE");
-	add_text_node(ctx, node, "SoftTokenApp", "");
-	add_eap_ttls(ctx, node);
-
-	return 0;
-}
-
-
-static void add_creation_date(struct hs20_svc *ctx, xml_node_t *cred)
-{
-	char str[30];
-	time_t now;
-	struct tm tm;
-
-	time(&now);
-	gmtime_r(&now, &tm);
-	snprintf(str, sizeof(str), "%04u-%02u-%02uT%02u:%02u:%02uZ",
-		 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-		 tm.tm_hour, tm.tm_min, tm.tm_sec);
-	xml_node_create_text(ctx->xml, cred, NULL, "CreationDate", str);
-}
-
-
-static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
-					const char *user, const char *realm,
-					const char *pw, int machine_managed)
-{
-	xml_node_t *cred;
-
-	cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
-	if (cred == NULL) {
-		debug_print(ctx, 1, "Failed to create Credential node");
-		return NULL;
-	}
-	add_creation_date(ctx, cred);
-	if (add_username_password(ctx, cred, user, pw, machine_managed) < 0) {
-		xml_node_free(ctx->xml, cred);
-		return NULL;
-	}
-	add_text_node(ctx, cred, "Realm", realm);
-
-	return cred;
-}
-
-
-static xml_node_t * build_credential(struct hs20_svc *ctx,
-				     const char *user, const char *realm,
-				     char *new_pw, size_t new_pw_len)
-{
-	if (new_password(new_pw, new_pw_len) < 0)
-		return NULL;
-	debug_print(ctx, 1, "Update password to '%s'", new_pw);
-	return build_credential_pw(ctx, user, realm, new_pw, 1);
-}
-
-
-static xml_node_t * build_credential_cert(struct hs20_svc *ctx,
-					  const char *user, const char *realm,
-					  const char *cert_fingerprint)
-{
-	xml_node_t *cred, *cert;
-
-	cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
-	if (cred == NULL) {
-		debug_print(ctx, 1, "Failed to create Credential node");
-		return NULL;
-	}
-	add_creation_date(ctx, cred);
-	cert = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate");
-	add_text_node(ctx, cert, "CertificateType", "x509v3");
-	add_text_node(ctx, cert, "CertSHA256Fingerprint", cert_fingerprint);
-	add_text_node(ctx, cred, "Realm", realm);
-
-	return cred;
-}
-
-
-static xml_node_t * build_post_dev_data_response(struct hs20_svc *ctx,
-						 xml_namespace_t **ret_ns,
-						 const char *session_id,
-						 const char *status,
-						 const char *error_code)
-{
-	xml_node_t *spp_node = NULL;
-	xml_namespace_t *ns;
-
-	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
-					"sppPostDevDataResponse");
-	if (spp_node == NULL)
-		return NULL;
-	if (ret_ns)
-		*ret_ns = ns;
-
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
-
-	if (error_code) {
-		xml_node_t *node;
-		node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
-		if (node)
-			xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
-					  error_code);
-	}
-
-	return spp_node;
-}
-
-
-static int add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node,
-			   xml_namespace_t *ns, const char *uri,
-			   xml_node_t *upd_node)
-{
-	xml_node_t *node, *tnds;
-	char *str;
-
-	tnds = mo_to_tnds(ctx->xml, upd_node, 0, NULL, NULL);
-	if (!tnds)
-		return -1;
-
-	str = xml_node_to_str(ctx->xml, tnds);
-	xml_node_free(ctx->xml, tnds);
-	if (str == NULL)
-		return -1;
-	node = xml_node_create_text(ctx->xml, spp_node, ns, "updateNode", str);
-	free(str);
-
-	xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", uri);
-
-	return 0;
-}
-
-
-static xml_node_t * read_subrem_file(struct hs20_svc *ctx,
-				     const char *subrem_id,
-				     char *uri, size_t uri_size)
-{
-	char fname[200];
-	char *buf, *buf2, *pos;
-	size_t len;
-	xml_node_t *node;
-
-	os_snprintf(fname, sizeof(fname), "%s/spp/subrem/%s",
-		    ctx->root_dir, subrem_id);
-	debug_print(ctx, 1, "Use subrem file %s", fname);
-
-	buf = os_readfile(fname, &len);
-	if (!buf)
-		return NULL;
-	buf2 = os_realloc(buf, len + 1);
-	if (!buf2) {
-		os_free(buf);
-		return NULL;
-	}
-	buf = buf2;
-	buf[len] = '\0';
-
-	pos = os_strchr(buf, '\n');
-	if (!pos) {
-		os_free(buf);
-		return NULL;
-	}
-	*pos++ = '\0';
-	os_strlcpy(uri, buf, uri_size);
-
-	node = xml_node_from_buf(ctx->xml, pos);
-	os_free(buf);
-
-	return node;
-}
-
-
-static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx,
-				       const char *user, const char *realm,
-				       const char *session_id,
-				       int machine_rem, int dmacc)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *cred;
-	char buf[400];
-	char new_pw[33];
-	char *status;
-	char *cert;
-
-	cert = db_get_val(ctx, user, realm, "cert", dmacc);
-	if (cert && cert[0] == '\0') {
-		os_free(cert);
-		cert = NULL;
-	}
-	if (cert) {
-		char *subrem;
-
-		/* No change needed in PPS MO unless specifically asked to */
-		cred = NULL;
-		buf[0] = '\0';
-
-		subrem = db_get_val(ctx, user, realm, "subrem", dmacc);
-		if (subrem && subrem[0]) {
-			cred = read_subrem_file(ctx, subrem, buf, sizeof(buf));
-			if (!cred) {
-				debug_print(ctx, 1,
-					    "Could not create updateNode from subrem file");
-				os_free(subrem);
-				os_free(cert);
-				return NULL;
-			}
-		}
-		os_free(subrem);
-	} else {
-		char *real_user = NULL;
-		char *pw;
-
-		if (dmacc) {
-			real_user = db_get_val(ctx, user, realm, "identity",
-					       dmacc);
-			if (!real_user) {
-				debug_print(ctx, 1,
-					    "Could not find user identity for dmacc user '%s'",
-					    user);
-				return NULL;
-			}
-		}
-
-		pw = db_get_session_val(ctx, user, realm, session_id,
-					"password");
-		if (pw && pw[0]) {
-			debug_print(ctx, 1, "New password from the user: '%s'",
-				    pw);
-			snprintf(new_pw, sizeof(new_pw), "%s", pw);
-			free(pw);
-			cred = build_credential_pw(ctx,
-						   real_user ? real_user : user,
-						   realm, new_pw, 0);
-		} else {
-			cred = build_credential(ctx,
-						real_user ? real_user : user,
-						realm, new_pw, sizeof(new_pw));
-		}
-
-		free(real_user);
-		if (!cred) {
-			debug_print(ctx, 1, "Could not build credential");
-			os_free(cert);
-			return NULL;
-		}
-
-		snprintf(buf, sizeof(buf),
-			 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
-			 realm);
-	}
-
-	status = "Remediation complete, request sppUpdateResponse";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (spp_node == NULL) {
-		debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
-		os_free(cert);
-		return NULL;
-	}
-
-	if ((cred && add_update_node(ctx, spp_node, ns, buf, cred) < 0) ||
-	    (!cred && !xml_node_create(ctx->xml, spp_node, ns, "noMOUpdate"))) {
-		debug_print(ctx, 1, "Could not add update node");
-		xml_node_free(ctx->xml, spp_node);
-		os_free(cert);
-		return NULL;
-	}
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   machine_rem ? "machine remediation" :
-			   "user remediation", cred);
-	xml_node_free(ctx->xml, cred);
-
-	if (cert) {
-		debug_print(ctx, 1, "Request DB remediation clearing on success notification (certificate credential)");
-		db_add_session(ctx, user, realm, session_id, NULL, NULL,
-			       CLEAR_REMEDIATION, NULL);
-	} else {
-		debug_print(ctx, 1, "Request DB password update on success "
-			    "notification");
-		db_add_session(ctx, user, realm, session_id, new_pw, NULL,
-			       UPDATE_PASSWORD, NULL);
-	}
-	os_free(cert);
-
-	return spp_node;
-}
-
-
-static xml_node_t * machine_remediation(struct hs20_svc *ctx,
-					const char *user,
-					const char *realm,
-					const char *session_id, int dmacc)
-{
-	return build_sub_rem_resp(ctx, user, realm, session_id, 1, dmacc);
-}
-
-
-static xml_node_t * cert_reenroll(struct hs20_svc *ctx,
-				  const char *user,
-				  const char *realm,
-				  const char *session_id)
-{
-	db_add_session(ctx, user, realm, session_id, NULL, NULL,
-		       CERT_REENROLL, NULL);
-	return spp_exec_get_certificate(ctx, session_id, user, realm, 0);
-}
-
-
-static xml_node_t * policy_remediation(struct hs20_svc *ctx,
-				       const char *user, const char *realm,
-				       const char *session_id, int dmacc)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *policy;
-	char buf[400];
-	const char *status;
-
-	hs20_eventlog(ctx, user, realm, session_id,
-		      "requires policy remediation", NULL);
-
-	db_add_session(ctx, user, realm, session_id, NULL, NULL,
-		       POLICY_REMEDIATION, NULL);
-
-	policy = build_policy(ctx, user, realm, dmacc);
-	if (!policy) {
-		return build_post_dev_data_response(
-			ctx, NULL, session_id,
-			"No update available at this time", NULL);
-	}
-
-	status = "Remediation complete, request sppUpdateResponse";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (spp_node == NULL)
-		return NULL;
-
-	snprintf(buf, sizeof(buf),
-		 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
-		 realm);
-
-	if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
-		xml_node_free(ctx->xml, spp_node);
-		xml_node_free(ctx->xml, policy);
-		return NULL;
-	}
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   "policy update (sub rem)", policy);
-	xml_node_free(ctx->xml, policy);
-
-	return spp_node;
-}
-
-
-static xml_node_t * browser_remediation(struct hs20_svc *ctx,
-					const char *session_id,
-					const char *redirect_uri,
-					const char *uri)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *exec_node;
-
-	if (redirect_uri == NULL) {
-		debug_print(ctx, 1, "Missing redirectURI attribute for user "
-			    "remediation");
-		return NULL;
-	}
-	debug_print(ctx, 1, "redirectURI %s", redirect_uri);
-
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
-		NULL);
-	if (spp_node == NULL)
-		return NULL;
-
-	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
-	xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
-			     uri);
-	return spp_node;
-}
-
-
-static xml_node_t * user_remediation(struct hs20_svc *ctx, const char *user,
-				     const char *realm, const char *session_id,
-				     const char *redirect_uri)
-{
-	char uri[300], *val;
-
-	hs20_eventlog(ctx, user, realm, session_id,
-		      "requires user remediation", NULL);
-	val = db_get_osu_config_val(ctx, realm, "remediation_url");
-	if (val == NULL)
-		return NULL;
-
-	db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
-		       USER_REMEDIATION, NULL);
-
-	snprintf(uri, sizeof(uri), "%s%s", val, session_id);
-	os_free(val);
-	return browser_remediation(ctx, session_id, redirect_uri, uri);
-}
-
-
-static xml_node_t * free_remediation(struct hs20_svc *ctx,
-				     const char *user, const char *realm,
-				     const char *session_id,
-				     const char *redirect_uri)
-{
-	char uri[300], *val;
-
-	hs20_eventlog(ctx, user, realm, session_id,
-		      "requires free/public account remediation", NULL);
-	val = db_get_osu_config_val(ctx, realm, "free_remediation_url");
-	if (val == NULL)
-		return NULL;
-
-	db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
-		       FREE_REMEDIATION, NULL);
-
-	snprintf(uri, sizeof(uri), "%s%s", val, session_id);
-	os_free(val);
-	return browser_remediation(ctx, session_id, redirect_uri, uri);
-}
-
-
-static xml_node_t * no_sub_rem(struct hs20_svc *ctx,
-			       const char *user, const char *realm,
-			       const char *session_id)
-{
-	const char *status;
-
-	hs20_eventlog(ctx, user, realm, session_id,
-		      "no subscription mediation available", NULL);
-
-	status = "No update available at this time";
-	return build_post_dev_data_response(ctx, NULL, session_id, status,
-					    NULL);
-}
-
-
-static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx,
-						  const char *user,
-						  const char *realm,
-						  const char *session_id,
-						  int dmacc,
-						  const char *redirect_uri)
-{
-	char *type, *identity;
-	xml_node_t *ret;
-	char *free_account;
-
-	identity = db_get_val(ctx, user, realm, "identity", dmacc);
-	if (identity == NULL || strlen(identity) == 0) {
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "user not found in database for remediation",
-			      NULL);
-		os_free(identity);
-		return build_post_dev_data_response(ctx, NULL, session_id,
-						    "Error occurred",
-						    "Not found");
-	}
-	os_free(identity);
-
-	free_account = db_get_osu_config_val(ctx, realm, "free_account");
-	if (free_account && strcmp(free_account, user) == 0) {
-		free(free_account);
-		return no_sub_rem(ctx, user, realm, session_id);
-	}
-	free(free_account);
-
-	type = db_get_val(ctx, user, realm, "remediation", dmacc);
-	if (type && strcmp(type, "free") != 0) {
-		char *val;
-		int shared = 0;
-		val = db_get_val(ctx, user, realm, "shared", dmacc);
-		if (val)
-			shared = atoi(val);
-		free(val);
-		if (shared) {
-			free(type);
-			return no_sub_rem(ctx, user, realm, session_id);
-		}
-	}
-	if (type && strcmp(type, "user") == 0)
-		ret = user_remediation(ctx, user, realm, session_id,
-				       redirect_uri);
-	else if (type && strcmp(type, "free") == 0)
-		ret = free_remediation(ctx, user, realm, session_id,
-				       redirect_uri);
-	else if (type && strcmp(type, "policy") == 0)
-		ret = policy_remediation(ctx, user, realm, session_id, dmacc);
-	else if (type && strcmp(type, "machine") == 0)
-		ret = machine_remediation(ctx, user, realm, session_id, dmacc);
-	else if (type && strcmp(type, "reenroll") == 0)
-		ret = cert_reenroll(ctx, user, realm, session_id);
-	else
-		ret = no_sub_rem(ctx, user, realm, session_id);
-	free(type);
-
-	return ret;
-}
-
-
-static xml_node_t * read_policy_file(struct hs20_svc *ctx,
-				     const char *policy_id)
-{
-	char fname[200];
-
-	snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
-		 ctx->root_dir, policy_id);
-	debug_print(ctx, 1, "Use policy file %s", fname);
-
-	return node_from_file(ctx->xml, fname);
-}
-
-
-static void update_policy_update_uri(struct hs20_svc *ctx, const char *realm,
-				     xml_node_t *policy)
-{
-	xml_node_t *node;
-	char *url;
-
-	node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
-	if (!node)
-		return;
-
-	url = db_get_osu_config_val(ctx, realm, "policy_url");
-	if (!url)
-		return;
-	xml_node_set_text(ctx->xml, node, url);
-	free(url);
-}
-
-
-static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
-				 const char *realm, int use_dmacc)
-{
-	char *policy_id;
-	xml_node_t *policy, *node;
-
-	policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc);
-	if (policy_id == NULL || strlen(policy_id) == 0) {
-		free(policy_id);
-		policy_id = strdup("default");
-		if (policy_id == NULL)
-			return NULL;
-	}
-	policy = read_policy_file(ctx, policy_id);
-	free(policy_id);
-	if (policy == NULL)
-		return NULL;
-
-	update_policy_update_uri(ctx, realm, policy);
-
-	node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate");
-	if (node && use_dmacc) {
-		char *pw;
-		pw = db_get_val(ctx, user, realm, "osu_password", use_dmacc);
-		if (pw == NULL ||
-		    build_username_password(ctx, node, user, pw) == NULL) {
-			debug_print(ctx, 1, "Failed to add Policy/PolicyUpdate/"
-				    "UsernamePassword");
-			free(pw);
-			xml_node_free(ctx->xml, policy);
-			return NULL;
-		}
-		free(pw);
-	}
-
-	return policy;
-}
-
-
-static xml_node_t * hs20_policy_update(struct hs20_svc *ctx,
-				       const char *user, const char *realm,
-				       const char *session_id, int dmacc)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node;
-	xml_node_t *policy;
-	char buf[400];
-	const char *status;
-	char *identity;
-
-	identity = db_get_val(ctx, user, realm, "identity", dmacc);
-	if (identity == NULL || strlen(identity) == 0) {
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "user not found in database for policy update",
-			      NULL);
-		os_free(identity);
-		return build_post_dev_data_response(ctx, NULL, session_id,
-						    "Error occurred",
-						    "Not found");
-	}
-	os_free(identity);
-
-	policy = build_policy(ctx, user, realm, dmacc);
-	if (!policy) {
-		return build_post_dev_data_response(
-			ctx, NULL, session_id,
-			"No update available at this time", NULL);
-	}
-
-	db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE,
-		       NULL);
-
-	status = "Update complete, request sppUpdateResponse";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (spp_node == NULL)
-		return NULL;
-
-	snprintf(buf, sizeof(buf),
-		 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
-		 realm);
-
-	if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
-		xml_node_free(ctx->xml, spp_node);
-		xml_node_free(ctx->xml, policy);
-		return NULL;
-	}
-
-	hs20_eventlog_node(ctx, user, realm, session_id, "policy update",
-			   policy);
-	xml_node_free(ctx->xml, policy);
-
-	return spp_node;
-}
-
-
-static xml_node_t * spp_get_mo(struct hs20_svc *ctx, xml_node_t *node,
-			       const char *urn, int *valid, char **ret_err)
-{
-	xml_node_t *child, *tnds, *mo;
-	const char *name;
-	char *mo_urn;
-	char *str;
-	char fname[200];
-
-	*valid = -1;
-	if (ret_err)
-		*ret_err = NULL;
-
-	xml_node_for_each_child(ctx->xml, child, node) {
-		xml_node_for_each_check(ctx->xml, child);
-		name = xml_node_get_localname(ctx->xml, child);
-		if (strcmp(name, "moContainer") != 0)
-			continue;
-		mo_urn = xml_node_get_attr_value_ns(ctx->xml, child, SPP_NS_URI,
-						    "moURN");
-		if (strcasecmp(urn, mo_urn) == 0) {
-			xml_node_get_attr_value_free(ctx->xml, mo_urn);
-			break;
-		}
-		xml_node_get_attr_value_free(ctx->xml, mo_urn);
-	}
-
-	if (child == NULL)
-		return NULL;
-
-	debug_print(ctx, 1, "moContainer text for %s", urn);
-	debug_dump_node(ctx, "moContainer", child);
-
-	str = xml_node_get_text(ctx->xml, child);
-	debug_print(ctx, 1, "moContainer payload: '%s'", str);
-	tnds = xml_node_from_buf(ctx->xml, str);
-	xml_node_get_text_free(ctx->xml, str);
-	if (tnds == NULL) {
-		debug_print(ctx, 1, "could not parse moContainer text");
-		return NULL;
-	}
-
-	snprintf(fname, sizeof(fname), "%s/spp/dm_ddf-v1_2.dtd", ctx->root_dir);
-	if (xml_validate_dtd(ctx->xml, tnds, fname, ret_err) == 0)
-		*valid = 1;
-	else if (ret_err && *ret_err &&
-		 os_strcmp(*ret_err, "No declaration for attribute xmlns of element MgmtTree\n") == 0) {
-		free(*ret_err);
-		debug_print(ctx, 1, "Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute");
-		*ret_err = NULL;
-		*valid = 1;
-	} else
-		*valid = 0;
-
-	mo = tnds_to_mo(ctx->xml, tnds);
-	xml_node_free(ctx->xml, tnds);
-	if (mo == NULL) {
-		debug_print(ctx, 1, "invalid moContainer for %s", urn);
-	}
-
-	return mo;
-}
-
-
-static xml_node_t * spp_exec_upload_mo(struct hs20_svc *ctx,
-				       const char *session_id, const char *urn)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *node, *exec_node;
-
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
-						NULL);
-	if (spp_node == NULL)
-		return NULL;
-
-	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
-
-	node = xml_node_create(ctx->xml, exec_node, ns, "uploadMO");
-	xml_node_add_attr(ctx->xml, node, ns, "moURN", urn);
-
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
-						   const char *realm,
-						   const char *session_id,
-						   const char *redirect_uri,
-						   const u8 *mac_addr)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *exec_node;
-	char uri[300], *val;
-
-	if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
-			   SUBSCRIPTION_REGISTRATION, mac_addr) < 0)
-		return NULL;
-	val = db_get_osu_config_val(ctx, realm, "signup_url");
-	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);
-	if (spp_node == NULL)
-		return NULL;
-
-	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
-
-	snprintf(uri, sizeof(uri), "%s%s", val, session_id);
-	os_free(val);
-	xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
-			     uri);
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_user_input_remediation(struct hs20_svc *ctx,
-						const char *user,
-						const char *realm, int dmacc,
-						const char *session_id)
-{
-	return build_sub_rem_resp(ctx, user, realm, session_id, 0, dmacc);
-}
-
-
-static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
-				    const char *field)
-{
-	char *cmd;
-	struct get_db_field_data data;
-
-	cmd = sqlite3_mprintf("SELECT value FROM osu_config WHERE realm=%Q AND "
-			      "field=%Q", realm, field);
-	if (cmd == NULL)
-		return NULL;
-	debug_print(ctx, 1, "DB: %s", cmd);
-	memset(&data, 0, sizeof(data));
-	data.field = "value";
-	if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
-	{
-		debug_print(ctx, 1, "DB: Could not find osu_config %s: %s",
-			    realm, sqlite3_errmsg(ctx->db));
-		sqlite3_free(cmd);
-		return NULL;
-	}
-	sqlite3_free(cmd);
-
-	debug_print(ctx, 1, "DB: return '%s'", data.value);
-	return data.value;
-}
-
-
-static xml_node_t * build_pps(struct hs20_svc *ctx,
-			      const char *user, const char *realm,
-			      const char *pw, const char *cert,
-			      int machine_managed, const char *test,
-			      const char *imsi, const char *dmacc_username,
-			      const char *dmacc_password,
-			      xml_node_t *policy_node)
-{
-	xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp, *p;
-	xml_node_t *cred, *eap, *userpw;
-
-	pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
-				   "PerProviderSubscription");
-	if (!pps) {
-		xml_node_free(ctx->xml, policy_node);
-		return NULL;
-	}
-
-	add_text_node(ctx, pps, "UpdateIdentifier", "1");
-
-	c = xml_node_create(ctx->xml, pps, NULL, "Cred01");
-
-	add_text_node(ctx, c, "CredentialPriority", "1");
-
-	if (imsi)
-		goto skip_aaa_trust_root;
-	aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot");
-	aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
-	add_text_node_conf(ctx, realm, aaa1, "CertURL",
-			   "aaa_trust_root_cert_url");
-	if (test && os_strcmp(test, "corrupt_aaa_hash") == 0) {
-		debug_print(ctx, 1,
-			    "TEST: Corrupt PPS/Cred*/AAAServerTrustRoot/Root*/CertSHA256FingerPrint");
-		add_text_node_conf_corrupt(ctx, realm, aaa1,
-					   "CertSHA256Fingerprint",
-					   "aaa_trust_root_cert_fingerprint");
-	} else {
-		add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
-				   "aaa_trust_root_cert_fingerprint");
-	}
-
-	if (test && os_strcmp(test, "corrupt_polupd_hash") == 0) {
-		debug_print(ctx, 1,
-			    "TEST: Corrupt PPS/Cred*/Policy/PolicyUpdate/Trustroot/CertSHA256FingerPrint");
-		p = xml_node_create(ctx->xml, c, NULL, "Policy");
-		upd = xml_node_create(ctx->xml, p, NULL, "PolicyUpdate");
-		add_text_node(ctx, upd, "UpdateInterval", "30");
-		add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
-		add_text_node(ctx, upd, "Restriction", "Unrestricted");
-		add_text_node_conf(ctx, realm, upd, "URI", "policy_url");
-		trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
-		add_text_node_conf(ctx, realm, trust, "CertURL",
-				   "policy_trust_root_cert_url");
-		add_text_node_conf_corrupt(ctx, realm, trust,
-					   "CertSHA256Fingerprint",
-					   "policy_trust_root_cert_fingerprint");
-	}
-skip_aaa_trust_root:
-
-	upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
-	add_text_node(ctx, upd, "UpdateInterval", "4294967295");
-	add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
-	add_text_node(ctx, upd, "Restriction", "HomeSP");
-	add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
-	trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
-	add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
-	if (test && os_strcmp(test, "corrupt_subrem_hash") == 0) {
-		debug_print(ctx, 1,
-			    "TEST: Corrupt PPS/Cred*/SubscriptionUpdate/Trustroot/CertSHA256FingerPrint");
-		add_text_node_conf_corrupt(ctx, realm, trust,
-					   "CertSHA256Fingerprint",
-					   "trust_root_cert_fingerprint");
-	} else {
-		add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
-				   "trust_root_cert_fingerprint");
-	}
-
-	if (dmacc_username &&
-	    !build_username_password(ctx, upd, dmacc_username,
-				     dmacc_password)) {
-		xml_node_free(ctx->xml, pps);
-		xml_node_free(ctx->xml, policy_node);
-		return NULL;
-	}
-
-	if (policy_node)
-		xml_node_add_child(ctx->xml, c, policy_node);
-
-	homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
-	add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
-	add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn");
-
-	xml_node_create(ctx->xml, c, NULL, "SubscriptionParameters");
-
-	cred = xml_node_create(ctx->xml, c, NULL, "Credential");
-	add_creation_date(ctx, cred);
-	if (imsi) {
-		xml_node_t *sim;
-		const char *type = "18"; /* default to EAP-SIM */
-
-		sim = xml_node_create(ctx->xml, cred, NULL, "SIM");
-		add_text_node(ctx, sim, "IMSI", imsi);
-		if (ctx->eap_method && os_strcmp(ctx->eap_method, "AKA") == 0)
-			type = "23";
-		else if (ctx->eap_method &&
-			 os_strcmp(ctx->eap_method, "AKA'") == 0)
-			type = "50";
-		add_text_node(ctx, sim, "EAPType", type);
-	} else if (cert) {
-		xml_node_t *dc;
-		dc = xml_node_create(ctx->xml, cred, NULL,
-				     "DigitalCertificate");
-		add_text_node(ctx, dc, "CertificateType", "x509v3");
-		add_text_node(ctx, dc, "CertSHA256Fingerprint", cert);
-	} else {
-		userpw = build_username_password(ctx, cred, user, pw);
-		add_text_node(ctx, userpw, "MachineManaged",
-			      machine_managed ? "TRUE" : "FALSE");
-		eap = xml_node_create(ctx->xml, userpw, NULL, "EAPMethod");
-		add_text_node(ctx, eap, "EAPType", "21");
-		add_text_node(ctx, eap, "InnerMethod", "MS-CHAP-V2");
-	}
-	add_text_node(ctx, cred, "Realm", realm);
-
-	return pps;
-}
-
-
-static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
-					     const char *session_id,
-					     const char *user,
-					     const char *realm,
-					     int add_est_user)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *enroll, *exec_node;
-	char *val;
-	char password[11];
-	char *b64;
-
-	if (add_est_user && new_password(password, sizeof(password)) < 0)
-		return NULL;
-
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
-						NULL);
-	if (spp_node == NULL)
-		return NULL;
-
-	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
-
-	enroll = xml_node_create(ctx->xml, exec_node, ns, "getCertificate");
-	xml_node_add_attr(ctx->xml, enroll, NULL, "enrollmentProtocol", "EST");
-
-	val = db_get_osu_config_val(ctx, realm, "est_url");
-	xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI",
-			     val ? val : "");
-	os_free(val);
-
-	if (!add_est_user)
-		return spp_node;
-
-	xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
-
-	b64 = base64_encode(password, strlen(password), NULL);
-	if (b64 == NULL) {
-		xml_node_free(ctx->xml, spp_node);
-		return NULL;
-	}
-	xml_node_create_text(ctx->xml, enroll, ns, "estPassword", b64);
-	free(b64);
-
-	db_update_session_password(ctx, user, realm, session_id, password);
-
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx,
-						 const char *session_id,
-						 int enrollment_done)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *node = NULL;
-	xml_node_t *pps, *tnds;
-	char buf[400];
-	char *str;
-	char *user, *realm, *pw, *type, *mm, *test;
-	const char *status;
-	int cert = 0;
-	int machine_managed = 0;
-	char *fingerprint;
-
-	user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
-	realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
-	pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
-
-	if (!user || !realm || !pw) {
-		debug_print(ctx, 1, "Could not find session info from DB for "
-			    "the new subscription");
-		free(user);
-		free(realm);
-		free(pw);
-		return NULL;
-	}
-
-	mm = db_get_session_val(ctx, NULL, NULL, session_id, "machine_managed");
-	if (mm && atoi(mm))
-		machine_managed = 1;
-	free(mm);
-
-	type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
-	if (type && strcmp(type, "cert") == 0)
-		cert = 1;
-	free(type);
-
-	if (cert && !enrollment_done) {
-		xml_node_t *ret;
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "request client certificate enrollment", NULL);
-		ret = spp_exec_get_certificate(ctx, session_id, user, realm, 1);
-		free(user);
-		free(realm);
-		free(pw);
-		return ret;
-	}
-
-	if (!cert && strlen(pw) == 0) {
-		machine_managed = 1;
-		free(pw);
-		pw = malloc(11);
-		if (pw == NULL || new_password(pw, 11) < 0) {
-			free(user);
-			free(realm);
-			free(pw);
-			return NULL;
-		}
-	}
-
-	status = "Provisioning complete, request sppUpdateResponse";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (spp_node == NULL)
-		return NULL;
-
-	fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
-	test = db_get_session_val(ctx, NULL, NULL, session_id, "test");
-	if (test)
-		debug_print(ctx, 1, "TEST: Requested special behavior: %s",
-			    test);
-	pps = build_pps(ctx, user, realm, pw,
-			fingerprint ? fingerprint : NULL, machine_managed,
-			test, NULL, NULL, NULL, NULL);
-	free(fingerprint);
-	free(test);
-	if (!pps) {
-		xml_node_free(ctx->xml, spp_node);
-		free(user);
-		free(realm);
-		free(pw);
-		return NULL;
-	}
-
-	debug_print(ctx, 1, "Request DB subscription registration on success "
-		    "notification");
-	if (machine_managed) {
-		db_update_session_password(ctx, user, realm, session_id, pw);
-		db_update_session_machine_managed(ctx, user, realm, session_id,
-						  machine_managed);
-	}
-	db_add_session_pps(ctx, user, realm, session_id, pps);
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   "new subscription", pps);
-	free(user);
-	free(pw);
-
-	tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
-	xml_node_free(ctx->xml, pps);
-	if (!tnds) {
-		xml_node_free(ctx->xml, spp_node);
-		free(realm);
-		return NULL;
-	}
-
-	str = xml_node_to_str(ctx->xml, tnds);
-	xml_node_free(ctx->xml, tnds);
-	if (str == NULL) {
-		xml_node_free(ctx->xml, spp_node);
-		free(realm);
-		return NULL;
-	}
-
-	node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
-	free(str);
-	snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
-	free(realm);
-	xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
-	xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
-
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
-						     const char *user,
-						     const char *realm,
-						     const char *session_id)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node;
-	xml_node_t *cred;
-	char buf[400];
-	char *status;
-	char *free_account, *pw;
-
-	free_account = db_get_osu_config_val(ctx, realm, "free_account");
-	if (free_account == NULL)
-		return NULL;
-	pw = db_get_val(ctx, free_account, realm, "password", 0);
-	if (pw == NULL) {
-		free(free_account);
-		return NULL;
-	}
-
-	cred = build_credential_pw(ctx, free_account, realm, pw, 1);
-	free(free_account);
-	free(pw);
-	if (!cred) {
-		xml_node_free(ctx->xml, cred);
-		return NULL;
-	}
-
-	status = "Remediation complete, request sppUpdateResponse";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (spp_node == NULL)
-		return NULL;
-
-	snprintf(buf, sizeof(buf),
-		 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
-		 realm);
-
-	if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
-		xml_node_free(ctx->xml, spp_node);
-		return NULL;
-	}
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   "free/public remediation", cred);
-	xml_node_free(ctx->xml, cred);
-
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx,
-					     const char *user,
-					     const char *realm, int dmacc,
-					     const char *session_id)
-{
-	char *val;
-	enum hs20_session_operation oper;
-
-	val = db_get_session_val(ctx, user, realm, session_id, "operation");
-	if (val == NULL) {
-		debug_print(ctx, 1, "No session %s found to continue",
-			    session_id);
-		return NULL;
-	}
-	oper = atoi(val);
-	free(val);
-
-	if (oper == USER_REMEDIATION) {
-		return hs20_user_input_remediation(ctx, user, realm, dmacc,
-						   session_id);
-	}
-
-	if (oper == FREE_REMEDIATION) {
-		return hs20_user_input_free_remediation(ctx, user, realm,
-							session_id);
-	}
-
-	if (oper == SUBSCRIPTION_REGISTRATION) {
-		return hs20_user_input_registration(ctx, session_id, 0);
-	}
-
-	debug_print(ctx, 1, "User session %s not in state for user input "
-		    "completion", session_id);
-	return NULL;
-}
-
-
-static xml_node_t * hs20_cert_reenroll_complete(struct hs20_svc *ctx,
-						 const char *session_id)
-{
-	char *user, *realm, *cert;
-	char *status;
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *cred;
-	char buf[400];
-
-	user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
-	realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
-	cert = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
-	if (!user || !realm || !cert) {
-		debug_print(ctx, 1,
-			    "Could not find session info from DB for certificate reenrollment");
-		free(user);
-		free(realm);
-		free(cert);
-		return NULL;
-	}
-
-	cred = build_credential_cert(ctx, user, realm, cert);
-	if (!cred) {
-		debug_print(ctx, 1, "Could not build credential");
-		free(user);
-		free(realm);
-		free(cert);
-		return NULL;
-	}
-
-	status = "Remediation complete, request sppUpdateResponse";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (spp_node == NULL) {
-		debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
-		free(user);
-		free(realm);
-		free(cert);
-		xml_node_free(ctx->xml, cred);
-		return NULL;
-	}
-
-	snprintf(buf, sizeof(buf),
-		 "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
-		 realm);
-
-	if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
-		debug_print(ctx, 1, "Could not add update node");
-		xml_node_free(ctx->xml, spp_node);
-		free(user);
-		free(realm);
-		free(cert);
-		return NULL;
-	}
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   "certificate reenrollment", cred);
-	xml_node_free(ctx->xml, cred);
-
-	free(user);
-	free(realm);
-	free(cert);
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx,
-					       const char *user,
-					       const char *realm, int dmacc,
-					       const char *session_id)
-{
-	char *val;
-	enum hs20_session_operation oper;
-
-	val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
-	if (val == NULL) {
-		debug_print(ctx, 1, "No session %s found to continue",
-			    session_id);
-		return NULL;
-	}
-	oper = atoi(val);
-	free(val);
-
-	if (oper == SUBSCRIPTION_REGISTRATION)
-		return hs20_user_input_registration(ctx, session_id, 1);
-	if (oper == CERT_REENROLL)
-		return hs20_cert_reenroll_complete(ctx, session_id);
-
-	debug_print(ctx, 1, "User session %s not in state for certificate "
-		    "enrollment completion", session_id);
-	return NULL;
-}
-
-
-static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx,
-					    const char *user,
-					    const char *realm, int dmacc,
-					    const char *session_id)
-{
-	char *val;
-	enum hs20_session_operation oper;
-	xml_node_t *spp_node, *node;
-	char *status;
-	xml_namespace_t *ns;
-
-	val = db_get_session_val(ctx, user, realm, session_id, "operation");
-	if (val == NULL) {
-		debug_print(ctx, 1, "No session %s found to continue",
-			    session_id);
-		return NULL;
-	}
-	oper = atoi(val);
-	free(val);
-
-	if (oper != SUBSCRIPTION_REGISTRATION) {
-		debug_print(ctx, 1, "User session %s not in state for "
-			    "enrollment failure", session_id);
-		return NULL;
-	}
-
-	status = "Error occurred";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (spp_node == NULL)
-		return NULL;
-	node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
-	xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
-			  "Credentials cannot be provisioned at this time");
-	db_remove_session(ctx, user, realm, session_id);
-
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_sim_provisioning(struct hs20_svc *ctx,
-					  const char *user,
-					  const char *realm, int dmacc,
-					  const char *session_id)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *node = NULL;
-	xml_node_t *pps, *tnds;
-	char buf[400];
-	char *str;
-	const char *status;
-	char dmacc_username[32];
-	char dmacc_password[32];
-	char *policy;
-	xml_node_t *policy_node = NULL;
-
-	if (!ctx->imsi) {
-		debug_print(ctx, 1, "IMSI not available for SIM provisioning");
-		return NULL;
-	}
-
-	if (new_password(dmacc_username, sizeof(dmacc_username)) < 0 ||
-	    new_password(dmacc_password, sizeof(dmacc_password)) < 0) {
-		debug_print(ctx, 1,
-			    "Failed to generate DMAcc username/password");
-		return NULL;
-	}
-
-	status = "Provisioning complete, request sppUpdateResponse";
-	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
-						NULL);
-	if (!spp_node)
-		return NULL;
-
-	policy = db_get_osu_config_val(ctx, realm, "sim_policy");
-	if (policy) {
-		policy_node = read_policy_file(ctx, policy);
-		os_free(policy);
-		if (!policy_node) {
-			xml_node_free(ctx->xml, spp_node);
-			return NULL;
-		}
-		update_policy_update_uri(ctx, realm, policy_node);
-		node = get_node_uri(ctx->xml, policy_node,
-				    "Policy/PolicyUpdate");
-		if (node)
-			build_username_password(ctx, node, dmacc_username,
-						dmacc_password);
-	}
-
-	pps = build_pps(ctx, NULL, realm, NULL, NULL, 0, NULL, ctx->imsi,
-			dmacc_username, dmacc_password, policy_node);
-	if (!pps) {
-		xml_node_free(ctx->xml, spp_node);
-		return NULL;
-	}
-
-	debug_print(ctx, 1,
-		    "Request DB subscription registration on success notification");
-	if (!user || !user[0])
-		user = ctx->imsi;
-	db_add_session(ctx, user, realm, session_id, NULL, NULL,
-		       SUBSCRIPTION_REGISTRATION, NULL);
-	db_add_session_dmacc(ctx, session_id, dmacc_username, dmacc_password);
-	if (ctx->eap_method)
-		db_add_session_eap_method(ctx, session_id, ctx->eap_method);
-	if (ctx->id_hash)
-		db_add_session_id_hash(ctx, session_id, ctx->id_hash);
-	db_add_session_pps(ctx, user, realm, session_id, pps);
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   "new subscription", pps);
-
-	tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
-	xml_node_free(ctx->xml, pps);
-	if (!tnds) {
-		xml_node_free(ctx->xml, spp_node);
-		return NULL;
-	}
-
-	str = xml_node_to_str(ctx->xml, tnds);
-	xml_node_free(ctx->xml, tnds);
-	if (!str) {
-		xml_node_free(ctx->xml, spp_node);
-		return NULL;
-	}
-
-	node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
-	free(str);
-	snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
-	xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
-	xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
-
-	return spp_node;
-}
-
-
-static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx,
-					   xml_node_t *node,
-					   const char *user,
-					   const char *realm,
-					   const char *session_id,
-					   int dmacc)
-{
-	const char *req_reason;
-	char *redirect_uri = NULL;
-	char *req_reason_buf = NULL;
-	char str[200];
-	xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
-	xml_node_t *mo, *macaddr;
-	char *version;
-	int valid;
-	char *supp, *pos;
-	char *err;
-	u8 wifi_mac_addr[ETH_ALEN];
-
-	version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
-					     "sppVersion");
-	if (version == NULL || strstr(version, "1.0") == NULL) {
-		ret = build_post_dev_data_response(
-			ctx, NULL, session_id, "Error occurred",
-			"SPP version not supported");
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "Unsupported sppVersion", ret);
-		xml_node_get_attr_value_free(ctx->xml, version);
-		return ret;
-	}
-	xml_node_get_attr_value_free(ctx->xml, version);
-
-	mo = get_node(ctx->xml, node, "supportedMOList");
-	if (mo == NULL) {
-		ret = build_post_dev_data_response(
-			ctx, NULL, session_id, "Error occurred",
-			"Other");
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "No supportedMOList element", ret);
-		return ret;
-	}
-	supp = xml_node_get_text(ctx->xml, mo);
-	for (pos = supp; pos && *pos; pos++)
-		*pos = tolower(*pos);
-	if (supp == NULL ||
-	    strstr(supp, URN_OMA_DM_DEVINFO) == NULL ||
-	    strstr(supp, URN_OMA_DM_DEVDETAIL) == NULL ||
-	    strstr(supp, URN_HS20_PPS) == NULL) {
-		xml_node_get_text_free(ctx->xml, supp);
-		ret = build_post_dev_data_response(
-			ctx, NULL, session_id, "Error occurred",
-			"One or more mandatory MOs not supported");
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "Unsupported MOs", ret);
-		return ret;
-	}
-	xml_node_get_text_free(ctx->xml, supp);
-
-	req_reason_buf = xml_node_get_attr_value(ctx->xml, node,
-						 "requestReason");
-	if (req_reason_buf == NULL) {
-		debug_print(ctx, 1, "No requestReason attribute");
-		return NULL;
-	}
-	req_reason = req_reason_buf;
-
-	redirect_uri = xml_node_get_attr_value(ctx->xml, node, "redirectURI");
-
-	debug_print(ctx, 1, "requestReason: %s  sessionID: %s  redirectURI: %s",
-		    req_reason, session_id, redirect_uri);
-	snprintf(str, sizeof(str), "sppPostDevData: requestReason=%s",
-		 req_reason);
-	hs20_eventlog(ctx, user, realm, session_id, str, NULL);
-
-	devinfo = spp_get_mo(ctx, node, URN_OMA_DM_DEVINFO, &valid, &err);
-	if (devinfo == NULL) {
-		ret = build_post_dev_data_response(ctx, NULL, session_id,
-						   "Error occurred", "Other");
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "No DevInfo moContainer in sppPostDevData",
-				   ret);
-		os_free(err);
-		goto out;
-	}
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   "Received DevInfo MO", devinfo);
-	if (valid == 0) {
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "OMA-DM DDF DTD validation errors in DevInfo MO",
-			      err);
-		ret = build_post_dev_data_response(ctx, NULL, session_id,
-						   "Error occurred", "Other");
-		os_free(err);
-		goto out;
-	}
-	os_free(err);
-	if (user)
-		db_update_mo(ctx, user, realm, "devinfo", devinfo);
-
-	devdetail = spp_get_mo(ctx, node, URN_OMA_DM_DEVDETAIL, &valid, &err);
-	if (devdetail == NULL) {
-		ret = build_post_dev_data_response(ctx, NULL, session_id,
-						   "Error occurred", "Other");
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "No DevDetail moContainer in sppPostDevData",
-				   ret);
-		os_free(err);
-		goto out;
-	}
-
-	hs20_eventlog_node(ctx, user, realm, session_id,
-			   "Received DevDetail MO", devdetail);
-	if (valid == 0) {
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "OMA-DM DDF DTD validation errors "
-			      "in DevDetail MO", err);
-		ret = build_post_dev_data_response(ctx, NULL, session_id,
-						   "Error occurred", "Other");
-		os_free(err);
-		goto out;
-	}
-	os_free(err);
-
-	os_memset(wifi_mac_addr, 0, ETH_ALEN);
-	macaddr = get_node(ctx->xml, devdetail,
-			   "Ext/org.wi-fi/Wi-Fi/Wi-FiMACAddress");
-	if (macaddr) {
-		char *addr, buf[50];
-
-		addr = xml_node_get_text(ctx->xml, macaddr);
-		if (addr && hwaddr_compact_aton(addr, wifi_mac_addr) == 0) {
-			snprintf(buf, sizeof(buf), "DevDetail MAC address: "
-				 MACSTR, MAC2STR(wifi_mac_addr));
-			hs20_eventlog(ctx, user, realm, session_id, buf, NULL);
-			xml_node_get_text_free(ctx->xml, addr);
-		} else {
-			hs20_eventlog(ctx, user, realm, session_id,
-				      "Could not extract MAC address from DevDetail",
-				      NULL);
-		}
-	} else {
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "No MAC address in DevDetail", NULL);
-	}
-
-	if (user)
-		db_update_mo(ctx, user, realm, "devdetail", devdetail);
-
-	if (user)
-		mo = spp_get_mo(ctx, node, URN_HS20_PPS, &valid, &err);
-	else {
-		mo = NULL;
-		err = NULL;
-	}
-	if (user && mo) {
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "Received PPS MO", mo);
-		if (valid == 0) {
-			hs20_eventlog(ctx, user, realm, session_id,
-				      "OMA-DM DDF DTD validation errors "
-				      "in PPS MO", err);
-			xml_node_get_attr_value_free(ctx->xml, redirect_uri);
-			os_free(err);
-			return build_post_dev_data_response(
-				ctx, NULL, session_id,
-				"Error occurred", "Other");
-		}
-		db_update_mo(ctx, user, realm, "pps", mo);
-		db_update_val(ctx, user, realm, "fetch_pps", "0", dmacc);
-		xml_node_free(ctx->xml, mo);
-	}
-	os_free(err);
-
-	if (user && !mo) {
-		char *fetch;
-		int fetch_pps;
-
-		fetch = db_get_val(ctx, user, realm, "fetch_pps", dmacc);
-		fetch_pps = fetch ? atoi(fetch) : 0;
-		free(fetch);
-
-		if (fetch_pps) {
-			enum hs20_session_operation oper;
-			if (strcasecmp(req_reason, "Subscription remediation")
-			    == 0)
-				oper = CONTINUE_SUBSCRIPTION_REMEDIATION;
-			else if (strcasecmp(req_reason, "Policy update") == 0)
-				oper = CONTINUE_POLICY_UPDATE;
-			else
-				oper = NO_OPERATION;
-			if (db_add_session(ctx, user, realm, session_id, NULL,
-					   NULL, oper, NULL) < 0)
-				goto out;
-
-			ret = spp_exec_upload_mo(ctx, session_id,
-						 URN_HS20_PPS);
-			hs20_eventlog_node(ctx, user, realm, session_id,
-					   "request PPS MO upload",
-					   ret);
-			goto out;
-		}
-	}
-
-	if (user && strcasecmp(req_reason, "MO upload") == 0) {
-		char *val = db_get_session_val(ctx, user, realm, session_id,
-					       "operation");
-		enum hs20_session_operation oper;
-		if (!val) {
-			debug_print(ctx, 1, "No session %s found to continue",
-				    session_id);
-			goto out;
-		}
-		oper = atoi(val);
-		free(val);
-		if (oper == CONTINUE_SUBSCRIPTION_REMEDIATION)
-			req_reason = "Subscription remediation";
-		else if (oper == CONTINUE_POLICY_UPDATE)
-			req_reason = "Policy update";
-		else {
-			debug_print(ctx, 1,
-				    "No pending operation in session %s",
-				    session_id);
-			goto out;
-		}
-	}
-
-	if (strcasecmp(req_reason, "Subscription registration") == 0) {
-		ret = hs20_subscription_registration(ctx, realm, session_id,
-						     redirect_uri,
-						     wifi_mac_addr);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "subscription registration response",
-				   ret);
-		goto out;
-	}
-	if (user && strcasecmp(req_reason, "Subscription remediation") == 0) {
-		ret = hs20_subscription_remediation(ctx, user, realm,
-						    session_id, dmacc,
-						    redirect_uri);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "subscription remediation response",
-				   ret);
-		goto out;
-	}
-	if (user && strcasecmp(req_reason, "Policy update") == 0) {
-		ret = hs20_policy_update(ctx, user, realm, session_id, dmacc);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "policy update response",
-				   ret);
-		goto out;
-	}
-
-	if (strcasecmp(req_reason, "User input completed") == 0) {
-		db_add_session_devinfo(ctx, session_id, devinfo);
-		db_add_session_devdetail(ctx, session_id, devdetail);
-		ret = hs20_user_input_complete(ctx, user, realm, dmacc,
-					       session_id);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "user input completed response", ret);
-		goto out;
-	}
-
-	if (strcasecmp(req_reason, "Certificate enrollment completed") == 0) {
-		ret = hs20_cert_enroll_completed(ctx, user, realm, dmacc,
-						 session_id);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "certificate enrollment response", ret);
-		goto out;
-	}
-
-	if (strcasecmp(req_reason, "Certificate enrollment failed") == 0) {
-		ret = hs20_cert_enroll_failed(ctx, user, realm, dmacc,
-					      session_id);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "certificate enrollment failed response",
-				   ret);
-		goto out;
-	}
-
-	if (strcasecmp(req_reason, "Subscription provisioning") == 0) {
-		ret = hs20_sim_provisioning(ctx, user, realm, dmacc,
-					    session_id);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "subscription provisioning response",
-				   ret);
-		goto out;
-	}
-
-	debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'",
-		    req_reason, user);
-out:
-	xml_node_get_attr_value_free(ctx->xml, req_reason_buf);
-	xml_node_get_attr_value_free(ctx->xml, redirect_uri);
-	if (devinfo)
-		xml_node_free(ctx->xml, devinfo);
-	if (devdetail)
-		xml_node_free(ctx->xml, devdetail);
-	return ret;
-}
-
-
-static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx,
-						const char *session_id,
-						const char *status,
-						const char *error_code)
-{
-	xml_namespace_t *ns;
-	xml_node_t *spp_node, *node;
-
-	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
-					"sppExchangeComplete");
-
-
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
-	xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
-
-	if (error_code) {
-		node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
-		xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
-				  error_code);
-	}
-
-	return spp_node;
-}
-
-
-static int add_subscription(struct hs20_svc *ctx, const char *session_id)
-{
-	char *user, *realm, *pw, *pw_mm, *pps, *str;
-	char *osu_user, *osu_password, *eap_method;
-	char *policy = NULL;
-	char *sql;
-	int ret = -1;
-	char *free_account;
-	int free_acc;
-	char *type;
-	int cert = 0;
-	char *cert_pem, *fingerprint;
-	const char *method;
-
-	user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
-	realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
-	pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
-	pw_mm = db_get_session_val(ctx, NULL, NULL, session_id,
-				   "machine_managed");
-	pps = db_get_session_val(ctx, NULL, NULL, session_id, "pps");
-	cert_pem = db_get_session_val(ctx, NULL, NULL, session_id, "cert_pem");
-	fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
-	type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
-	if (type && strcmp(type, "cert") == 0)
-		cert = 1;
-	free(type);
-	osu_user = db_get_session_val(ctx, NULL, NULL, session_id, "osu_user");
-	osu_password = db_get_session_val(ctx, NULL, NULL, session_id,
-					  "osu_password");
-	eap_method = db_get_session_val(ctx, NULL, NULL, session_id,
-					"eap_method");
-
-	if (!user || !realm || !pw) {
-		debug_print(ctx, 1, "Could not find session info from DB for "
-			    "the new subscription");
-		goto out;
-	}
-
-	free_account = db_get_osu_config_val(ctx, realm, "free_account");
-	free_acc = free_account && strcmp(free_account, user) == 0;
-	free(free_account);
-
-	policy = db_get_osu_config_val(ctx, realm, "sim_policy");
-
-	debug_print(ctx, 1,
-		    "New subscription: user='%s' realm='%s' free_acc=%d",
-		    user, realm, free_acc);
-	debug_print(ctx, 1, "New subscription: pps='%s'", pps);
-
-	sql = sqlite3_mprintf("UPDATE eventlog SET user=%Q, realm=%Q WHERE "
-			      "sessionid=%Q AND (user='' OR user IS NULL)",
-			      user, realm, session_id);
-	if (sql) {
-		debug_print(ctx, 1, "DB: %s", sql);
-		if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-			debug_print(ctx, 1, "Failed to update eventlog in "
-				    "sqlite database: %s",
-				    sqlite3_errmsg(ctx->db));
-		}
-		sqlite3_free(sql);
-	}
-
-	if (free_acc) {
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "completed shared free account registration",
-			      NULL);
-		ret = 0;
-		goto out;
-	}
-
-	str = db_get_session_val(ctx, NULL, NULL, session_id, "mac_addr");
-
-	if (eap_method && eap_method[0])
-		method = eap_method;
-	else
-		method = cert ? "TLS" : "TTLS-MSCHAPV2";
-	sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr,osu_user,osu_password,policy) VALUES (%Q,%Q,%d,%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
-			      user, realm, cert ? 0 : 1,
-			      method,
-			      fingerprint ? fingerprint : "",
-			      cert_pem ? cert_pem : "",
-			      pw_mm && atoi(pw_mm) ? 1 : 0,
-			      str ? str : "",
-			      osu_user ? osu_user : "",
-			      osu_password ? osu_password : "",
-			      policy ? policy : "");
-	free(str);
-	if (sql == NULL)
-		goto out;
-	debug_print(ctx, 1, "DB: %s", sql);
-	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
-		debug_print(ctx, 1, "Failed to add user in sqlite database: %s",
-			    sqlite3_errmsg(ctx->db));
-		sqlite3_free(sql);
-		goto out;
-	}
-	sqlite3_free(sql);
-
-	if (cert)
-		ret = 0;
-	else
-		ret = update_password(ctx, user, realm, pw, 0);
-	if (ret < 0) {
-		sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
-				      user, realm);
-		if (sql) {
-			debug_print(ctx, 1, "DB: %s", sql);
-			sqlite3_exec(ctx->db, sql, NULL, NULL, NULL);
-			sqlite3_free(sql);
-		}
-	}
-
-	if (pps)
-		db_update_mo_str(ctx, user, realm, "pps", pps);
-
-	str = db_get_session_val(ctx, NULL, NULL, session_id, "devinfo");
-	if (str) {
-		db_update_mo_str(ctx, user, realm, "devinfo", str);
-		free(str);
-	}
-
-	str = db_get_session_val(ctx, NULL, NULL, session_id, "devdetail");
-	if (str) {
-		db_update_mo_str(ctx, user, realm, "devdetail", str);
-		free(str);
-	}
-
-	if (cert && user) {
-		const char *serialnum;
-
-		str = db_get_session_val(ctx, NULL, NULL, session_id,
-					 "mac_addr");
-
-		if (os_strncmp(user, "cert-", 5) == 0)
-			serialnum = user + 5;
-		else
-			serialnum = "";
-		sql = sqlite3_mprintf("INSERT OR REPLACE INTO cert_enroll (mac_addr,user,realm,serialnum) VALUES(%Q,%Q,%Q,%Q)",
-				      str ? str : "", user, realm ? realm : "",
-				      serialnum);
-		free(str);
-		if (sql) {
-			debug_print(ctx, 1, "DB: %s", sql);
-			if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
-			    SQLITE_OK) {
-				debug_print(ctx, 1,
-					    "Failed to add cert_enroll entry into sqlite database: %s",
-					    sqlite3_errmsg(ctx->db));
-			}
-			sqlite3_free(sql);
-		}
-	}
-
-	str = db_get_session_val(ctx, NULL, NULL, session_id,
-				 "mobile_identifier_hash");
-	if (str) {
-		sql = sqlite3_mprintf("DELETE FROM sim_provisioning WHERE mobile_identifier_hash=%Q",
-				      str);
-		if (sql) {
-			debug_print(ctx, 1, "DB: %s", sql);
-			if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
-			    SQLITE_OK) {
-				debug_print(ctx, 1,
-					    "Failed to delete pending sim_provisioning entry: %s",
-					    sqlite3_errmsg(ctx->db));
-			}
-			sqlite3_free(sql);
-		}
-		os_free(str);
-	}
-
-	if (ret == 0) {
-		hs20_eventlog(ctx, user, realm, session_id,
-			      "completed subscription registration", NULL);
-	}
-
-out:
-	free(user);
-	free(realm);
-	free(pw);
-	free(pw_mm);
-	free(pps);
-	free(cert_pem);
-	free(fingerprint);
-	free(osu_user);
-	free(osu_password);
-	free(eap_method);
-	os_free(policy);
-	return ret;
-}
-
-
-static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx,
-					     xml_node_t *node,
-					     const char *user,
-					     const char *realm,
-					     const char *session_id,
-					     int dmacc)
-{
-	char *status;
-	xml_node_t *ret;
-	char *val;
-	enum hs20_session_operation oper;
-
-	status = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
-					    "sppStatus");
-	if (status == NULL) {
-		debug_print(ctx, 1, "No sppStatus attribute");
-		return NULL;
-	}
-
-	debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s  sessionID: %s",
-		    status, session_id);
-
-	val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
-	if (!val) {
-		debug_print(ctx, 1,
-			    "No session active for sessionID: %s",
-			    session_id);
-		oper = NO_OPERATION;
-	} else
-		oper = atoi(val);
-
-	if (strcasecmp(status, "OK") == 0) {
-		char *new_pw = NULL;
-
-		xml_node_get_attr_value_free(ctx->xml, status);
-
-		if (oper == USER_REMEDIATION) {
-			new_pw = db_get_session_val(ctx, user, realm,
-						    session_id, "password");
-			if (new_pw == NULL || strlen(new_pw) == 0) {
-				free(new_pw);
-				ret = build_spp_exchange_complete(
-					ctx, session_id, "Error occurred",
-					"Other");
-				hs20_eventlog_node(ctx, user, realm,
-						   session_id, "No password "
-						   "had been assigned for "
-						   "session", ret);
-				db_remove_session(ctx, user, realm, session_id);
-				return ret;
-			}
-			oper = UPDATE_PASSWORD;
-		}
-		if (oper == UPDATE_PASSWORD) {
-			if (!new_pw) {
-				new_pw = db_get_session_val(ctx, user, realm,
-							    session_id,
-							    "password");
-				if (!new_pw) {
-					db_remove_session(ctx, user, realm,
-							  session_id);
-					return NULL;
-				}
-			}
-			debug_print(ctx, 1, "Update user '%s' password in DB",
-				    user);
-			if (update_password(ctx, user, realm, new_pw, dmacc) <
-			    0) {
-				debug_print(ctx, 1, "Failed to update user "
-					    "'%s' password in DB", user);
-				ret = build_spp_exchange_complete(
-					ctx, session_id, "Error occurred",
-					"Other");
-				hs20_eventlog_node(ctx, user, realm,
-						   session_id, "Failed to "
-						   "update database", ret);
-				db_remove_session(ctx, user, realm, session_id);
-				return ret;
-			}
-			hs20_eventlog(ctx, user, realm,
-				      session_id, "Updated user password "
-				      "in database", NULL);
-		}
-		if (oper == CLEAR_REMEDIATION) {
-			debug_print(ctx, 1,
-				    "Clear remediation requirement for user '%s' in DB",
-				    user);
-			if (clear_remediation(ctx, user, realm, dmacc) < 0) {
-				debug_print(ctx, 1,
-					    "Failed to clear remediation requirement for user '%s' in DB",
-					    user);
-				ret = build_spp_exchange_complete(
-					ctx, session_id, "Error occurred",
-					"Other");
-				hs20_eventlog_node(ctx, user, realm,
-						   session_id,
-						   "Failed to update database",
-						   ret);
-				db_remove_session(ctx, user, realm, session_id);
-				return ret;
-			}
-			hs20_eventlog(ctx, user, realm,
-				      session_id,
-				      "Cleared remediation requirement in database",
-				      NULL);
-		}
-		if (oper == SUBSCRIPTION_REGISTRATION) {
-			if (add_subscription(ctx, session_id) < 0) {
-				debug_print(ctx, 1, "Failed to add "
-					    "subscription into DB");
-				ret = build_spp_exchange_complete(
-					ctx, session_id, "Error occurred",
-					"Other");
-				hs20_eventlog_node(ctx, user, realm,
-						   session_id, "Failed to "
-						   "update database", ret);
-				db_remove_session(ctx, user, realm, session_id);
-				return ret;
-			}
-		}
-		if (oper == POLICY_REMEDIATION || oper == POLICY_UPDATE) {
-			char *val;
-			val = db_get_val(ctx, user, realm, "remediation",
-					 dmacc);
-			if (val && strcmp(val, "policy") == 0)
-				db_update_val(ctx, user, realm, "remediation",
-					      "", dmacc);
-			free(val);
-		}
-		if (oper == POLICY_UPDATE)
-			db_update_val(ctx, user, realm, "polupd_done", "1",
-				      dmacc);
-		if (oper == CERT_REENROLL) {
-			char *new_user;
-			char event[200];
-
-			new_user = db_get_session_val(ctx, NULL, NULL,
-						      session_id, "user");
-			if (!new_user) {
-				debug_print(ctx, 1,
-					    "Failed to find new user name (cert-serialnum)");
-				ret = build_spp_exchange_complete(
-					ctx, session_id, "Error occurred",
-					"Other");
-				hs20_eventlog_node(ctx, user, realm,
-						   session_id,
-						   "Failed to find new user name (cert reenroll)",
-						   ret);
-				db_remove_session(ctx, NULL, NULL, session_id);
-				return ret;
-			}
-
-			debug_print(ctx, 1,
-				    "Update certificate user entry to use the new serial number (old=%s new=%s)",
-				    user, new_user);
-			os_snprintf(event, sizeof(event), "renamed user to: %s",
-				    new_user);
-			hs20_eventlog(ctx, user, realm, session_id, event,
-				      NULL);
-
-			if (db_update_val(ctx, user, realm, "identity",
-					  new_user, 0) < 0 ||
-			    db_update_val(ctx, new_user, realm, "remediation",
-					  "", 0) < 0) {
-				debug_print(ctx, 1,
-					    "Failed to update user name (cert-serialnum)");
-				ret = build_spp_exchange_complete(
-					ctx, session_id, "Error occurred",
-					"Other");
-				hs20_eventlog_node(ctx, user, realm,
-						   session_id,
-						   "Failed to update user name (cert reenroll)",
-						   ret);
-				db_remove_session(ctx, NULL, NULL, session_id);
-				os_free(new_user);
-				return ret;
-			}
-
-			os_free(new_user);
-		}
-		ret = build_spp_exchange_complete(
-			ctx, session_id,
-			"Exchange complete, release TLS connection", NULL);
-		hs20_eventlog_node(ctx, user, realm, session_id,
-				   "Exchange completed", ret);
-		db_remove_session(ctx, NULL, NULL, session_id);
-		return ret;
-	}
-
-	ret = build_spp_exchange_complete(ctx, session_id, "Error occurred",
-					  "Other");
-	hs20_eventlog_node(ctx, user, realm, session_id, "Error occurred", ret);
-	db_remove_session(ctx, user, realm, session_id);
-	xml_node_get_attr_value_free(ctx->xml, status);
-	return ret;
-}
-
-
-#define SPP_SESSION_ID_LEN 16
-
-static char * gen_spp_session_id(void)
-{
-	FILE *f;
-	int i;
-	char *session;
-
-	session = os_malloc(SPP_SESSION_ID_LEN * 2 + 1);
-	if (session == NULL)
-		return NULL;
-
-	f = fopen("/dev/urandom", "r");
-	if (f == NULL) {
-		os_free(session);
-		return NULL;
-	}
-	for (i = 0; i < SPP_SESSION_ID_LEN; i++)
-		os_snprintf(session + i * 2, 3, "%02x", fgetc(f));
-
-	fclose(f);
-	return session;
-}
-
-xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
-				     const char *auth_user,
-				     const char *auth_realm, int dmacc)
-{
-	xml_node_t *ret = NULL;
-	char *session_id;
-	const char *op_name;
-	char *xml_err;
-	char fname[200];
-
-	debug_dump_node(ctx, "received request", node);
-
-	if (!dmacc && auth_user && auth_realm) {
-		char *real;
-		real = db_get_val(ctx, auth_user, auth_realm, "identity", 0);
-		if (!real) {
-			real = db_get_val(ctx, auth_user, auth_realm,
-					  "identity", 1);
-			if (real)
-				dmacc = 1;
-		}
-		os_free(real);
-	}
-
-	snprintf(fname, sizeof(fname), "%s/spp/spp.xsd", ctx->root_dir);
-	if (xml_validate(ctx->xml, node, fname, &xml_err) < 0) {
-		/*
-		 * We may not be able to extract the sessionID from invalid
-		 * input, but well, we can try.
-		 */
-		session_id = xml_node_get_attr_value_ns(ctx->xml, node,
-							SPP_NS_URI,
-							"sessionID");
-		debug_print(ctx, 1,
-			    "SPP message failed validation, xsd file: %s  xml-error: %s",
-			    fname, xml_err);
-		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
-				   "SPP message failed validation", node);
-		hs20_eventlog(ctx, auth_user, auth_realm, session_id,
-			      "Validation errors", xml_err);
-		os_free(xml_err);
-		xml_node_get_attr_value_free(ctx->xml, session_id);
-		/* TODO: what to return here? */
-		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
-					   "SppValidationError");
-		return ret;
-	}
-
-	session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
-						"sessionID");
-	if (session_id) {
-		char *tmp;
-		debug_print(ctx, 1, "Received sessionID %s", session_id);
-		tmp = os_strdup(session_id);
-		xml_node_get_attr_value_free(ctx->xml, session_id);
-		if (tmp == NULL)
-			return NULL;
-		session_id = tmp;
-	} else {
-		session_id = gen_spp_session_id();
-		if (session_id == NULL) {
-			debug_print(ctx, 1, "Failed to generate sessionID");
-			return NULL;
-		}
-		debug_print(ctx, 1, "Generated sessionID %s", session_id);
-	}
-
-	op_name = xml_node_get_localname(ctx->xml, node);
-	if (op_name == NULL) {
-		debug_print(ctx, 1, "Could not get op_name");
-		return NULL;
-	}
-
-	if (strcmp(op_name, "sppPostDevData") == 0) {
-		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
-				   "sppPostDevData received and validated",
-				   node);
-		ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
-					     session_id, dmacc);
-	} else if (strcmp(op_name, "sppUpdateResponse") == 0) {
-		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
-				   "sppUpdateResponse received and validated",
-				   node);
-		ret = hs20_spp_update_response(ctx, node, auth_user,
-					       auth_realm, session_id, dmacc);
-	} else {
-		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
-				   "Unsupported SPP message received and "
-				   "validated", node);
-		debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
-		/* TODO: what to return here? */
-		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
-					   "SppUnknownCommandError");
-	}
-	os_free(session_id);
-
-	if (ret == NULL) {
-		/* TODO: what to return here? */
-		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
-					   "SppInternalError");
-	}
-
-	return ret;
-}
-
-
-int hs20_spp_server_init(struct hs20_svc *ctx)
-{
-	char fname[200];
-	ctx->db = NULL;
-	snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
-	if (sqlite3_open(fname, &ctx->db)) {
-		printf("Failed to open sqlite database: %s\n",
-		       sqlite3_errmsg(ctx->db));
-		sqlite3_close(ctx->db);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-void hs20_spp_server_deinit(struct hs20_svc *ctx)
-{
-	sqlite3_close(ctx->db);
-	ctx->db = NULL;
-}
diff --git a/hs20/server/spp_server.h b/hs20/server/spp_server.h
deleted file mode 100644
index 421974c..0000000
--- a/hs20/server/spp_server.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Hotspot 2.0 SPP server
- * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef SPP_SERVER_H
-#define SPP_SERVER_H
-
-struct hs20_svc {
-	const void *ctx;
-	struct xml_node_ctx *xml;
-	char *root_dir;
-	FILE *debug_log;
-	sqlite3 *db;
-	const char *addr;
-	const char *test;
-	const char *imsi;
-	const char *eap_method;
-	const char *id_hash;
-};
-
-
-void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
-	__attribute__ ((format (printf, 3, 4)));
-void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node);
-
-xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
-				     const char *auth_user,
-				     const char *auth_realm, int dmacc);
-int hs20_spp_server_init(struct hs20_svc *ctx);
-void hs20_spp_server_deinit(struct hs20_svc *ctx);
-
-#endif /* SPP_SERVER_H */
diff --git a/hs20/server/sql-example.txt b/hs20/server/sql-example.txt
deleted file mode 100644
index 20dcf2f..0000000
--- a/hs20/server/sql-example.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','fqdn','example.com');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','friendly_name','Example Operator');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','spp_http_auth_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/spp-root-ca.der');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/aaa-root-ca.der');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_account','free');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','policy_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','remediation_url','https://subscription-server.osu.example.com/hs20/remediation.php?session_id=');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_remediation_url','https://subscription-server.osu.example.com/hs20/free-remediation.php?session_id=');
-INSERT INTO osu_config(realm,field,value) VALUES('example.com','signup_url','https://subscription-server.osu.example.com/hs20/signup.php?session_id=');
-
-
-INSERT INTO users(identity,realm,methods,password,phase2,shared) VALUES('free','example.com','TTLS-MSCHAPV2','free',1,1);
-
-INSERT INTO wildcards(identity,methods) VALUES('','TTLS,TLS');
diff --git a/hs20/server/sql.txt b/hs20/server/sql.txt
deleted file mode 100644
index 2cc6ede..0000000
--- a/hs20/server/sql.txt
+++ /dev/null
@@ -1,108 +0,0 @@
-CREATE TABLE eventlog(
-	user TEXT,
-	realm TEXT,
-	sessionid TEXT COLLATE NOCASE,
-	timestamp TEXT,
-	notes TEXT,
-	dump TEXT,
-	addr TEXT
-);
-
-CREATE TABLE sessions(
-	timestamp TEXT,
-	id TEXT COLLATE NOCASE,
-	user TEXT,
-	realm TEXT,
-	password TEXT,
-	machine_managed BOOLEAN,
-	operation INTEGER,
-	type TEXT,
-	pps TEXT,
-	redirect_uri TEXT,
-	devinfo TEXT,
-	devdetail TEXT,
-	cert TEXT,
-	cert_pem TEXT,
-	mac_addr TEXT,
-	osu_user TEXT,
-	osu_password TEXT,
-	eap_method TEXT,
-	mobile_identifier_hash TEXT,
-	test TEXT
-);
-
-CREATE index sessions_id_index ON sessions(id);
-
-CREATE TABLE osu_config(
-       realm TEXT,
-       field TEXT,
-       value TEXT
-);
-
-CREATE TABLE users(
-	identity TEXT PRIMARY KEY,
-	methods TEXT,
-	password TEXT,
-	machine_managed BOOLEAN,
-	remediation TEXT,
-	phase2 INTEGER,
-	realm TEXT,
-	policy TEXT,
-	devinfo TEXT,
-	devdetail TEXT,
-	pps TEXT,
-	fetch_pps INTEGER,
-	osu_user TEXT,
-	osu_password TEXT,
-	shared INTEGER,
-	cert TEXT,
-	cert_pem TEXT,
-	t_c_timestamp INTEGER,
-	mac_addr TEXT,
-	last_msk TEXT,
-	polupd_done TEXT,
-	subrem TEXT
-);
-
-CREATE TABLE wildcards(
-	identity TEXT PRIMARY KEY,
-	methods TEXT
-);
-
-CREATE TABLE authlog(
-	timestamp TEXT,
-	session TEXT,
-	nas_ip TEXT,
-	username TEXT,
-	note TEXT
-);
-
-CREATE TABLE pending_tc(
-	mac_addr TEXT PRIMARY KEY,
-	identity TEXT
-);
-
-CREATE TABLE current_sessions(
-	mac_addr TEXT PRIMARY KEY,
-	identity TEXT,
-	start_time TEXT,
-	nas TEXT,
-	hs20_t_c_filtering BOOLEAN,
-	waiting_coa_ack BOOLEAN,
-	coa_ack_received BOOLEAN
-);
-
-CREATE TABLE cert_enroll(
-	mac_addr TEXT PRIMARY KEY,
-	user TEXT,
-	realm TEXT,
-	serialnum TEXT
-);
-
-CREATE TABLE sim_provisioning(
-	mobile_identifier_hash TEXT PRIMARY KEY,
-	imsi TEXT,
-	mac_addr TEXT,
-	eap_method TEXT,
-	timestamp TEXT
-);
diff --git a/hs20/server/www/add-free.php b/hs20/server/www/add-free.php
deleted file mode 100644
index 1efc655..0000000
--- a/hs20/server/www/add-free.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (isset($_POST["id"]))
-  $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
-else
-  die("Missing session id");
-if (strlen($id) < 32)
-  die("Invalid session id");
-
-$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
-if ($row == false) {
-   die("Session not found");
-}
-
-$uri = $row['redirect_uri'];
-$rowid = $row['rowid'];
-$realm = $row['realm'];
-
-$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
-if (!$row || strlen($row['value']) == 0) {
-  die("Free account disabled");
-}
-
-$user = $row['value'];
-
-$row = $db->query("SELECT password FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
-if (!$row)
-  die("Free account not found");
-
-$pw = $row['password'];
-
-if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', machine_managed='1' WHERE rowid=$rowid")) {
-  die("Failed to update session database");
-}
-
-$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
-	"VALUES ('$user', '$realm', '$id', " .
-	"strftime('%Y-%m-%d %H:%M:%f','now'), " .
-	"'completed user input response for a new PPS MO')");
-
-header("Location: $uri", true, 302);
-
-?>
diff --git a/hs20/server/www/add-mo.php b/hs20/server/www/add-mo.php
deleted file mode 100644
index a3b4513..0000000
--- a/hs20/server/www/add-mo.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (isset($_POST["id"]))
-  $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
-else
-  die("Missing session id");
-
-$user = $_POST["user"];
-$pw = $_POST["password"];
-if (strlen($id) < 32 || !isset($user) || !isset($pw)) {
-  die("Invalid POST data");
-}
-
-if (strlen($user) < 1 || strncasecmp($user, "cert-", 5) == 0) {
-  echo "<html><body><p><red>Invalid username</red></p>\n";
-  echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
-  echo "</body></html>\n";
-  exit;
-}
-
-$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
-if ($row == false) {
-   die("Session not found");
-}
-$realm = $row['realm'];
-
-$userrow = $db->query("SELECT identity FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
-if ($userrow) {
-  echo "<html><body><p><red>Selected username is not available</red></p>\n";
-  echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
-  echo "</body></html>\n";
-  exit;
-}
-
-$uri = $row['redirect_uri'];
-$rowid = $row['rowid'];
-
-if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', type='password' WHERE rowid=$rowid")) {
-  die("Failed to update session database");
-}
-
-$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
-	"VALUES ('$user', '$realm', '$id', " .
-	"strftime('%Y-%m-%d %H:%M:%f','now'), " .
-	"'completed user input response for a new PPS MO')");
-
-header("Location: $uri", true, 302);
-
-?>
diff --git a/hs20/server/www/cert-enroll.php b/hs20/server/www/cert-enroll.php
deleted file mode 100644
index f023ca5..0000000
--- a/hs20/server/www/cert-enroll.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (isset($_GET["id"]))
-  $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
-else
-  die("Missing session id");
-if (strlen($id) < 32)
-  die("Invalid session id");
-
-$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
-if ($row == false) {
-   die("Session not found");
-}
-
-$uri = $row['redirect_uri'];
-$rowid = $row['rowid'];
-$realm = $row['realm'];
-
-$user = sha1(mt_rand());
-
-if (!$db->exec("UPDATE sessions SET user='$user', type='cert' WHERE rowid=$rowid")) {
-  die("Failed to update session database");
-}
-
-$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
-	"VALUES ('', '$realm', '$id', " .
-	"strftime('%Y-%m-%d %H:%M:%f','now'), " .
-	"'completed user input response for client certificate enrollment')");
-
-header("Location: $uri", true, 302);
-
-?>
diff --git a/hs20/server/www/config.php b/hs20/server/www/config.php
deleted file mode 100644
index 4272b10..0000000
--- a/hs20/server/www/config.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-$osu_root = "/home/user/hs20-server";
-$osu_db = "sqlite:$osu_root/AS/DB/eap_user.db";
-$t_c_file = "$osu_root/terms-and-conditions";
-$t_c_timestamp = 123456789;
-$hostapd_ctrl = "udg:///home/user/hs20-server/AS/ctrl/as"
-?>
diff --git a/hs20/server/www/est.php b/hs20/server/www/est.php
deleted file mode 100644
index b7fb260..0000000
--- a/hs20/server/www/est.php
+++ /dev/null
@@ -1,232 +0,0 @@
-<?php
-
-require('config.php');
-
-$params = explode("/", $_SERVER["PATH_INFO"], 3);
-$realm = $params[1];
-$cmd = $params[2];
-$method = $_SERVER["REQUEST_METHOD"];
-
-unset($user);
-unset($rowid);
-
-$db = new PDO($osu_db);
-if (!$db) {
-  error_log("EST: Could not access database");
-  die("Could not access database");
-}
-
-if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
-  $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
-		  'uri'=>1, 'response'=>1);
-  $data = array();
-  $keys = implode('|', array_keys($needed));
-  preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
-		 $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
-  foreach ($matches as $m) {
-    $data[$m[1]] = $m[3] ? $m[3] : $m[4];
-    unset($needed[$m[1]]);
-  }
-  if ($needed) {
-    error_log("EST: Missing auth parameter");
-    die('Authentication failed');
-  }
-  $user = $data['username'];
-  if (strlen($user) < 1) {
-    error_log("EST: Empty username");
-    die('Authentication failed');
-  }
-
-  $sql = "SELECT rowid,password,operation FROM sessions " .
-    "WHERE user='$user' AND realm='$realm'";
-  $q = $db->query($sql);
-  if (!$q) {
-    error_log("EST: Session not found for user=$user realm=$realm");
-    die("Session not found");
-  }
-  $row = $q->fetch();
-  if (!$row) {
-    error_log("EST: Session fetch failed for user=$user realm=$realm");
-    die('Session not found');
-  }
-  $rowid = $row['rowid'];
-
-  $oper = $row['operation'];
-  if ($oper != '5') {
-    error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
-    die("Session not found");
-  }
-  $pw = $row['password'];
-  if (strlen($pw) < 1) {
-    error_log("EST: Empty password for user=$user realm=$realm");
-    die('Authentication failed');
-  }
-
-  $A1 = md5($user . ':' . $realm . ':' . $pw);
-  $A2 = md5($method . ':' . $data['uri']);
-  $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
-	      $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
-  if ($data['response'] != $resp) {
-    error_log("EST: Incorrect authentication response for user=$user realm=$realm");
-    die('Authentication failed');
-  }
-} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
-	   $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
-	   isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
-  $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
-  $sql = "SELECT rowid,password,operation FROM sessions " .
-    "WHERE user='$user' AND realm='$realm'";
-  $q = $db->query($sql);
-  if (!$q) {
-    error_log("EST: Session not found for user=$user realm=$realm");
-    die("Session not found");
-  }
-  $row = $q->fetch();
-  if (!$row) {
-    error_log("EST: Session fetch failed for user=$user realm=$realm");
-    die('Session not found');
-  }
-  $rowid = $row['rowid'];
-
-  $oper = $row['operation'];
-  if ($oper != '10') {
-    error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
-    die("Session not found");
-  }
-}
-
-
-if ($method == "GET" && $cmd == "cacerts") {
-  $fname = "$osu_root/est/$realm-cacerts.pkcs7";
-  if (!file_exists($fname)) {
-    error_log("EST: cacerts - unknown realm $realm");
-    die("Unknown realm");
-  }
-
-  header("Content-Transfer-Encoding: base64");
-  header("Content-Type: application/pkcs7-mime");
-
-  $data = file_get_contents($fname);
-  echo wordwrap(base64_encode($data), 72, "\n", true);
-  echo "\n";
-  error_log("EST: cacerts");
-} else if ($method == "GET" && $cmd == "csrattrs") {
-  header("Content-Transfer-Encoding: base64");
-  header("Content-Type: application/csrattrs");
-  readfile("$osu_root/est/est-attrs.b64");
-  error_log("EST: csrattrs");
-} else if ($method == "POST" &&
-           ($cmd == "simpleenroll" || $cmd == "simplereenroll")) {
-  $reenroll = $cmd == "simplereenroll";
-  if (!$reenroll && (!isset($user) || strlen($user) == 0)) {
-    header('HTTP/1.1 401 Unauthorized');
-    header('WWW-Authenticate: Digest realm="'.$realm.
-	   '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
-    error_log("EST: simpleenroll - require authentication");
-    die('Authentication required');
-  }
-  if ($reenroll &&
-      (!isset($user) ||
-       !isset($_SERVER["SSL_CLIENT_VERIFY"]) ||
-       $_SERVER["SSL_CLIENT_VERIFY"] != "SUCCESS")) {
-    header('HTTP/1.1 403 Forbidden');
-    error_log("EST: simplereenroll - require certificate authentication");
-    die('Authentication required');
-  }
-  if (!isset($_SERVER["CONTENT_TYPE"])) {
-    error_log("EST: simpleenroll without Content-Type");
-    die("Missing Content-Type");
-  }
-  if (!stristr($_SERVER["CONTENT_TYPE"], "application/pkcs10")) {
-    error_log("EST: simpleenroll - unexpected Content-Type: " .
-	      $_SERVER["CONTENT_TYPE"]);
-    die("Unexpected Content-Type");
-  }
-
-  $data = file_get_contents("php://input");
-  error_log("EST: simpleenroll - POST data from php://input: " . $data);
-  $req = base64_decode($data);
-  if ($req == FALSE) {
-    error_log("EST: simpleenroll - Invalid base64-encoded PKCS#10 data");
-    die("Invalid base64-encoded PKCS#10 data");
-  }
-  $cadir = "$osu_root/est";
-  $reqfile = "$cadir/tmp/cert-req.pkcs10";
-  $f = fopen($reqfile, "wb");
-  fwrite($f, $req);
-  fclose($f);
-
-  $req_pem = "$reqfile.pem";
-  if (file_exists($req_pem))
-    unlink($req_pem);
-  exec("openssl req -in $reqfile -inform DER -out $req_pem -outform PEM");
-  if (!file_exists($req_pem)) {
-    error_log("EST: simpleenroll - Failed to parse certificate request");
-    die("Failed to parse certificate request");
-  }
-
-  /* FIX: validate request and add HS 2.0 extensions to cert */
-  $cert_pem = "$cadir/tmp/req-signed.pem";
-  if (file_exists($cert_pem))
-    unlink($cert_pem);
-  exec("openssl x509 -req -in $req_pem -CAkey $cadir/cakey.pem -out $cert_pem -CA $cadir/cacert.pem -CAserial $cadir/serial -days 365 -text");
-  if (!file_exists($cert_pem)) {
-    error_log("EST: simpleenroll - Failed to sign certificate");
-    die("Failed to sign certificate");
-  }
-
-  $cert = file_get_contents($cert_pem);
-  $handle = popen("openssl x509 -in $cert_pem -serial -noout", "r");
-  $serial = fread($handle, 200);
-  pclose($handle);
-  $pattern = "/serial=(?P<snhex>[0-9a-fA-F:]*)/m";
-  preg_match($pattern, $serial, $matches);
-  if (!isset($matches['snhex']) || strlen($matches['snhex']) < 1) {
-    error_log("EST: simpleenroll - Could not get serial number");
-    die("Could not get serial number");
-  }
-  $sn = str_replace(":", "", strtoupper($matches['snhex']));
-
-  $user = "cert-$sn";
-  error_log("EST: user = $user");
-
-  $cert_der = "$cadir/tmp/req-signed.der";
-  if (file_exists($cert_der))
-    unlink($cert_der);
-  exec("openssl x509 -in $cert_pem -inform PEM -out $cert_der -outform DER");
-  if (!file_exists($cert_der)) {
-    error_log("EST: simpleenroll - Failed to convert certificate");
-    die("Failed to convert certificate");
-  }
-  $der = file_get_contents($cert_der);
-  $fingerprint = hash("sha256", $der);
-  error_log("EST: sha256(DER cert): $fingerprint");
-
-  $pkcs7 = "$cadir/tmp/est-client.pkcs7";
-  if (file_exists($pkcs7))
-    unlink($pkcs7);
-  exec("openssl crl2pkcs7 -nocrl -certfile $cert_pem -out $pkcs7 -outform DER");
-  if (!file_exists($pkcs7)) {
-    error_log("EST: simpleenroll - Failed to prepare PKCS#7 file");
-    die("Failed to prepare PKCS#7 file");
-  }
-
-  if (!$db->exec("UPDATE sessions SET user='$user', cert='$fingerprint', cert_pem='$cert' WHERE rowid=$rowid")) {
-    error_log("EST: simpleenroll - Failed to update session database");
-    die("Failed to update session database");
-  }
-
-  header("Content-Transfer-Encoding: base64");
-  header("Content-Type: application/pkcs7-mime");
-
-  $data = file_get_contents($pkcs7);
-  $resp = wordwrap(base64_encode($data), 72, "\n", true);
-  echo $resp . "\n";
-  error_log("EST: simpleenroll - PKCS#7 response: " . $resp);
-} else {
-  header("HTTP/1.0 404 Not Found");
-  error_log("EST: Unexpected method or path");
-  die("Unexpected method or path");
-}
-
-?>
diff --git a/hs20/server/www/free-remediation.php b/hs20/server/www/free-remediation.php
deleted file mode 100644
index 5648b30..0000000
--- a/hs20/server/www/free-remediation.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<html>
-<head>
-<title>Hotspot 2.0 - public and free hotspot - remediation</title>
-</head>
-<body>
-
-<h3>Hotspot 2.0 - public and free hotspot</h3>
-
-<p>Terms and conditions have changed. You need to accept the new terms
-to continue using this network.</p>
-
-<p>Terms and conditions..</p>
-
-<?php
-echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Accept</a><br>\n";
-?>
-
-</body>
-</html>
diff --git a/hs20/server/www/free.php b/hs20/server/www/free.php
deleted file mode 100644
index 8195069..0000000
--- a/hs20/server/www/free.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<html>
-<head>
-<title>Hotspot 2.0 - public and free hotspot</title>
-</head>
-<body>
-
-<?php
-
-$id = $_GET["session_id"];
-
-echo "<h3>Hotspot 2.0 - public and free hotspot</h3>\n";
-
-echo "<form action=\"add-free.php\" method=\"POST\">\n";
-echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
-
-?>
-
-<p>Terms and conditions..</p>
-<input type="submit" value="Accept">
-</form>
-
-</body>
-</html>
diff --git a/hs20/server/www/redirect.php b/hs20/server/www/redirect.php
deleted file mode 100644
index 8fc9cd6..0000000
--- a/hs20/server/www/redirect.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (isset($_GET["id"]))
-	$id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
-else
-	$id = 0;
-
-$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
-if ($row == false) {
-   die("Session not found");
-}
-
-$uri = $row['redirect_uri'];
-
-header("Location: $uri", true, 302);
-
-$user = $row['user'];
-$realm = $row['realm'];
-
-$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
-	  "VALUES ('$user', '$realm', '$id', " .
-	  "strftime('%Y-%m-%d %H:%M:%f','now'), " .
-	  "'redirected after user input')");
-
-?>
diff --git a/hs20/server/www/remediation-pw.php b/hs20/server/www/remediation-pw.php
deleted file mode 100644
index 76fdccb..0000000
--- a/hs20/server/www/remediation-pw.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (isset($_POST["id"]))
-  $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
-else
-  die("Missing session id");
-
-$pw = $_POST["password"];
-if (strlen($id) < 32 || !isset($pw)) {
-  die("Invalid POST data");
-}
-
-$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
-if ($row == false) {
-   die("Session not found");
-}
-$user = $row['user'];
-$realm = $row['realm'];
-
-$uri = $row['redirect_uri'];
-$rowid = $row['rowid'];
-
-if (!$db->exec("UPDATE sessions SET password='$pw' WHERE rowid=$rowid")) {
-  die("Failed to update session database");
-}
-
-$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
-	"VALUES ('$user', '$realm', '$id', " .
-	"strftime('%Y-%m-%d %H:%M:%f','now'), " .
-	"'completed user input response for subscription remediation')");
-
-header("Location: $uri", true, 302);
-
-?>
diff --git a/hs20/server/www/remediation.php b/hs20/server/www/remediation.php
deleted file mode 100644
index 3628065..0000000
--- a/hs20/server/www/remediation.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<html>
-<head>
-<title>Hotspot 2.0 subscription remediation</title>
-</head>
-<body>
-
-<?php
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (isset($_GET["session_id"]))
-	$id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["session_id"]);
-else
-	$id = 0;
-echo "SessionID: " . $id . "<br>\n";
-
-$row = $db->query("SELECT * FROM sessions WHERE id='$id'")->fetch();
-if ($row == false) {
-   die("Session not found");
-}
-
-$username = $row['user'];
-echo "User: " . $username . "@" . $row['realm'] . "<br>\n";
-
-$user = $db->query("SELECT machine_managed,methods FROM users WHERE identity='$username'")->fetch();
-if ($user == false) {
-   die("User not found");
-}
-
-echo "<hr><br>\n";
-
-$cert = $user['methods'] == "TLS" || strncmp($username, "cert-", 5) == 0;
-
-if ($cert) {
-   echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
-} else if ($user['machine_managed'] == "1") {
-   echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
-   echo "This will provide a new machine-generated password.<br>\n";
-} else {
-   echo "<form action=\"remediation-pw.php\" method=\"POST\">\n";
-   echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
-   echo "New password: <input type=\"password\" name=\"password\"><br>\n";
-   echo "<input type=\"submit\" value=\"Change password\">\n";
-   echo "</form>\n";
-}
-
-?>
-
-</body>
-</html>
diff --git a/hs20/server/www/signup.php b/hs20/server/www/signup.php
deleted file mode 100644
index 80a9d40..0000000
--- a/hs20/server/www/signup.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<html>
-<head>
-<title>Hotspot 2.0 signup</title>
-</head>
-<body>
-
-<?php
-
-$id = $_GET["session_id"];
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-$row = $db->query("SELECT realm,test FROM sessions WHERE id='$id'")->fetch();
-if ($row == false) {
-   die("Session not found for id: $id");
-}
-$realm = $row['realm'];
-$test = $row['test'];
-
-if (strlen($test) > 0) {
-  echo "<p style=\"color:#FF0000\">Special test functionality: $test</red></big></p>\n";
-}
-
-echo "<h3>Sign up for a subscription - $realm</h3>\n";
-
-echo "<p>This page can be used to select between three different types of subscriptions for testing purposes.</p>\n";
-
-echo "<h4>Option 1 - shared free access credential</h4>\n";
-
-$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
-if ($row && strlen($row['value']) > 0) {
-  echo "<p><a href=\"free.php?session_id=$id\">Sign up for free access</a></p>\n";
-}
-
-echo "<h4>Option 2 - username/password credential</h4>\n";
-
-echo "<form action=\"add-mo.php\" method=\"POST\">\n";
-echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
-?>
-Select a username and password. Leave password empty to get automatically
-generated and machine managed password.<br>
-Username: <input type="text" name="user"><br>
-Password: <input type="password" name="password"><br>
-<input type="submit" value="Complete subscription registration">
-</form>
-
-<?php
-echo "<h4>Option 3 - client certificate credential</h4>\n";
-
-echo "<p><a href=\"cert-enroll.php?id=$id\">Enroll a client certificate</a></p>\n"
-?>
-
-</body>
-</html>
diff --git a/hs20/server/www/spp.php b/hs20/server/www/spp.php
deleted file mode 100644
index c56d3d6..0000000
--- a/hs20/server/www/spp.php
+++ /dev/null
@@ -1,168 +0,0 @@
-<?php
-
-require('config.php');
-
-if (!stristr($_SERVER["CONTENT_TYPE"], "application/soap+xml")) {
-  error_log("spp.php - Unexpected Content-Type " . $_SERVER["CONTENT_TYPE"]);
-  die("Unexpected Content-Type");
-}
-
-if ($_SERVER["REQUEST_METHOD"] != "POST") {
-  error_log("spp.php - Unexpected method " . $_SERVER["REQUEST_METHOD"]);
-  die("Unexpected method");
-}
-
-if (isset($_GET["realm"])) {
-  $realm = $_GET["realm"];
-  $realm = PREG_REPLACE("/[^0-9a-zA-Z\.\-]/i", '', $realm);
-} else {
-  error_log("spp.php - Realm not specified");
-  die("Realm not specified");
-}
-
-if (isset($_GET["test"]))
-  $test = PREG_REPLACE("/[^0-9a-zA-Z\_\-]/i", '', $_GET["test"]);
-else
-  $test = "";
-
-unset($user);
-putenv("HS20CERT");
-
-if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
-  $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
-		  'uri'=>1, 'response'=>1);
-  $data = array();
-  $keys = implode('|', array_keys($needed));
-  preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
-		 $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
-  foreach ($matches as $m) {
-    $data[$m[1]] = $m[3] ? $m[3] : $m[4];
-    unset($needed[$m[1]]);
-  }
-  if ($needed) {
-    error_log("spp.php - Authentication failed - missing: " . print_r($needed));
-    die('Authentication failed');
-  }
-  $user = $data['username'];
-  if (strlen($user) < 1) {
-    error_log("spp.php - Authentication failed - empty username");
-    die('Authentication failed');
-  }
-
-
-  $db = new PDO($osu_db);
-  if (!$db) {
-    error_log("spp.php - Could not access database");
-    die("Could not access database");
-  }
-  $row = $db->query("SELECT password FROM users " .
-		    "WHERE identity='$user' AND realm='$realm'")->fetch();
-  if (!$row) {
-    $row = $db->query("SELECT osu_password FROM users " .
-		      "WHERE osu_user='$user' AND realm='$realm'")->fetch();
-    $pw = $row['osu_password'];
-  } else
-    $pw = $row['password'];
-  if (!$row) {
-    error_log("spp.php - Authentication failed - user '$user' not found");
-    die('Authentication failed');
-  }
-  if (strlen($pw) < 1) {
-    error_log("spp.php - Authentication failed - empty password");
-    die('Authentication failed');
-  }
-
-  $A1 = md5($user . ':' . $realm . ':' . $pw);
-  $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
-  $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
-	      $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
-  if ($data['response'] != $resp) {
-    error_log("Authentication failure - response mismatch");
-    die('Authentication failed');
-  }
-} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
-	   $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
-	   isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
-  $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
-  putenv("HS20CERT=yes");
-} else if (isset($_GET["hotspot2dot0-mobile-identifier-hash"])) {
-  $id_hash = $_GET["hotspot2dot0-mobile-identifier-hash"];
-  $id_hash = PREG_REPLACE("/[^0-9a-h]/i", '', $id_hash);
-
-  $db = new PDO($osu_db);
-  if (!$db) {
-    error_log("spp.php - Could not access database");
-    die("Could not access database");
-  }
-
-  $row = $db->query("SELECT * FROM sim_provisioning " .
-		    "WHERE mobile_identifier_hash='$id_hash'")->fetch();
-  if (!$row) {
-    error_log("spp.php - SIM provisioning failed - mobile_identifier_hash not found");
-    die('SIM provisioning failed - mobile_identifier_hash not found');
-  }
-
-  $imsi = $row['imsi'];
-  $mac_addr = $row['mac_addr'];
-  $eap_method = $row['eap_method'];
-
-  $row = $db->query("SELECT COUNT(*) FROM osu_config " .
-		    "WHERE realm='$realm'")->fetch();
-  if (!$row || intval($row[0]) < 1) {
-    error_log("spp.php - SIM provisioning failed - realm $realm not found");
-    die('SIM provisioning failed');
-  }
-
-  error_log("spp.php - SIM provisioning for IMSI $imsi");
-  putenv("HS20SIMPROV=yes");
-  putenv("HS20IMSI=$imsi");
-  putenv("HS20MACADDR=$mac_addr");
-  putenv("HS20EAPMETHOD=$eap_method");
-  putenv("HS20IDHASH=$id_hash");
-} else if (!isset($_SERVER["PATH_INFO"]) ||
-	   $_SERVER["PATH_INFO"] != "/signup") {
-  header('HTTP/1.1 401 Unauthorized');
-  header('WWW-Authenticate: Digest realm="'.$realm.
-	 '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
-  error_log("spp.php - Authentication required (not signup)");
-  die('Authentication required (not signup)');
-}
-
-
-if (isset($user) && strlen($user) > 0)
-  putenv("HS20USER=$user");
-else
-  putenv("HS20USER");
-
-putenv("HS20REALM=$realm");
-$postdata = file_get_contents("php://input");
-putenv("HS20POST=$postdata");
-$addr = $_SERVER["REMOTE_ADDR"];
-putenv("HS20ADDR=$addr");
-putenv("HS20TEST=$test");
-
-$last = exec("$osu_root/spp/hs20_spp_server -r$osu_root -f/tmp/hs20_spp_server.log", $output, $ret);
-
-if ($ret == 2) {
-  if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
-    header('HTTP/1.1 401 Unauthorized');
-    header('WWW-Authenticate: Digest realm="'.$realm.
-           '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
-    error_log("spp.php - Authentication required (ret 2)");
-    die('Authentication required');
-  } else {
-    error_log("spp.php - Unexpected authentication error");
-    die("Unexpected authentication error");
-  }
-}
-if ($ret != 0) {
-  error_log("spp.php - Failed to process SPP request");
-  die("Failed to process SPP request");
-}
-//error_log("spp.php: Response: " . implode($output));
-
-header("Content-Type: application/soap+xml");
-
-echo implode($output);
-
-?>
diff --git a/hs20/server/www/terms.php b/hs20/server/www/terms.php
deleted file mode 100644
index acba23e..0000000
--- a/hs20/server/www/terms.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-require('config.php');
-
-function print_header()
-{
-   echo "<html>\n";
-   echo "<head><title>HS 2.0 Terms and Conditions</title></head>\n";
-   echo "<body>\n";
-}
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (!isset($_GET["addr"])) {
-   die("Missing addr parameter");
-}
-$addr = $_GET["addr"];
-
-$accept = isset($_GET["accept"]) && $_GET["accept"] == "yes";
-
-$res = $db->prepare("SELECT identity FROM pending_tc WHERE mac_addr=?");
-$res->execute(array($addr));
-$row = $res->fetch();
-if (!$row) {
-   die("No pending session for the specified MAC address");
-}
-$identity = $row[0];
-
-if (!$accept) {
-   print_header();
-
-   echo "<p>Accept the following terms and conditions by clicking here: <a href=\"terms.php?addr=$addr&accept=yes\">Accept</a></p>\n<hr>\n";
-   readfile($t_c_file);
-} else {
-   $res = $db->prepare("UPDATE users SET t_c_timestamp=? WHERE identity=?");
-   if (!$res->execute(array($t_c_timestamp, $identity))) {
-      die("Failed to update user account.");
-   }
-
-   $res = $db->prepare("DELETE FROM pending_tc WHERE mac_addr=?");
-   $res->execute(array($addr));
-
-   $fp = fsockopen($hostapd_ctrl);
-   if (!$fp) {
-      die("Could not connect to hostapd(AS)");
-   }
-
-   fwrite($fp, "DAC_REQUEST coa $addr t_c_clear");
-   fclose($fp);
-
-   $waiting = true;
-   $ack = false;
-   for ($i = 1; $i <= 10; $i++) {
-      $res = $db->prepare("SELECT waiting_coa_ack,coa_ack_received FROM current_sessions WHERE mac_addr=?");
-      $res->execute(array($addr));
-      $row = $res->fetch();
-      if (!$row) {
-         die("No current session for the specified MAC address");
-      }
-      if (strlen($row[0]) > 0)
-            $waiting = $row[0] == 1;
-      if (strlen($row[1]) > 0)
-            $ack = $row[1] == 1;
-      $res->closeCursor();
-      if (!$waiting)
-         break;
-      sleep(1);
-   }
-   if ($ack) {
-      header('X-WFA-Hotspot20-Filtering: removed');
-      print_header();
-      echo "<p>Terms and conditions were accepted.</p>\n";
-
-      echo "<P>Filtering disabled.</P>\n";
-   } else {
-      print_header();
-      echo "<P>Failed to disable filtering.</P>\n";
-   }
-}
-
-?>
-
-</body>
-</html>
diff --git a/hs20/server/www/users.php b/hs20/server/www/users.php
deleted file mode 100644
index 2bd5552..0000000
--- a/hs20/server/www/users.php
+++ /dev/null
@@ -1,377 +0,0 @@
-<?php
-
-require('config.php');
-
-$db = new PDO($osu_db);
-if (!$db) {
-   die($sqliteerror);
-}
-
-if (isset($_GET["id"])) {
-	$id = $_GET["id"];
-	if (!is_numeric($id))
-		$id = 0;
-} else
-	$id = 0;
-if (isset($_GET["cmd"]))
-	$cmd = $_GET["cmd"];
-else
-	$cmd = '';
-
-if ($cmd == 'eventlog' && $id > 0) {
-	$row = $db->query("SELECT dump FROM eventlog WHERE rowid=$id")->fetch();
-	$dump = $row['dump'];
-	if ($dump[0] == '<') {
-	  header("Content-type: text/xml");
-	  echo "<?xml version=\"1.0\"?>\n";
-	  echo $dump;
-	} else {
-	  header("Content-type: text/plain");
-	  echo $dump;
-	}
-	exit;
-}
-
-if ($cmd == 'mo' && $id > 0) {
-	$mo = $_GET["mo"];
-	if (!isset($mo))
-		exit;
-	if ($mo != "devinfo" && $mo != "devdetail" && $mo != "pps")
-		exit;
-	$row = $db->query("SELECT $mo FROM users WHERE rowid=$id")->fetch();
-	header("Content-type: text/xml");
-	echo "<?xml version=\"1.0\"?>\n";
-	echo $row[$mo];
-	exit;
-}
-
-if ($cmd == 'cert' && $id > 0) {
-	$row = $db->query("SELECT cert_pem FROM users WHERE rowid=$id")->fetch();
-	header("Content-type: text/plain");
-	echo $row['cert_pem'];
-	exit;
-}
-
-?>
-
-<html>
-<head><title>HS 2.0 users</title></head>
-<body>
-
-<?php
-
-if ($cmd == 'subrem-clear' && $id > 0) {
-	$db->exec("UPDATE users SET remediation='' WHERE rowid=$id");
-}
-if ($cmd == 'subrem-add-user' && $id > 0) {
-	$db->exec("UPDATE users SET remediation='user' WHERE rowid=$id");
-}
-if ($cmd == 'subrem-add-machine' && $id > 0) {
-	$db->exec("UPDATE users SET remediation='machine' WHERE rowid=$id");
-}
-if ($cmd == 'subrem-add-reenroll' && $id > 0) {
-	$db->exec("UPDATE users SET remediation='reenroll' WHERE rowid=$id");
-}
-if ($cmd == 'subrem-add-policy' && $id > 0) {
-	$db->exec("UPDATE users SET remediation='policy' WHERE rowid=$id");
-}
-if ($cmd == 'subrem-add-free' && $id > 0) {
-	$db->exec("UPDATE users SET remediation='free' WHERE rowid=$id");
-}
-if ($cmd == 'fetch-pps-on' && $id > 0) {
-	$db->exec("UPDATE users SET fetch_pps=1 WHERE rowid=$id");
-}
-if ($cmd == 'fetch-pps-off' && $id > 0) {
-	$db->exec("UPDATE users SET fetch_pps=0 WHERE rowid=$id");
-}
-if ($cmd == 'reset-pw' && $id > 0) {
-	$db->exec("UPDATE users SET password='ChangeMe' WHERE rowid=$id");
-}
-if ($cmd == "policy" && $id > 0 && isset($_GET["policy"])) {
-	$policy = $_GET["policy"];
-	if ($policy == "no-policy" ||
-	    is_readable("$osu_root/spp/policy/$policy.xml")) {
-		$db->exec("UPDATE users SET policy='$policy' WHERE rowid=$id");
-	}
-}
-if ($cmd == "account-type" && $id > 0 && isset($_GET["type"])) {
-	$type = $_GET["type"];
-	if ($type == "shared")
-		$db->exec("UPDATE users SET shared=1 WHERE rowid=$id");
-	if ($type == "default")
-		$db->exec("UPDATE users SET shared=0 WHERE rowid=$id");
-}
-
-if ($cmd == "set-osu-cred" && $id > 0) {
-  $osu_user = $_POST["osu_user"];
-  $osu_password = $_POST["osu_password"];
-  if (strlen($osu_user) == 0)
-    $osu_password = "";
-  $db->exec("UPDATE users SET osu_user='$osu_user', osu_password='$osu_password' WHERE rowid=$id");
-}
-
-if ($cmd == 'clear-t-c' && $id > 0) {
-	$db->exec("UPDATE users SET t_c_timestamp=NULL WHERE rowid=$id");
-}
-
-$dump = 0;
-
-if ($id > 0) {
-
-if (isset($_GET["dump"])) {
-	$dump = $_GET["dump"];
-	if (!is_numeric($dump))
-		$dump = 0;
-} else
-	$dump = 0;
-
-echo "[<a href=\"users.php\">All users</a>] ";
-if ($dump == 0)
-	echo "[<a href=\"users.php?id=$id&dump=1\">Include debug dump</a>] ";
-else
-	echo "[<a href=\"users.php?id=$id\">Without debug dump</a>] ";
-echo "<br>\n";
-
-$row = $db->query("SELECT rowid,* FROM users WHERE rowid=$id")->fetch();
-
-echo "<H3>" . $row['identity'] . "@" . $row['realm'] . "</H3>\n";
-
-echo "MO: ";
-if (strlen($row['devinfo']) > 0) {
-	echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devinfo\">DevInfo</a>]\n";
-}
-if (strlen($row['devdetail']) > 0) {
-	echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devdetail\">DevDetail</a>]\n";
-}
-if (strlen($row['pps']) > 0) {
-	echo "[<a href=\"users.php?cmd=mo&id=$id&mo=pps\">PPS</a>]\n";
-}
-if (strlen($row['cert_pem']) > 0) {
-	echo "[<a href=\"users.php?cmd=cert&id=$id\">Certificate</a>]\n";
-}
-echo "<BR>\n";
-
-echo "Fetch PPS MO: ";
-if ($row['fetch_pps'] == "1") {
-	echo "On next connection " .
-		"[<a href=\"users.php?cmd=fetch-pps-off&id=$id\">" .
-		"do not fetch</a>]<br>\n";
-} else {
-	echo "Do not fetch " .
-		"[<a href=\"users.php?cmd=fetch-pps-on&id=$id\">" .
-		"request fetch</a>]<br>\n";
-}
-
-$cert = $row['cert'];
-if (strlen($cert) > 0) {
-  echo "Certificate fingerprint: $cert<br>\n";
-}
-
-echo "Remediation: ";
-$rem = $row['remediation'];
-if ($rem == "") {
-	echo "Not required";
-	echo " [<a href=\"users.php?cmd=subrem-add-user&id=" .
-		   $row['rowid'] . "\">add:user</a>]";
-	echo " [<a href=\"users.php?cmd=subrem-add-machine&id=" .
-		   $row['rowid'] . "\">add:machine</a>]";
-	if ($row['methods'] == 'TLS') {
-		echo " [<a href=\"users.php?cmd=subrem-add-reenroll&id=" .
-			   $row['rowid'] . "\">add:reenroll</a>]";
-	}
-	echo " [<a href=\"users.php?cmd=subrem-add-policy&id=" .
-		   $row['rowid'] . "\">add:policy</a>]";
-	echo " [<a href=\"users.php?cmd=subrem-add-free&id=" .
-		   $row['rowid'] . "\">add:free</a>]";
-} else if ($rem == "user") {
-	echo "User [<a href=\"users.php?cmd=subrem-clear&id=" .
-		       $row['rowid'] . "\">clear</a>]";
-} else if ($rem == "policy") {
-	echo "Policy [<a href=\"users.php?cmd=subrem-clear&id=" .
-		       $row['rowid'] . "\">clear</a>]";
-} else if ($rem == "free") {
-	echo "Free [<a href=\"users.php?cmd=subrem-clear&id=" .
-		       $row['rowid'] . "\">clear</a>]";
-} else if ($rem == "reenroll") {
-	echo "Reenroll [<a href=\"users.php?cmd=subrem-clear&id=" .
-		       $row['rowid'] . "\">clear</a>]";
-} else  {
-	echo "Machine [<a href=\"users.php?cmd=subrem-clear&id=" .
-			  $row['rowid'] . "\">clear</a>]";
-}
-echo "<br>\n";
-
-if (strncmp($row['identity'], "cert-", 5) != 0)
-   echo "Machine managed: " . ($row['machine_managed'] == "1" ? "TRUE" : "FALSE") . "<br>\n";
-
-echo "<form>Policy: <select name=\"policy\" " .
-	"onChange=\"window.location='users.php?cmd=policy&id=" .
-	$row['rowid'] . "&policy=' + this.value;\">\n";
-echo "<option value=\"" . $row['policy'] . "\" selected>" . $row['policy'] .
-      "</option>\n";
-$files = scandir("$osu_root/spp/policy");
-foreach ($files as $file) {
-	if (!preg_match("/.xml$/", $file))
-		continue;
-	if ($file == $row['policy'] . ".xml")
-		continue;
-	$p = substr($file, 0, -4);
-	echo "<option value=\"$p\">$p</option>\n";
-}
-echo "<option value=\"no-policy\">no policy</option>\n";
-echo "</select></form>\n";
-
-echo "<form>Account type: <select name=\"type\" " .
-	"onChange=\"window.location='users.php?cmd=account-type&id=" .
-	$row['rowid'] . "&type=' + this.value;\">\n";
-if ($row['shared'] > 0) {
-  $default_sel = "";
-  $shared_sel = " selected";
-} else {
-  $default_sel = " selected";
-  $shared_sel = "";
-}
-echo "<option value=\"default\"$default_sel>default</option>\n";
-echo "<option value=\"shared\"$shared_sel>shared</option>\n";
-echo "</select></form>\n";
-
-echo "Phase 2 method(s): " . $row['methods'] . "<br>\n";
-
-echo "<br>\n";
-echo "<a href=\"users.php?cmd=reset-pw&id=" .
-	 $row['rowid'] . "\">Reset AAA password</a><br>\n";
-
-echo "<br>\n";
-echo "<form action=\"users.php?cmd=set-osu-cred&id=" . $row['rowid'] .
-  "\" method=\"POST\">\n";
-echo "OSU credentials (if username empty, AAA credentials are used):<br>\n";
-echo "username: <input type=\"text\" name=\"osu_user\" value=\"" .
-  $row['osu_user'] . "\">\n";
-echo "password: <input type=\"password\" name=\"osu_password\">\n";
-echo "<input type=\"submit\" value=\"Set OSU credentials\">\n";
-echo "</form>\n";
-
-if (strlen($row['t_c_timestamp']) > 0) {
-	echo "<br>\n";
-	echo "<a href=\"users.php?cmd=clear-t-c&id=" .
-		$row['rowid'] .
-		"\">Clear Terms and Conditions acceptance</a><br>\n";
-}
-
-echo "<hr>\n";
-
-$user = $row['identity'];
-$osu_user = $row['osu_user'];
-$realm = $row['realm'];
-}
-
-if ($id > 0 || ($id == 0 && $cmd == 'eventlog')) {
-
-  if ($id == 0) {
-    echo "[<a href=\"users.php\">All users</a>] ";
-    echo "<br>\n";
-  }
-
-echo "<table border=1>\n";
-echo "<tr>";
-if ($id == 0) {
-  echo "<th>user<th>realm";
-}
-echo "<th>time<th>address<th>sessionID<th>notes";
-if ($dump > 0)
-	echo "<th>dump";
-echo "\n";
-if (isset($_GET["limit"])) {
-	$limit = $_GET["limit"];
-	if (!is_numeric($limit))
-		$limit = 20;
-} else
-	$limit = 20;
-if ($id == 0)
-  $res = $db->query("SELECT rowid,* FROM eventlog ORDER BY timestamp DESC LIMIT $limit");
-else if (strlen($osu_user) > 0)
-  $res = $db->query("SELECT rowid,* FROM eventlog WHERE (user='$user' OR user='$osu_user') AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
-else
-  $res = $db->query("SELECT rowid,* FROM eventlog WHERE user='$user' AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
-foreach ($res as $row) {
-	echo "<tr>";
-	if ($id == 0) {
-	  echo "<td>" . $row['user'] . "\n";
-	  echo "<td>" . $row['realm'] . "\n";
-	}
-	echo "<td>" . $row['timestamp'] . "\n";
-	echo "<td>" . $row['addr'] . "\n";
-	echo "<td>" . $row['sessionid'] . "\n";
-	echo "<td>" . $row['notes'] . "\n";
-	$d = $row['dump'];
-	if (strlen($d) > 0) {
-		echo "[<a href=\"users.php?cmd=eventlog&id=" . $row['rowid'] .
-		  "\">";
-		if ($d[0] == '<')
-		  echo "XML";
-		else
-		  echo "txt";
-		echo "</a>]\n";
-		if ($dump > 0)
-			echo "<td>" . htmlspecialchars($d) . "\n";
-	}
-}
-echo "</table>\n";
-
-}
-
-
-if ($id == 0 && $cmd != 'eventlog') {
-
-echo "[<a href=\"users.php?cmd=eventlog&limit=50\">Eventlog</a>] ";
-echo "<br>\n";
-
-echo "<table border=1 cellspacing=0 cellpadding=0>\n";
-echo "<tr><th>User<th>Realm<th><small>Remediation</small><th>Policy<th><small>Account type</small><th><small>Phase 2 method(s)</small><th>DevId<th>MAC Address<th>T&C\n";
-
-$res = $db->query('SELECT rowid,* FROM users WHERE (phase2=1 OR methods=\'TLS\') ORDER BY identity');
-foreach ($res as $row) {
-	echo "<tr><td><a href=\"users.php?id=" . $row['rowid'] . "\"> " .
-	    $row['identity'] . " </a>";
-	echo "<td>" . $row['realm'];
-	$rem = $row['remediation'];
-	echo "<td>";
-	if ($rem == "") {
-		echo "-";
-	} else if ($rem == "user") {
-		echo "User";
-	} else if ($rem == "policy") {
-		echo "Policy";
-	} else if ($rem == "free") {
-		echo "Free";
-	} else if ($rem == "reenroll") {
-		echo "Reenroll";
-	} else  {
-		echo "Machine";
-	}
-	echo "<td>" . $row['policy'];
-	if ($row['shared'] > 0)
-	  echo "<td>shared";
-	else
-	  echo "<td>default";
-	echo "<td><small>" . $row['methods'] . "</small>";
-	echo "<td>";
-	$xml = xml_parser_create();
-	xml_parse_into_struct($xml, $row['devinfo'], $devinfo);
-	foreach($devinfo as $k) {
-	  if ($k['tag'] == 'DEVID') {
-	    echo "<small>" . $k['value'] . "</small>";
-	    break;
-	  }
-	}
-	echo "<td><small>" . $row['mac_addr'] . "</small>";
-	echo "<td><small>" . $row['t_c_timestamp'] . "</small>";
-	echo "\n";
-}
-echo "</table>\n";
-
-}
-
-?>
-
-</html>
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index c8fbb6a..69550cf 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -793,6 +793,8 @@
 #ifdef CONFIG_SAE_PK
 		sae_deinit_pk(tmp->pk);
 #endif /* CONFIG_SAE_PK */
+		os_free(tmp->success_mac);
+		os_free(tmp->fail_mac);
 		os_free(tmp);
 	}
 }
@@ -931,31 +933,6 @@
 	os_free(conf->hs20_wan_metrics);
 	os_free(conf->hs20_connection_capability);
 	os_free(conf->hs20_operating_class);
-	os_free(conf->hs20_icons);
-	if (conf->hs20_osu_providers) {
-		for (i = 0; i < conf->hs20_osu_providers_count; i++) {
-			struct hs20_osu_provider *p;
-			size_t j;
-			p = &conf->hs20_osu_providers[i];
-			os_free(p->friendly_name);
-			os_free(p->server_uri);
-			os_free(p->method_list);
-			for (j = 0; j < p->icons_count; j++)
-				os_free(p->icons[j]);
-			os_free(p->icons);
-			os_free(p->osu_nai);
-			os_free(p->osu_nai2);
-			os_free(p->service_desc);
-		}
-		os_free(conf->hs20_osu_providers);
-	}
-	if (conf->hs20_operator_icon) {
-		for (i = 0; i < conf->hs20_operator_icon_count; i++)
-			os_free(conf->hs20_operator_icon[i]);
-		os_free(conf->hs20_operator_icon);
-	}
-	os_free(conf->subscr_remediation_url);
-	os_free(conf->hs20_sim_provisioning_url);
 	os_free(conf->t_c_filename);
 	os_free(conf->t_c_server_url);
 #endif /* CONFIG_HS20 */
@@ -1513,6 +1490,13 @@
 		wpa_printf(MSG_INFO,
 			   "Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS");
 	}
+
+	if (full_config && conf->ieee80211be && !bss->disable_11be &&
+	    !bss->beacon_prot && ap_pmf_enabled(bss)) {
+		bss->beacon_prot = 1;
+		wpa_printf(MSG_INFO,
+			   "Enabling beacon protection as IEEE 802.11be is enabled for this BSS");
+	}
 #endif /* CONFIG_IEEE80211BE */
 
 	if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
@@ -1521,6 +1505,13 @@
 		return -1;
 	}
 
+	/* Do not advertise SPP A-MSDU support if not using CCMP/GCMP */
+	if (full_config && bss->spp_amsdu &&
+	    !(bss->wpa &&
+	      bss->rsn_pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+				   WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP)))
+		bss->spp_amsdu = false;
+
 	return 0;
 }
 
@@ -1687,11 +1678,6 @@
 		if (full_config)
 			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
 #endif /* CONFIG_WEP */
-	} else if (bss->osen) {
-		bss->ssid.security_policy = SECURITY_OSEN;
-		bss->wpa_group = WPA_CIPHER_CCMP;
-		bss->wpa_pairwise = 0;
-		bss->rsn_pairwise = WPA_CIPHER_CCMP;
 	} else {
 		bss->ssid.security_policy = SECURITY_PLAINTEXT;
 		if (full_config) {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 55f3b64..a587b96 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -93,7 +93,6 @@
 	SECURITY_IEEE_802_1X = 2,
 	SECURITY_WPA_PSK = 3,
 	SECURITY_WPA = 4,
-	SECURITY_OSEN = 5
 } secpolicy;
 
 struct hostapd_ssid {
@@ -189,7 +188,6 @@
 	unsigned int wildcard_prefix:1;
 	unsigned int password_hash:1; /* whether password is hashed with
 				       * nt_password_hash() */
-	unsigned int remediation:1;
 	unsigned int macacl:1;
 	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
 	struct hostapd_radius_attr *accept_attr;
@@ -260,6 +258,10 @@
 	int vlan_id;
 	struct sae_pt *pt;
 	struct sae_pk *pk;
+	u8 *success_mac;
+	unsigned int num_success_mac, next_success_mac;
+	u8 *fail_mac;
+	unsigned int num_fail_mac, next_fail_mac;
 };
 
 struct dpp_controller_conf {
@@ -451,7 +453,6 @@
 	int pac_key_lifetime;
 	int pac_key_refresh_time;
 	int eap_teap_auth;
-	int eap_teap_pac_no_inner;
 	int eap_teap_separate_result;
 	int eap_teap_id;
 	int eap_teap_method_sequence;
@@ -466,6 +467,7 @@
 	char *radius_server_clients;
 	int radius_server_auth_port;
 	int radius_server_acct_port;
+	int radius_server_acct_log;
 	int radius_server_ipv6;
 
 	int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
@@ -621,7 +623,6 @@
 	u8 qos_map_set[16 + 2 * 21];
 	unsigned int qos_map_set_len;
 
-	int osen;
 	int proxy_arp;
 	int na_mcast_to_ucast;
 
@@ -637,37 +638,7 @@
 	size_t hs20_connection_capability_len;
 	u8 *hs20_operating_class;
 	u8 hs20_operating_class_len;
-	struct hs20_icon {
-		u16 width;
-		u16 height;
-		char language[3];
-		char type[256];
-		char name[256];
-		char file[256];
-	} *hs20_icons;
-	size_t hs20_icons_count;
-	u8 osu_ssid[SSID_MAX_LEN];
-	size_t osu_ssid_len;
-	struct hs20_osu_provider {
-		unsigned int friendly_name_count;
-		struct hostapd_lang_string *friendly_name;
-		char *server_uri;
-		int *method_list;
-		char **icons;
-		size_t icons_count;
-		char *osu_nai;
-		char *osu_nai2;
-		unsigned int service_desc_count;
-		struct hostapd_lang_string *service_desc;
-	} *hs20_osu_providers, *last_osu;
-	size_t hs20_osu_providers_count;
-	size_t hs20_osu_providers_nai_count;
-	char **hs20_operator_icon;
-	size_t hs20_operator_icon_count;
 	unsigned int hs20_deauth_req_timeout;
-	char *subscr_remediation_url;
-	u8 subscr_remediation_method;
-	char *hs20_sim_provisioning_url;
 	char *t_c_filename;
 	u32 t_c_timestamp;
 	char *t_c_server_url;
@@ -689,6 +660,7 @@
 	enum sae_pwe sae_pwe;
 	int *sae_groups;
 	struct sae_password_entry *sae_passwords;
+	int sae_track_password;
 
 	char *wowlan_triggers; /* Wake-on-WLAN triggers */
 
@@ -920,6 +892,14 @@
 	int macsec_csindex;
 
 	/**
+	 * macsec_icv_indicator - Always include ICV Indicator
+	 * (for compatibility with older MACsec switches)
+	 *
+	 * Range: 0-1 (default: 0)
+	 */
+	int macsec_icv_indicator;
+
+	/**
 	 * mka_ckn - MKA pre-shared CKN
 	 */
 #define MACSEC_CKN_MAX_LEN 32
@@ -973,9 +953,9 @@
 
 	u8 rnr;
 	char *config_id;
-	bool xrates_supported;
 
 	bool ssid_protection;
+	bool known_sta_identification;
 
 #ifdef CONFIG_IEEE80211BE
 	/* The AP is part of an AP MLD */
@@ -995,6 +975,9 @@
 	bool mld_indicate_disabled;
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_IEEE80211BE */
+	int mbssid_index;
+
+	bool spp_amsdu;
 };
 
 /**
@@ -1152,9 +1135,10 @@
 	double ignore_assoc_probability;
 	double ignore_reassoc_probability;
 	double corrupt_gtk_rekey_mic_probability;
-	int ecsa_ie_only;
 	unsigned int skip_send_eapol;
 	unsigned int enable_eapol_large_timeout;
+	int ecsa_ie_only;
+	int csa_ie_only;
 	bool delay_eapol_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
@@ -1248,9 +1232,13 @@
 		MBSSID_ENABLED = 1,
 		ENHANCED_MBSSID_ENABLED = 2,
 	} mbssid;
+	unsigned int mbssid_max;
 
 	/* Whether to enable TWT responder in HT and VHT modes */
 	bool ht_vht_twt_responder;
+
+	bool channel_usage;
+	bool peer_to_peer_twt;
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 92dbc16..d342132 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -39,6 +39,8 @@
 		res |= WPA_STA_AUTHENTICATED;
 	if (flags & WLAN_STA_ASSOC)
 		res |= WPA_STA_ASSOCIATED;
+	if (flags & WLAN_STA_SPP_AMSDU)
+		res |= WPA_STA_SPP_AMSDU;
 	return res;
 }
 
@@ -116,9 +118,11 @@
 		goto fail;
 #endif /* CONFIG_FILS */
 
-	pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
-	if (add_buf_data(&assocresp, buf, pos - buf) < 0)
-		goto fail;
+	if (!hapd->conf->rsn_override_omit_rsnxe) {
+		pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
+		if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+			goto fail;
+	}
 
 	if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
 	    add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
@@ -181,11 +185,6 @@
 	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
 	    add_buf_data(&proberesp, buf, pos - buf) < 0)
 		goto fail;
-
-	pos = hostapd_eid_osen(hapd, buf);
-	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
-	    add_buf_data(&proberesp, buf, pos - buf) < 0)
-		goto fail;
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
@@ -472,7 +471,8 @@
 		    size_t eht_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
-		    int set, const u8 *link_addr, bool mld_link_sta)
+		    int set, const u8 *link_addr, bool mld_link_sta,
+		    u16 eml_cap)
 {
 	struct hostapd_sta_add_params params;
 
@@ -512,6 +512,9 @@
 		params.mld_link_id = hapd->mld_link_id;
 		params.mld_link_addr = link_addr;
 		params.mld_link_sta = mld_link_sta;
+		/* Copy EML capabilities of ML STA */
+		if (link_addr)
+			params.eml_cap = eml_cap;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -781,6 +784,12 @@
 int hostapd_driver_scan(struct hostapd_data *hapd,
 			struct wpa_driver_scan_params *params)
 {
+	params->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		params->link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
 	if (hapd->driver && hapd->driver->scan2)
 		return hapd->driver->scan2(hapd->drv_priv, params);
 	return -1;
@@ -916,9 +925,50 @@
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+static bool hostapd_is_action_frame_link_agnostic(u8 category, u8 sub_category)
+{
+	/* As per IEEE P802.11be/D7.0, 35.3.14 (MLD individually addressed
+	 * Management frame delivery), between an AP MLD and a non-AP MLD, the
+	 * following individually addressed MMPDUs shall be intended for an MLD.
+	 */
+	switch (category) {
+	case WLAN_ACTION_BLOCK_ACK:
+	case WLAN_ACTION_FT:
+	case WLAN_ACTION_SA_QUERY:
+	case WLAN_ACTION_WNM:
+		switch (sub_category) {
+		case WNM_BSS_TRANS_MGMT_REQ:
+		case WNM_BSS_TRANS_MGMT_RESP:
+		case WNM_SLEEP_MODE_REQ:
+		case WNM_SLEEP_MODE_RESP:
+			return true;
+		default:
+			return false;
+		}
+	case WLAN_ACTION_ROBUST_AV_STREAMING:
+		switch (sub_category) {
+		case ROBUST_AV_SCS_REQ:
+		case ROBUST_AV_SCS_RESP:
+		case ROBUST_AV_MSCS_REQ:
+		case ROBUST_AV_MSCS_RESP:
+			return true;
+		default:
+			return false;
+		}
+	/* TODO: Handle EHT/EPCS related action frames once the support is
+	 * added. */
+	default:
+		return false;
+	}
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
 static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
 				unsigned int wait, const u8 *dst,
-				const u8 *data, size_t len, bool addr3_ap)
+				const u8 *data, size_t len, bool addr3_ap,
+				const u8 *forced_a3)
 {
 	const u8 *own_addr = hapd->own_addr;
 	const u8 *bssid;
@@ -926,12 +976,15 @@
 		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 	};
 	struct sta_info *sta;
+	int link_id = -1;
 
 	if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
 		return 0;
 	bssid = hapd->own_addr;
-	if (!addr3_ap && !is_multicast_ether_addr(dst) &&
-	    len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+	if (forced_a3) {
+		bssid = forced_a3;
+	} else if (!addr3_ap && !is_multicast_ether_addr(dst) &&
+		   len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
 		/*
 		 * Public Action frames to a STA that is not a member of the BSS
 		 * shall use wildcard BSSID value.
@@ -956,11 +1009,15 @@
 			own_addr = hapd->mld->mld_addr;
 			bssid = own_addr;
 		}
+
+		if (!hostapd_is_action_frame_link_agnostic(data[0], data[1]))
+			link_id = hapd->mld_link_id;
 #endif /* CONFIG_IEEE80211BE */
 	}
 
 	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
-					 own_addr, bssid, data, len, 0);
+					 own_addr, bssid, data, len, 0,
+					 link_id);
 }
 
 
@@ -968,7 +1025,8 @@
 			    unsigned int wait, const u8 *dst, const u8 *data,
 			    size_t len)
 {
-	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false);
+	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false,
+				    NULL);
 }
 
 
@@ -977,7 +1035,19 @@
 				     unsigned int wait, const u8 *dst,
 				     const u8 *data, size_t len)
 {
-	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true);
+	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true,
+				    NULL);
+}
+
+
+int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd,
+					 unsigned int freq,
+					 unsigned int wait, const u8 *dst,
+					 const u8 *a3,
+					 const u8 *data, size_t len)
+{
+	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false,
+				    a3);
 }
 
 
@@ -1018,6 +1088,12 @@
 	}
 	data.radar_background = radar_background;
 
+	data.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		data.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
 	if (!res) {
 		if (radar_background)
@@ -1261,3 +1337,27 @@
 
 	return hapd->driver->get_multi_hw_info(hapd->drv_priv, num_multi_hws);
 }
+
+
+int hostapd_drv_add_pmkid(struct hostapd_data *hapd,
+			  struct wpa_pmkid_params *params)
+{
+	if (!hapd->driver || !hapd->driver->add_pmkid || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->add_pmkid(hapd->drv_priv, params);
+}
+
+
+int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk,
+		      size_t pmk_len, const u8 *pmkid, int akmp)
+{
+	struct wpa_pmkid_params params;
+
+	os_memset(&params, 0, sizeof(params));
+	params.bssid = bssid;
+	params.pmkid = pmkid;
+	params.pmk = pmk;
+	params.pmk_len = pmk_len;
+
+	return hostapd_drv_add_pmkid(hapd, &params);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 6b7f02a..b527636 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -49,7 +49,8 @@
 		    size_t eht_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
-		    int set, const u8 *link_addr, bool mld_link_sta);
+		    int set, const u8 *link_addr, bool mld_link_sta,
+		    u16 eml_cap);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
 			     size_t elem_len);
@@ -116,6 +117,11 @@
 				     unsigned int freq,
 				     unsigned int wait, const u8 *dst,
 				     const u8 *data, size_t len);
+int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd,
+					 unsigned int freq,
+					 unsigned int wait, const u8 *dst,
+					 const u8 *a3,
+					 const u8 *data, size_t len);
 static inline void
 hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
 {
@@ -482,4 +488,9 @@
 hostapd_get_multi_hw_info(struct hostapd_data *hapd,
 			  unsigned int *num_multi_hws);
 
+int hostapd_drv_add_pmkid(struct hostapd_data *hapd,
+			  struct wpa_pmkid_params *params);
+int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk,
+		      size_t pmk_len, const u8 *pmkid, int akmp);
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 837b690..3e80318 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -14,6 +14,7 @@
 #include "eap_server/eap.h"
 #include "eap_server/eap_sim_db.h"
 #include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius.h"
 #include "radius/radius_server.h"
 #include "hostapd.h"
 #include "ap_config.h"
@@ -89,7 +90,6 @@
 	user->force_version = eap_user->force_version;
 	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
-	user->remediation = eap_user->remediation;
 	user->accept_attr = eap_user->accept_attr;
 	user->t_c_timestamp = eap_user->t_c_timestamp;
 	rv = 0;
@@ -102,6 +102,114 @@
 }
 
 
+/**
+ * hostapd_radius_log_acct_req - Callback for logging received RADIUS
+ * accounting requests
+ * @ctx: Context (struct hostapd_data)
+ * @msg: Received RADIUS accounting request
+ * @status_type: Status type from the message (parsed Acct-Status-Type
+ * attribute)
+ * Returns: 0 on success, -1 on failure
+ */
+static int hostapd_radius_log_acct_req(void *ctx, struct radius_msg *msg,
+				       u32 status_type)
+{
+	char nas_id[RADIUS_MAX_ATTR_LEN + 1] = "";
+	char session_id[RADIUS_MAX_ATTR_LEN + 1] = "";
+	char username[RADIUS_MAX_ATTR_LEN + 1] = "";
+	char calling_station_id[3 * ETH_ALEN] = "";
+	u32 session_time = 0, terminate_cause = 0,
+		bytes_in = 0, bytes_out = 0,
+		packets_in = 0, packets_out = 0,
+		gigawords_in = 0, gigawords_out = 0;
+	unsigned long long total_bytes_in = 0, total_bytes_out = 0;
+
+	/* Parse NAS identification (required by RFC 2866, section 4.1) */
+	if (radius_msg_get_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) nas_id,
+				sizeof(nas_id) - 1))
+		nas_id[0] = '\0';
+
+	/* Process Accounting-On and Accounting-Off messages separately */
+	if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON ||
+	    status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) {
+		wpa_printf(MSG_INFO, "RADIUS ACCT: NAS='%s' status='%s'",
+			   nas_id,
+			   status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON
+			   ? "Accounting-On" : "Accounting-Off");
+		return 0;
+	}
+
+	/* Parse session ID (required by RFC 2866, section 5.5) */
+	if (radius_msg_get_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+				(u8 *) session_id,
+				sizeof(session_id) - 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS ACCT: request doesn't include session ID");
+		return -1;
+	}
+
+	/* Parse user name */
+	radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) username,
+			    sizeof(username) - 1);
+
+	/* Parse device identifier */
+	radius_msg_get_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+			    (u8 *) calling_station_id,
+			    sizeof(calling_station_id) - 1);
+
+	switch (status_type) {
+	case RADIUS_ACCT_STATUS_TYPE_START:
+		wpa_printf(MSG_INFO,
+			   "RADIUS ACCT: NAS='%s' session='%s' status='Accounting-Start' station='%s' username='%s'",
+			   nas_id, session_id, calling_station_id, username);
+		break;
+	case RADIUS_ACCT_STATUS_TYPE_STOP:
+	case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE:
+		/* Parse counters */
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+					  &session_time);
+		radius_msg_get_attr_int32(msg,
+					  RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+					  &terminate_cause);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS,
+					  &bytes_in);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+					  &bytes_out);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS,
+					  &packets_in);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+					  &packets_out);
+		radius_msg_get_attr_int32(msg,
+					  RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+					  &gigawords_in);
+		radius_msg_get_attr_int32(msg,
+					  RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+					  &gigawords_out);
+
+		/* RFC 2869, section 5.1 and 5.2 */
+		total_bytes_in = ((u64) gigawords_in << 32) + bytes_in;
+		total_bytes_out = ((u64) gigawords_out << 32) + bytes_out;
+
+		wpa_printf(MSG_INFO,
+			   "RADIUS ACCT: NAS='%s' session='%s' status='%s' station='%s' username='%s' session_time=%u term_cause=%u pck_in=%u pck_out=%u bytes_in=%llu bytes_out=%llu",
+			   nas_id, session_id,
+			   status_type == RADIUS_ACCT_STATUS_TYPE_STOP ?
+			   "Accounting-Stop" : "Accounting-Interim-Update",
+			   calling_station_id, username, session_time,
+			   terminate_cause, packets_in, packets_out,
+			   total_bytes_in, total_bytes_out);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS ACCT: Unknown request status type %u",
+			   status_type);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
 {
 	struct radius_server_conf srv;
@@ -129,6 +237,8 @@
 	srv.conf_ctx = hapd;
 	srv.ipv6 = conf->radius_server_ipv6;
 	srv.get_eap_user = hostapd_radius_get_eap_user;
+	if (conf->radius_server_acct_log)
+		srv.acct_req_cb = hostapd_radius_log_acct_req;
 	srv.eap_req_id_text = conf->eap_req_id_text;
 	srv.eap_req_id_text_len = conf->eap_req_id_text_len;
 	srv.sqlite_file = conf->eap_user_sqlite;
@@ -136,9 +246,6 @@
 	srv.dump_msk_file = conf->dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
 #ifdef CONFIG_HS20
-	srv.subscr_remediation_url = conf->subscr_remediation_url;
-	srv.subscr_remediation_method = conf->subscr_remediation_method;
-	srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
 	srv.t_c_server_url = conf->t_c_server_url;
 #endif /* CONFIG_HS20 */
 	srv.erp_domain = conf->erp_domain;
@@ -224,7 +331,6 @@
 	cfg->pac_key_lifetime = hapd->conf->pac_key_lifetime;
 	cfg->pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
 	cfg->eap_teap_auth = hapd->conf->eap_teap_auth;
-	cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
 	cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
 	cfg->eap_teap_id = hapd->conf->eap_teap_id;
 	cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 2e3d904..a7d7ecd 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -262,6 +262,7 @@
 {
 	u8 *pos = eid;
 	u8 *end = eid + max_len;
+	bool force_global;
 
 	if (!hapd->iconf->ieee80211d || max_len < 6 ||
 	    hapd->iface->current_mode == NULL)
@@ -272,11 +273,23 @@
 	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
 	pos += 3;
 
-	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+	/* The 6 GHz band uses global operating classes */
+	force_global = is_6ghz_op_class(hapd->iconf->op_class);
+
+#ifdef CONFIG_MBO
+	/* Wi-Fi Agile Muiltiband AP is required to use a global operating
+	 * class. */
+	if (hapd->conf->mbo_enabled)
+		force_global = true;
+#endif /* CONFIG_MBO */
+
+	if (force_global) {
 		/* Force the third octet of the country string to indicate
 		 * Global Operating Class (Table E-4) */
 		eid[4] = 0x04;
+	}
 
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
 		/* Operating Triplet field */
 		/* Operating Extension Identifier (>= 201 to indicate this is
 		 * not a Subband Triplet field) */
@@ -390,19 +403,6 @@
 }
 
 
-static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
-{
-	const u8 *ie;
-
-	ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE);
-	if (!ie || 2U + ie[1] > len)
-		return pos;
-
-	os_memcpy(pos, ie, 2 + ie[1]);
-	return pos + 2 + ie[1];
-}
-
-
 static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos,
 				      size_t len)
 {
@@ -503,6 +503,11 @@
 	if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
 		return eid;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iconf->csa_ie_only)
+		return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
 	*eid++ = 4;
 	*eid++ = hapd->cs_block_tx;
@@ -580,7 +585,6 @@
 	size_t len, rnr_len = 0;
 	u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
 	u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL;
-	size_t i;
 
 	if (!iface->mbssid_max_interfaces ||
 	    iface->num_bss > iface->mbssid_max_interfaces ||
@@ -588,14 +592,6 @@
 	     !iface->ema_max_periodicity))
 		goto fail;
 
-	/* Make sure bss->xrates_supported is set for all BSSs to know whether
-	 * it need to be non-inherited. */
-	for (i = 0; i < iface->num_bss; i++) {
-		u8 buf[100];
-
-		hostapd_eid_ext_supp_rates(iface->bss[i], buf);
-	}
-
 	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
 	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
 				     NULL, 0, &rnr_len);
@@ -946,9 +942,8 @@
 		pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
 
-	/* WPA / OSEN */
+	/* WPA */
 	pos = hostapd_get_wpa_ie(hapd, pos, epos - pos);
-	pos = hostapd_get_osen_ie(hapd, pos, epos - pos);
 
 	/* Wi-Fi Alliance WMM */
 	pos = hostapd_eid_wmm(hapd, pos);
@@ -1414,6 +1409,7 @@
 	size_t csa_offs_len;
 	struct radius_sta rad_info;
 	struct probe_resp_params params;
+	char *hex = NULL;
 #ifdef CONFIG_IEEE80211BE
 	int mld_id;
 	u16 links;
@@ -1646,8 +1642,20 @@
 	if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
 		return;
 
+	if (hapd->conf->notify_mgmt_frames) {
+		size_t hex_len;
+
+		hex_len = len * 2 + 1;
+		hex = os_malloc(hex_len);
+		if (hex)
+			wpa_snprintf_hex(hex, hex_len, (const u8 *) mgmt, len);
+	}
+
 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
-		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+		     " signal=%d%s%s", MAC2STR(mgmt->sa), ssi_signal,
+		     hex ? " buf=" : "", hex ? hex : "");
+
+	os_free(hex);
 
 	os_memset(&params, 0, sizeof(params));
 
@@ -2401,9 +2409,8 @@
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
 
-	/* WPA / OSEN */
+	/* WPA */
 	tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos);
-	tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos);
 
 	/* Wi-Fi Alliance WMM */
 	tailpos = hostapd_eid_wmm(hapd, tailpos);
@@ -2575,10 +2582,6 @@
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_HS20
 	params->disable_dgaf = hapd->conf->disable_dgaf;
-	if (hapd->conf->osen) {
-		params->privacy = 1;
-		params->osen = 1;
-	}
 #endif /* CONFIG_HS20 */
 	params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
 	params->pbss = hapd->conf->pbss;
@@ -2909,7 +2912,15 @@
 					is_identical_vendor_ies = true;
 					num_own_elem_vendor_ies++;
 				}
-				continue;
+
+				/* Update the parsed EIDs bitmap */
+				if (is_ext)
+					parsed_ext_eid_bmap[own_eid / 8] |=
+						BIT(own_eid % 8);
+				else
+					parsed_eid_bmap[own_eid / 8] |=
+						BIT(own_eid % 8);
+				break;
 			}
 
 			/* No need to include this non-matching Vendor Specific
@@ -3090,7 +3101,7 @@
 {
 	bool tx_vap = hapd == hostapd_mbssid_get_tx_bss(hapd);
 	size_t link_data_len, sta_profile_len;
-	size_t own_data_len;
+	size_t own_data_len, fixed;
 	struct probe_resp_params link_params;
 	struct probe_resp_params own_params;
 	struct ieee80211_mgmt *link_data;
@@ -3118,7 +3129,10 @@
 	own_data_len = own_params.resp_len;
 
 	/* Consider the length of the variable fields */
-	own_data_len -= offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+	fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+	if (own_data_len < fixed)
+		goto fail;
+	own_data_len -= fixed;
 
 	for_each_mld_link(link_bss, hapd) {
 		if (link_bss == hapd || !link_bss->started)
@@ -3143,8 +3157,10 @@
 		link_data_len = link_params.resp_len;
 
 		/* Consider length of the variable fields */
-		link_data_len -= offsetof(struct ieee80211_mgmt,
-					  u.probe_resp.variable);
+		fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+		if (link_data_len < fixed)
+			continue;
+		link_data_len -= fixed;
 
 		sta_profile = hostapd_gen_sta_profile(link_data, link_data_len,
 						      own_data, own_data_len,
@@ -3177,6 +3193,7 @@
 		os_free(link_params.resp);
 	}
 
+fail:
 	os_free(own_params.resp);
 }
 
@@ -3227,7 +3244,8 @@
 				continue;
 #endif /* CONFIG_IEEE80211BE */
 
-			if (other->bss[i] && other->bss[i]->started)
+			if (other->bss[i] && other->bss[i]->started &&
+			    other->bss[i]->beacon_set_done)
 				__ieee802_11_set_beacon(other->bss[i]);
 		}
 	}
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index b93a5d2..441995b 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -475,6 +475,10 @@
 
 #ifdef CONFIG_IEEE80211BE
 	if (sta->mld_info.mld_sta) {
+		u16 mld_sta_capa = sta->mld_info.common_info.mld_capa;
+		u8 max_simul_links = mld_sta_capa &
+			EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+
 		for (i = 0; i < MAX_NUM_MLD_LINKS; ++i) {
 			if (!sta->mld_info.links[i].valid)
 				continue;
@@ -485,6 +489,11 @@
 			if (!os_snprintf_error(buflen - len, ret))
 				len += ret;
 		}
+
+		ret = os_snprintf(buf + len, buflen - len,
+				  "max_simul_links=%d\n", max_simul_links);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -917,6 +926,15 @@
 			len += ret;
 		}
 
+		if (hapd->iconf->punct_bitmap) {
+			ret = os_snprintf(buf + len, buflen - len,
+					  "punct_bitmap=0x%x\n",
+					  hapd->iconf->punct_bitmap);
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
+		}
+
 		if (hapd->conf->mld_ap) {
 			struct hostapd_data *link_bss;
 
@@ -951,6 +969,15 @@
 					return len;
 				len += ret;
 			}
+
+			ret = os_snprintf(buf + len, buflen - len,
+					  "ap_mld_type=%s\n",
+					  (hapd->iface->mld_mld_capa &
+					   EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK)
+					  ? "NSTR" : "STR");
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
 		}
 	}
 #endif /* CONFIG_IEEE80211BE */
@@ -1127,20 +1154,11 @@
 		} \
 	} while (0)
 
-#define SET_CSA_SETTING_EXT(str) \
-	do { \
-		const char *pos2 = os_strstr(pos, " " #str "="); \
-		if (pos2) { \
-			pos2 += sizeof(" " #str "=") - 1; \
-			settings->str = atoi(pos2); \
-		} \
-	} while (0)
-
 	SET_CSA_SETTING(center_freq1);
 	SET_CSA_SETTING(center_freq2);
 	SET_CSA_SETTING(bandwidth);
 	SET_CSA_SETTING(sec_channel_offset);
-	SET_CSA_SETTING_EXT(punct_bitmap);
+	SET_CSA_SETTING(punct_bitmap);
 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
@@ -1148,7 +1166,6 @@
 		settings->freq_params.eht_enabled;
 	settings->block_tx = !!os_strstr(pos, " blocktx");
 #undef SET_CSA_SETTING
-#undef SET_CSA_SETTING_EXT
 
 	return 0;
 }
@@ -1181,6 +1198,7 @@
 	size_t pmk_len;
 	char *pos, *pos2;
 	int akmp = 0, expiration = 0;
+	int ret;
 
 	/*
 	 * Entry format:
@@ -1216,8 +1234,18 @@
 	if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
 		return -1;
 
-	return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
-				   pmkid, expiration, akmp, NULL);
+	ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+				  pmkid, expiration, akmp, NULL, false);
+	if (ret)
+		return ret;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+					  pmkid, expiration, akmp, NULL, true);
+#endif /* CONFIG_IEEE80211BE */
+
+	return ret;
 }
 
 
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index d1bffa8..d94ca9e 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1382,6 +1382,21 @@
 }
 
 
+static void hostapd_gas_req_wait(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Timeout while waiting for Config Request");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
 static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
 {
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
@@ -1400,6 +1415,9 @@
 
 	if (!hapd->dpp_auth->configurator)
 		hostapd_dpp_start_gas_client(hapd);
+	else
+		eloop_register_timeout(10, 0, hostapd_gas_req_wait,
+				       hapd, NULL);
 }
 
 
@@ -2142,11 +2160,22 @@
 
 	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP, pkhash) < 0) {
+				WPA_KEY_MGMT_DPP, pkhash, false) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
 		goto done;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap &&
+	    wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP, pkhash, true) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to add PMKSA cache entry (MLD)");
+		goto done;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
 					DPP_STATUS_OK);
 done:
@@ -2916,11 +2945,22 @@
 
 	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP, pkhash) < 0) {
+				WPA_KEY_MGMT_DPP, pkhash, false) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
 		goto done;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap &&
+	    wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP, pkhash, true) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to add PMKSA cache entry (MLD)");
+		goto done;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
 		   MACSTR, MAC2STR(src));
 
@@ -2940,6 +2980,10 @@
 	const u8 *hdr;
 	unsigned int pkex_t;
 
+	/* Discard DPP Action frames if there is no global DPP context */
+	if (!hapd->iface->interfaces || !hapd->iface->interfaces->dpp)
+		return;
+
 	if (len < DPP_HDR_LEN)
 		return;
 	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
@@ -3079,6 +3123,7 @@
 	struct wpabuf *resp;
 
 	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+	eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
 	if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
 	    !ether_addr_equal(sa, auth->peer_mac_addr)) {
 #ifdef CONFIG_DPP2
@@ -3359,6 +3404,7 @@
 #ifdef CONFIG_DPP3
 	hostapd_dpp_push_button_stop(hapd);
 #endif /* CONFIG_DPP3 */
+	eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
 }
 
 
@@ -3512,6 +3558,7 @@
 	eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
 #ifdef CONFIG_DPP2
 	eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
 			     hapd, NULL);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 05adc41..0b4613e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -18,6 +18,7 @@
 #include "common/dpp.h"
 #include "common/sae.h"
 #include "common/hw_features_common.h"
+#include "common/nan_de.h"
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
@@ -365,12 +366,6 @@
 		ie = elems.wpa_ie - 2;
 		ielen = elems.wpa_ie_len + 2;
 		wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
-#ifdef CONFIG_HS20
-	} else if (elems.osen) {
-		ie = elems.osen - 2;
-		ielen = elems.osen_len + 2;
-		wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
-#endif /* CONFIG_HS20 */
 	} else {
 		ie = NULL;
 		ielen = 0;
@@ -578,7 +573,8 @@
 					  elems.rsnxe ? elems.rsnxe - 2 : NULL,
 					  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
 					  elems.mdie, elems.mdie_len,
-					  elems.owe_dh, elems.owe_dh_len, NULL);
+					  elems.owe_dh, elems.owe_dh_len, NULL,
+					  ap_sta_is_mld(hapd, sta));
 		reason = WLAN_REASON_INVALID_IE;
 		status = WLAN_STATUS_INVALID_IE;
 		switch (res) {
@@ -648,6 +644,11 @@
 		else
 			sta->flags &= ~WLAN_STA_MFP;
 
+		if (wpa_auth_uses_spp_amsdu(sta->wpa_sm))
+			sta->flags |= WLAN_STA_SPP_AMSDU;
+		else
+			sta->flags &= ~WLAN_STA_SPP_AMSDU;
+
 #ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
@@ -677,6 +678,13 @@
 			goto fail;
 		}
 #endif /* CONFIG_SAE */
+
+		wpa_auth_set_ssid_protection(
+			sta->wpa_sm,
+			hapd->conf->ssid_protection &&
+			ieee802_11_rsnx_capab_len(
+				elems.rsnxe, elems.rsnxe_len,
+				WLAN_RSNX_CAPAB_SSID_PROTECTION));
 	} else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
 		struct wpabuf *wps;
@@ -705,29 +713,6 @@
 			sta->flags |= WLAN_STA_MAYBE_WPS;
 		wpabuf_free(wps);
 #endif /* CONFIG_WPS */
-#ifdef CONFIG_HS20
-	} else if (hapd->conf->osen) {
-		if (elems.osen == NULL) {
-			hostapd_logger(
-				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-				HOSTAPD_LEVEL_INFO,
-				"No HS 2.0 OSEN element in association request");
-			return WLAN_STATUS_INVALID_IE;
-		}
-
-		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
-		if (sta->wpa_sm == NULL)
-			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-							sta->addr, NULL);
-		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_WARNING,
-				   "Failed to initialize WPA state machine");
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
-		}
-		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
-				      elems.osen - 2, elems.osen_len + 2) < 0)
-			return WLAN_STATUS_INVALID_IE;
-#endif /* CONFIG_HS20 */
 	}
 #ifdef CONFIG_WPS
 skip_wpa_check:
@@ -910,6 +895,12 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
 
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+	hostapd_set_sta_flags(hapd, sta);
+
 #ifdef CONFIG_IEEE80211BE
 	if (hostapd_process_assoc_ml_info(hapd, sta, req_ies, req_ies_len,
 					  !!reassoc, WLAN_STATUS_SUCCESS,
@@ -920,11 +911,6 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
-	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
-	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
-	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
-
-	hostapd_set_sta_flags(hapd, sta);
 	if (updated)
 		ap_sta_set_authorized_event(hapd, sta, 1);
 
@@ -950,6 +936,10 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (elems.wfa_capab)
+		hostapd_wfa_capab(hapd, sta, elems.wfa_capab,
+				  elems.wfa_capab + elems.wfa_capab_len);
+
 	return 0;
 
 fail:
@@ -1787,8 +1777,8 @@
 		pos = mgmt->u.action.u.vs_public_action.variable;
 		end = drv_mgmt->frame + drv_mgmt->frame_len;
 		pos++;
-		hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, drv_mgmt->freq,
-				       pos, end - pos);
+		hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
+				       drv_mgmt->freq, pos, end - pos);
 		return;
 	}
 #endif /* CONFIG_NAN_USD */
@@ -1855,6 +1845,11 @@
 	if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
 	    bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
 		return HAPD_BROADCAST;
+#ifdef CONFIG_NAN_USD
+	if (nan_de_is_nan_network_id(bssid))
+		return HAPD_BROADCAST; /* Process NAN Network ID like broadcast
+					*/
+#endif /* CONFIG_NAN_USD */
 
 	for (i = 0; i < iface->num_bss; i++) {
 		struct hostapd_data *hapd;
@@ -1988,18 +1983,19 @@
 {
 	struct ieee80211_hdr *hdr;
 	struct hostapd_data *orig_hapd, *tmp_hapd;
+	const u8 *bssid;
 
 	orig_hapd = hapd;
 
 	hdr = (struct ieee80211_hdr *) buf;
 	hapd = switch_link_hapd(hapd, link_id);
-	tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
+	bssid = get_hdr_bssid(hdr, len);
+	tmp_hapd = get_hapd_bssid(hapd->iface, bssid, link_id);
 	if (tmp_hapd) {
 		hapd = tmp_hapd;
 #ifdef CONFIG_IEEE80211BE
-	} else if (hapd->conf->mld_ap &&
-		   ether_addr_equal(hapd->mld->mld_addr,
-				    get_hdr_bssid(hdr, len))) {
+	} else if (hapd->conf->mld_ap && bssid &&
+		   ether_addr_equal(hapd->mld->mld_addr, bssid)) {
 		/* AP MLD address match - use hapd pointer as-is */
 #endif /* CONFIG_IEEE80211BE */
 	} else {
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index a510ee3..c0e9030 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -89,8 +89,6 @@
 			user->next = (void *) 1;
 		} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
 			set_user_methods(user, argv[i]);
-		} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
-			user->remediation = strlen(argv[i]) > 0;
 		} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
 			user->t_c_timestamp = strtol(argv[i], NULL, 10);
 		}
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 4642e49..13cf766 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -179,14 +179,6 @@
 		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
 	if (hapd->conf->hs20_operating_class)
 		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
-	if (hapd->conf->hs20_osu_providers_count)
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
-	if (hapd->conf->hs20_osu_providers_nai_count)
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
-	if (hapd->conf->hs20_icons_count)
-		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
-	if (hapd->conf->hs20_operator_icon_count)
-		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
 	gas_anqp_set_element_len(buf, len);
 }
 #endif /* CONFIG_HS20 */
@@ -706,232 +698,6 @@
 	}
 }
 
-
-static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
-			  const char *name)
-{
-	size_t j;
-	struct hs20_icon *icon = NULL;
-
-	for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
-		if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
-			icon = &bss->hs20_icons[j];
-	}
-	if (!icon)
-		return; /* icon info not found */
-
-	wpabuf_put_le16(buf, icon->width);
-	wpabuf_put_le16(buf, icon->height);
-	wpabuf_put_data(buf, icon->language, 3);
-	wpabuf_put_u8(buf, os_strlen(icon->type));
-	wpabuf_put_str(buf, icon->type);
-	wpabuf_put_u8(buf, os_strlen(icon->name));
-	wpabuf_put_str(buf, icon->name);
-}
-
-
-static void anqp_add_osu_provider(struct wpabuf *buf,
-				  struct hostapd_bss_config *bss,
-				  struct hs20_osu_provider *p)
-{
-	u8 *len, *len2, *count;
-	unsigned int i;
-
-	len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
-
-	/* OSU Friendly Name Duples */
-	len2 = wpabuf_put(buf, 2);
-	for (i = 0; i < p->friendly_name_count; i++) {
-		struct hostapd_lang_string *s = &p->friendly_name[i];
-		wpabuf_put_u8(buf, 3 + s->name_len);
-		wpabuf_put_data(buf, s->lang, 3);
-		wpabuf_put_data(buf, s->name, s->name_len);
-	}
-	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
-	/* OSU Server URI */
-	if (p->server_uri) {
-		wpabuf_put_u8(buf, os_strlen(p->server_uri));
-		wpabuf_put_str(buf, p->server_uri);
-	} else
-		wpabuf_put_u8(buf, 0);
-
-	/* OSU Method List */
-	count = wpabuf_put(buf, 1);
-	for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
-		wpabuf_put_u8(buf, p->method_list[i]);
-	*count = i;
-
-	/* Icons Available */
-	len2 = wpabuf_put(buf, 2);
-	for (i = 0; i < p->icons_count; i++)
-		anqp_add_icon(buf, bss, p->icons[i]);
-	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
-	/* OSU_NAI */
-	if (p->osu_nai) {
-		wpabuf_put_u8(buf, os_strlen(p->osu_nai));
-		wpabuf_put_str(buf, p->osu_nai);
-	} else
-		wpabuf_put_u8(buf, 0);
-
-	/* OSU Service Description Duples */
-	len2 = wpabuf_put(buf, 2);
-	for (i = 0; i < p->service_desc_count; i++) {
-		struct hostapd_lang_string *s = &p->service_desc[i];
-		wpabuf_put_u8(buf, 3 + s->name_len);
-		wpabuf_put_data(buf, s->lang, 3);
-		wpabuf_put_data(buf, s->name, s->name_len);
-	}
-	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
-	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
-}
-
-
-static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
-					struct wpabuf *buf)
-{
-	if (hapd->conf->hs20_osu_providers_count) {
-		size_t i;
-		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
-		wpabuf_put_u8(buf, 0); /* Reserved */
-
-		/* OSU SSID */
-		wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
-		wpabuf_put_data(buf, hapd->conf->osu_ssid,
-				hapd->conf->osu_ssid_len);
-
-		/* Number of OSU Providers */
-		wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
-
-		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
-			anqp_add_osu_provider(
-				buf, hapd->conf,
-				&hapd->conf->hs20_osu_providers[i]);
-		}
-
-		gas_anqp_set_element_len(buf, len);
-	}
-}
-
-
-static void anqp_add_osu_provider_nai(struct wpabuf *buf,
-				      struct hs20_osu_provider *p)
-{
-	/* OSU_NAI for shared BSS (Single SSID) */
-	if (p->osu_nai2) {
-		wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
-		wpabuf_put_str(buf, p->osu_nai2);
-	} else {
-		wpabuf_put_u8(buf, 0);
-	}
-}
-
-
-static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
-					    struct wpabuf *buf)
-{
-	if (hapd->conf->hs20_osu_providers_nai_count) {
-		size_t i;
-		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
-		wpabuf_put_u8(buf, 0); /* Reserved */
-
-		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
-			anqp_add_osu_provider_nai(
-				buf, &hapd->conf->hs20_osu_providers[i]);
-		}
-
-		gas_anqp_set_element_len(buf, len);
-	}
-}
-
-
-static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
-				      struct wpabuf *buf,
-				      const u8 *name, size_t name_len)
-{
-	struct hs20_icon *icon;
-	size_t i;
-	u8 *len;
-
-	wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
-			  name, name_len);
-	for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
-		icon = &hapd->conf->hs20_icons[i];
-		if (name_len == os_strlen(icon->name) &&
-		    os_memcmp(name, icon->name, name_len) == 0)
-			break;
-	}
-
-	if (i < hapd->conf->hs20_icons_count)
-		icon = &hapd->conf->hs20_icons[i];
-	else
-		icon = NULL;
-
-	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-	wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
-	wpabuf_put_u8(buf, 0); /* Reserved */
-
-	if (icon) {
-		char *data;
-		size_t data_len;
-
-		data = os_readfile(icon->file, &data_len);
-		if (data == NULL || data_len > 65535) {
-			wpabuf_put_u8(buf, 2); /* Download Status:
-						* Unspecified file error */
-			wpabuf_put_u8(buf, 0);
-			wpabuf_put_le16(buf, 0);
-		} else {
-			wpabuf_put_u8(buf, 0); /* Download Status: Success */
-			wpabuf_put_u8(buf, os_strlen(icon->type));
-			wpabuf_put_str(buf, icon->type);
-			wpabuf_put_le16(buf, data_len);
-			wpabuf_put_data(buf, data, data_len);
-		}
-		os_free(data);
-	} else {
-		wpabuf_put_u8(buf, 1); /* Download Status: File not found */
-		wpabuf_put_u8(buf, 0);
-		wpabuf_put_le16(buf, 0);
-	}
-
-	gas_anqp_set_element_len(buf, len);
-}
-
-
-static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
-					    struct wpabuf *buf)
-{
-	struct hostapd_bss_config *bss = hapd->conf;
-	size_t i;
-	u8 *len;
-
-	if (!bss->hs20_operator_icon_count)
-		return;
-
-	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-	wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
-	wpabuf_put_u8(buf, 0); /* Reserved */
-
-	for (i = 0; i < bss->hs20_operator_icon_count; i++)
-		anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
-
-	gas_anqp_set_element_len(buf, len);
-}
-
 #endif /* CONFIG_HS20 */
 
 
@@ -973,7 +739,6 @@
 gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
 				unsigned int request,
 				const u8 *home_realm, size_t home_realm_len,
-				const u8 *icon_name, size_t icon_name_len,
 				const u16 *extra_req,
 				unsigned int num_extra_req)
 {
@@ -984,8 +749,6 @@
 	len = 1400;
 	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
 		len += 1000;
-	if (request & ANQP_REQ_ICON_REQUEST)
-		len += 65536;
 #ifdef CONFIG_FILS
 	if (request & ANQP_FILS_REALM_INFO)
 		len += 2 * dl_list_len(&hapd->conf->fils_realms);
@@ -1054,14 +817,6 @@
 		anqp_add_connection_capability(hapd, buf);
 	if (request & ANQP_REQ_OPERATING_CLASS)
 		anqp_add_operating_class(hapd, buf);
-	if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
-		anqp_add_osu_providers_list(hapd, buf);
-	if (request & ANQP_REQ_ICON_REQUEST)
-		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
-	if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
-		anqp_add_operator_icon_metadata(hapd, buf);
-	if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
-		anqp_add_osu_providers_nai_list(hapd, buf);
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
@@ -1079,8 +834,6 @@
 	unsigned int request;
 	const u8 *home_realm_query;
 	size_t home_realm_query_len;
-	const u8 *icon_name;
-	size_t icon_name_len;
 	int p2p_sd;
 	u16 extra_req[ANQP_MAX_EXTRA_REQ];
 	unsigned int num_extra_req;
@@ -1245,20 +998,6 @@
 		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
 			     hapd->conf->hs20_operating_class != NULL, qi);
 		break;
-	case HS20_STYPE_OSU_PROVIDERS_LIST:
-		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
-			     hapd->conf->hs20_osu_providers_count, qi);
-		break;
-	case HS20_STYPE_OPERATOR_ICON_METADATA:
-		set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
-			     "Operator Icon Metadata",
-			     hapd->conf->hs20_operator_icon_count, qi);
-		break;
-	case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
-		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
-			     "OSU Providers NAI List",
-			     hapd->conf->hs20_osu_providers_nai_count, qi);
-		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
 			   subtype);
@@ -1284,23 +1023,6 @@
 }
 
 
-static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
-				    const u8 *pos, const u8 *end,
-				    struct anqp_query_info *qi)
-{
-	qi->request |= ANQP_REQ_ICON_REQUEST;
-	qi->icon_name = pos;
-	qi->icon_name_len = end - pos;
-	if (hapd->conf->hs20_icons_count) {
-		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
-			   "(local)");
-	} else {
-		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
-			   "available");
-	}
-}
-
-
 static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
 					 const u8 *pos, const u8 *end,
 					 struct anqp_query_info *qi)
@@ -1323,9 +1045,6 @@
 	case HS20_STYPE_NAI_HOME_REALM_QUERY:
 		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
 		break;
-	case HS20_STYPE_ICON_REQUEST:
-		rx_anqp_hs_icon_request(hapd, pos, end, qi);
-		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
 			   "%u", subtype);
@@ -1455,7 +1174,6 @@
 	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
 					      qi->home_realm_query,
 					      qi->home_realm_query_len,
-					      qi->icon_name, qi->icon_name_len,
 					      qi->extra_req, qi->num_extra_req);
 	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
 			buf);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 7d92489..65dc14d 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -162,7 +162,7 @@
 	else
 		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
-	if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
+	if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
 		hostapd_setup_wpa(hapd);
 		if (hapd->wpa_auth)
 			wpa_init_keys(hapd->wpa_auth);
@@ -242,6 +242,10 @@
 		if (os_strcmp(newconf->bss[i]->iface,
 			      oldconf->bss[i]->iface) != 0)
 			return 1;
+#ifdef CONFIG_IEEE80211BE
+		if (newconf->bss[i]->mld_ap != oldconf->bss[i]->mld_ap)
+			return 1;
+#endif /* CONFIG_IEEE80211BE */
 	}
 
 	return 0;
@@ -302,7 +306,6 @@
 				   "Failed to enable interface on config reload");
 		return res;
 	}
-	iface->conf = newconf;
 
 	for (j = 0; j < iface->num_bss; j++) {
 		hapd = iface->bss[j];
@@ -330,6 +333,7 @@
 		hostapd_reload_bss(hapd);
 	}
 
+	iface->conf = newconf;
 	hostapd_config_free(oldconf);
 
 
@@ -354,7 +358,7 @@
 				   ifname, i);
 		}
 	}
-	if (hapd->conf->ieee80211w) {
+	if (ap_pmf_enabled(hapd->conf)) {
 		for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
 			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
 						NULL, i, 0, 0, NULL,
@@ -521,7 +525,10 @@
 
 	authsrv_deinit(hapd);
 
-	if (hapd->interface_added) {
+	/* For single drv, first bss would have interface_added flag set.
+	 * Don't remove interface now. Driver deinit part will take care
+	 */
+	if (hapd->interface_added && hapd->iface->bss[0] != hapd) {
 		hapd->interface_added = 0;
 		if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
 			wpa_printf(MSG_WARNING,
@@ -633,7 +640,7 @@
 	}
 
 	/* Put all freeing logic above this */
-	if (!hapd->mld->num_links)
+	if (!hapd->mld || !hapd->mld->num_links)
 		return;
 
 	/* If not started, not yet linked to the MLD. However, the first
@@ -703,6 +710,7 @@
 		acs_cleanup(iface);
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = NULL;
+	iface->num_hw_features = 0;
 	iface->current_mode = NULL;
 	os_free(iface->current_rates);
 	iface->current_rates = NULL;
@@ -1663,7 +1671,7 @@
 		return -1;
 	}
 
-	if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
+	if (conf->wpa && hostapd_setup_wpa(hapd))
 		return -1;
 
 	if (accounting_init(hapd)) {
@@ -1794,26 +1802,26 @@
 
 int hostapd_set_acl(struct hostapd_data *hapd)
 {
-	struct hostapd_config *conf = hapd->iconf;
+	struct hostapd_bss_config *conf = hapd->conf;
 	int err = 0;
 	u8 accept_acl;
 
 	if (hapd->iface->drv_max_acl_mac_addrs == 0)
 		return 0;
 
-	if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+	if (conf->macaddr_acl == DENY_UNLESS_ACCEPTED) {
 		accept_acl = 1;
-		err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
-					   conf->bss[0]->num_accept_mac,
+		err = hostapd_set_acl_list(hapd, conf->accept_mac,
+					   conf->num_accept_mac,
 					   accept_acl);
 		if (err) {
 			wpa_printf(MSG_DEBUG, "Failed to set accept acl");
 			return -1;
 		}
-	} else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+	} else if (conf->macaddr_acl == ACCEPT_UNLESS_DENIED) {
 		accept_acl = 0;
-		err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
-					   conf->bss[0]->num_deny_mac,
+		err = hostapd_set_acl_list(hapd, conf->deny_mac,
+					   conf->num_deny_mac,
 					   accept_acl);
 		if (err) {
 			wpa_printf(MSG_DEBUG, "Failed to set deny acl");
@@ -1906,18 +1914,30 @@
 static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
 					      void *ctx)
 {
+	struct hostapd_data *hapd = iface->bss[0];
 	bool all_no_ir, is_6ghz;
 	int i, j;
 	struct hostapd_hw_modes *mode = NULL;
+	struct hostapd_hw_modes *hw_features;
+	u16 num_hw_features, flags;
+	u8 dfs_domain;
 
-	if (hostapd_get_hw_features(iface))
-		return 0;
+	if (hostapd_drv_none(hapd))
+		return -1;
+
+	hw_features = hostapd_get_hw_feature_data(hapd, &num_hw_features,
+						  &flags, &dfs_domain);
+	if (!hw_features) {
+		wpa_printf(MSG_DEBUG,
+			   "Could not fetching hardware channel list");
+		return -1;
+	}
 
 	all_no_ir = true;
 	is_6ghz = false;
 
-	for (i = 0; i < iface->num_hw_features; i++) {
-		mode = &iface->hw_features[i];
+	for (i = 0; i < num_hw_features; i++) {
+		mode = &hw_features[i];
 
 		if (mode->mode == iface->conf->hw_mode) {
 			if (iface->freq > 0 &&
@@ -1939,26 +1959,25 @@
 	}
 
 	if (!mode || !is_6ghz)
-		return 0;
-	iface->current_mode = mode;
+		goto free_hw_features;
 
 	if (iface->state == HAPD_IFACE_ENABLED) {
 		if (!all_no_ir) {
 			struct hostapd_channel_data *chan;
 
-			chan = hw_get_channel_freq(iface->current_mode->mode,
+			chan = hw_get_channel_freq(mode->mode,
 						   iface->freq, NULL,
-						   iface->hw_features,
-						   iface->num_hw_features);
+						   hw_features,
+						   num_hw_features);
 
 			if (!chan) {
 				wpa_printf(MSG_ERROR,
 					   "NO_IR: Could not derive chan from freq");
-				return 0;
+				goto free_hw_features;
 			}
 
 			if (!(chan->flag & HOSTAPD_CHAN_NO_IR))
-				return 0;
+				goto free_hw_features;
 			wpa_printf(MSG_DEBUG,
 				   "NO_IR: The current channel has NO_IR flag now, stop AP.");
 		} else {
@@ -1975,20 +1994,20 @@
 		if (all_no_ir) {
 			wpa_printf(MSG_DEBUG,
 				   "NO_IR: AP in NO_IR and all chan in the new chanlist are NO_IR. Ignore");
-			return 0;
+			goto free_hw_features;
 		}
 
 		if (!iface->conf->acs) {
 			struct hostapd_channel_data *chan;
 
-			chan = hw_get_channel_freq(iface->current_mode->mode,
+			chan = hw_get_channel_freq(mode->mode,
 						   iface->freq, NULL,
-						   iface->hw_features,
-						   iface->num_hw_features);
+						   hw_features,
+						   num_hw_features);
 			if (!chan) {
 				wpa_printf(MSG_ERROR,
 					   "NO_IR: Could not derive chan from freq");
-				return 0;
+				goto free_hw_features;
 			}
 
 			/* If the last operating channel is NO_IR, trigger ACS.
@@ -1999,13 +2018,15 @@
 				if (acs_init(iface) != HOSTAPD_CHAN_ACS)
 					wpa_printf(MSG_ERROR,
 						   "NO_IR: Could not start ACS");
-				return 0;
+				goto free_hw_features;
 			}
 		}
 
 		setup_interface2(iface);
 	}
 
+free_hw_features:
+	hostapd_free_hw_features(hw_features, num_hw_features);
 	return 0;
 }
 
@@ -2744,7 +2765,7 @@
 		hostapd_neighbor_set_own_report(iface->bss[j]);
 
 	if (iface->interfaces && iface->interfaces->count > 1)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_update_beacons(iface);
 
 	return 0;
 
@@ -2944,6 +2965,7 @@
 #ifdef CONFIG_SAE
 	dl_list_init(&hapd->sae_commit_queue);
 #endif /* CONFIG_SAE */
+	dl_list_init(&hapd->erp_keys);
 
 	return hapd;
 }
@@ -3080,14 +3102,17 @@
 #endif /* CONFIG_IEEE80211BE */
 
 
-static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
-					 struct hapd_interfaces *interfaces)
+void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+				  struct hapd_interfaces *interfaces)
 {
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_mld *mld, **all_mld;
 	struct hostapd_bss_config *conf;
 	size_t i;
 
+	if (hapd->mld)
+		return;
+
 	conf = hapd->conf;
 
 	if (!hapd->iconf || !hapd->iconf->ieee80211be || !conf->mld_ap ||
@@ -3330,6 +3355,7 @@
 {
 	struct hostapd_iface *new_iface = NULL, *iface = NULL;
 	struct hostapd_data *hapd;
+	struct hostapd_config *conf;
 	int k;
 	size_t i, bss_idx;
 
@@ -3345,17 +3371,26 @@
 
 	wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
 		   config_fname, phy, iface ? "" : " --> new PHY");
+
+	conf = interfaces->config_read_cb(config_fname);
+	if (!conf)
+		return NULL;
+
+#ifdef CONFIG_IEEE80211BE
+	/* AP MLD can be enabled with the same interface name, so even if we
+	 * get the interface, we still need to allocate a new hostapd_iface
+	 * structure. */
+	if (conf->bss[0]->mld_ap)
+		iface = NULL;
+#endif /* CONFIG_IEEE80211BE */
+
 	if (iface) {
-		struct hostapd_config *conf;
 		struct hostapd_bss_config **tmp_conf;
 		struct hostapd_data **tmp_bss;
 		struct hostapd_bss_config *bss;
 		const char *ifname;
 
 		/* Add new BSS to existing iface */
-		conf = interfaces->config_read_cb(config_fname);
-		if (conf == NULL)
-			return NULL;
 		if (conf->num_bss > 1) {
 			wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
 			hostapd_config_free(conf);
@@ -3405,6 +3440,8 @@
 		conf->bss[0] = NULL;
 		hostapd_config_free(conf);
 	} else {
+		hostapd_config_free(conf);
+
 		/* Add a new iface with the first BSS */
 		new_iface = iface = hostapd_init(interfaces, config_fname);
 		if (!iface)
@@ -3439,22 +3476,24 @@
 		return;
 
 #ifdef CONFIG_IEEE80211BE
-	/* In case of non-ML operation, de-init. But if ML operation exist,
-	 * even if that's the last BSS in the interface, the driver (drv) could
-	 * be in use for a different AP MLD. Hence, need to check if drv is
-	 * still being used by some other BSS before de-initiallizing. */
-	if (!iface->bss[0]->conf->mld_ap) {
-		driver->hapd_deinit(drv_priv);
-	} else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
-		   driver->is_drv_shared &&
-		   !driver->is_drv_shared(drv_priv,
-					  iface->bss[0]->mld_link_id)) {
+	if (!driver->is_drv_shared ||
+	    !driver->is_drv_shared(drv_priv, iface->bss[0]->mld_link_id)) {
 		driver->hapd_deinit(drv_priv);
 		hostapd_mld_interface_freed(iface->bss[0]);
-	} else if (hostapd_if_link_remove(iface->bss[0],
-					  WPA_IF_AP_BSS,
-					  iface->bss[0]->conf->iface,
-					  iface->bss[0]->mld_link_id)) {
+		iface->bss[0]->drv_priv = NULL;
+		return;
+	}
+
+	if (iface->bss[0]->conf->mld_ap) {
+		if (hostapd_if_link_remove(iface->bss[0],
+					WPA_IF_AP_BSS,
+					iface->bss[0]->conf->iface,
+					iface->bss[0]->mld_link_id))
+			wpa_printf(MSG_WARNING,
+				   "Failed to remove link BSS interface %s",
+				   iface->bss[0]->conf->iface);
+	} else if (hostapd_if_remove(iface->bss[0], WPA_IF_AP_BSS,
+				     iface->bss[0]->conf->iface)) {
 		wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
 			   iface->bss[0]->conf->iface);
 	}
@@ -3611,8 +3650,6 @@
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
 {
 	size_t j;
-	const struct wpa_driver_ops *driver;
-	void *drv_priv;
 
 	if (hapd_iface == NULL)
 		return -1;
@@ -3627,8 +3664,6 @@
 	}
 
 	wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
-	driver = hapd_iface->bss[0]->driver;
-	drv_priv = hapd_iface->bss[0]->drv_priv;
 
 	hapd_iface->driver_ap_teardown =
 		!!(hapd_iface->drv_flags &
@@ -3647,7 +3682,8 @@
 		hostapd_free_hapd_data(hapd);
 	}
 
-	hostapd_deinit_driver(driver, drv_priv, hapd_iface);
+	hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+			      hapd_iface->bss[0]->drv_priv, hapd_iface);
 
 	/* From hostapd_cleanup_iface: These were initialized in
 	 * hostapd_setup_interface and hostapd_setup_interface_complete
@@ -4068,6 +4104,23 @@
 #endif /* CONFIG_IEEE80211BE */
 
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
+	ap_sta_clear_assoc_timeout(hapd, sta);
+
+#ifdef CONFIG_IEEE80211BE
+	if (ap_sta_is_mld(hapd, sta)) {
+		struct hostapd_data *bss;
+		struct sta_info *lsta;
+
+		for_each_mld_link(bss, hapd) {
+			if (bss == hapd)
+				continue;
+			lsta = ap_get_sta(bss, sta->addr);
+			if (lsta)
+				ap_sta_clear_assoc_timeout(bss, lsta);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	sta->post_csa_sa_query = 0;
 
 #ifdef CONFIG_P2P
@@ -4084,8 +4137,14 @@
 	/* Start accounting here, if IEEE 802.1X and WPA are not used.
 	 * IEEE 802.1X/WPA code will start accounting after the station has
 	 * been authorized. */
-	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
-		ap_sta_set_authorized(hapd, sta, 1);
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) {
+		if (ap_sta_set_authorized(hapd, sta, 1)) {
+			/* Update driver authorized flag for the STA to cover
+			 * the case where AP SME is in the driver and there is
+			 * no separate event for handling TX status event for
+			 * the (Re)Association Response frame. */
+			hostapd_set_sta_flags(hapd, sta);
+		}
 		os_get_reltime(&sta->connected_time);
 		accounting_sta_start(hapd, sta);
 	}
@@ -4200,8 +4259,8 @@
 }
 
 
-static int hostapd_build_beacon_data(struct hostapd_data *hapd,
-				     struct beacon_data *beacon)
+int hostapd_build_beacon_data(struct hostapd_data *hapd,
+			      struct beacon_data *beacon)
 {
 	struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
 	struct wpa_driver_ap_params params;
@@ -4382,6 +4441,10 @@
 	hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
 	hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
 
+#ifdef CONFIG_IEEE80211BE
+	conf->punct_bitmap = params->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
 	/* TODO: maybe call here hostapd_config_check here? */
 
 	return 0;
@@ -4394,9 +4457,6 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
-#ifdef CONFIG_IEEE80211BE
-	u16 old_punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
 	u8 chan, bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
@@ -4445,16 +4505,9 @@
 	if (ret)
 		return ret;
 
-#ifdef CONFIG_IEEE80211BE
-	old_punct_bitmap = iface->conf->punct_bitmap;
-	iface->conf->punct_bitmap = settings->punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
 
 	/* change back the configuration */
-#ifdef CONFIG_IEEE80211BE
-	iface->conf->punct_bitmap = old_punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
 	hostapd_change_config_freq(iface->bss[0], iface->conf,
 				   &old_freq, NULL);
 
@@ -4768,6 +4821,7 @@
 		struct cca_settings settings;
 		int ret;
 
+		os_memset(&settings, 0, sizeof(settings));
 		hostapd_cleanup_cca_params(bss);
 		bss->cca_color = r;
 		bss->cca_count = 10;
@@ -5042,6 +5096,28 @@
 		link_bss->drv_priv = NULL;
 }
 
+
+/* Return the number of currently active links, not counting the calling link
+ * (i.e., a value that is suitable to be used as-is in fields that use encoding
+ * of the value minus 1). */
+u8 hostapd_get_active_links(struct hostapd_data *hapd)
+{
+	struct hostapd_data *link_bss;
+	u8 active_links = 0;
+
+	if (!hapd || !hapd->conf->mld_ap)
+		return 0;
+
+	for_each_mld_link(link_bss, hapd) {
+		if (link_bss == hapd || !link_bss->started)
+			continue;
+
+		active_links++;
+	}
+
+	return active_links;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 5d91d85..bb85de9 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -768,6 +768,8 @@
 struct hostapd_iface *
 hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
 			   const char *config_fname, int debug);
+void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+				  struct hapd_interfaces *interfaces);
 void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 			   int reassoc);
 void hostapd_interface_deinit_free(struct hostapd_iface *iface);
@@ -859,8 +861,11 @@
 u8 hostapd_get_mld_id(struct hostapd_data *hapd);
 int hostapd_mld_add_link(struct hostapd_data *hapd);
 int hostapd_mld_remove_link(struct hostapd_data *hapd);
+u8 hostapd_get_active_links(struct hostapd_data *hapd);
 struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
 
+int hostapd_build_beacon_data(struct hostapd_data *hapd,
+			      struct beacon_data *beacon);
 void free_beacon_data(struct beacon_data *beacon);
 int hostapd_fill_cca_settings(struct hostapd_data *hapd,
 			      struct cca_settings *settings);
@@ -887,4 +892,11 @@
 
 u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
 
+static inline bool ap_pmf_enabled(struct hostapd_bss_config *conf)
+{
+	return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION;
+}
+
 #endif /* HOSTAPD_H */
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 05e9b9d..4ae3b6b 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -44,113 +44,6 @@
 }
 
 
-u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
-{
-	u8 *len;
-	u16 capab;
-
-	if (!hapd->conf->osen)
-		return eid;
-
-	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
-	len = eid++; /* to be filled */
-	WPA_PUT_BE24(eid, OUI_WFA);
-	eid += 3;
-	*eid++ = HS20_OSEN_OUI_TYPE;
-
-	/* Group Data Cipher Suite */
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
-	eid += RSN_SELECTOR_LEN;
-
-	/* Pairwise Cipher Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
-	eid += RSN_SELECTOR_LEN;
-
-	/* AKM Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
-	eid += RSN_SELECTOR_LEN;
-
-	/* RSN Capabilities */
-	capab = 0;
-	if (hapd->conf->wmm_enabled) {
-		/* 4 PTKSA replay counters when using WMM */
-		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
-	}
-	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-		capab |= WPA_CAPABILITY_MFPC;
-		if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
-			capab |= WPA_CAPABILITY_MFPR;
-	}
-#ifdef CONFIG_OCV
-	if (hapd->conf->ocv &&
-	    (hapd->iface->drv_flags2 &
-	     (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
-		capab |= WPA_CAPABILITY_OCVC;
-#endif /* CONFIG_OCV */
-	WPA_PUT_LE16(eid, capab);
-	eid += 2;
-
-	*len = eid - len - 1;
-
-	return eid;
-}
-
-
-int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
-			       u8 osu_method, const char *url)
-{
-	struct wpabuf *buf;
-	size_t len = 0;
-	int ret;
-
-	/* TODO: should refuse to send notification if the STA is not associated
-	 * or if the STA did not indicate support for WNM-Notification */
-
-	if (url) {
-		len = 1 + os_strlen(url);
-		if (5 + len > 255) {
-			wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
-				   "WNM-Notification: '%s'", url);
-			return -1;
-		}
-	}
-
-	buf = wpabuf_alloc(4 + 7 + len);
-	if (buf == NULL)
-		return -1;
-
-	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
-	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
-	wpabuf_put_u8(buf, 1); /* Dialog token */
-	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
-
-	/* Subscription Remediation subelement */
-	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
-	wpabuf_put_u8(buf, 5 + len);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
-	if (url) {
-		wpabuf_put_u8(buf, len - 1);
-		wpabuf_put_data(buf, url, len - 1);
-		wpabuf_put_u8(buf, osu_method);
-	} else {
-		/* Server URL and Server Method fields not included */
-		wpabuf_put_u8(buf, 0);
-	}
-
-	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
-				      wpabuf_head(buf), wpabuf_len(buf));
-
-	wpabuf_free(buf);
-
-	return ret;
-}
-
-
 int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
 					  const u8 *addr,
 					  const struct wpabuf *payload)
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 02d6759..cef3817 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -528,12 +528,6 @@
 	else
 		ieee80211n_scan_channels_5g(iface, &params);
 
-	params.link_id = -1;
-#ifdef CONFIG_IEEE80211BE
-	if (iface->bss[0]->conf->mld_ap)
-		params.link_id = iface->bss[0]->mld_link_id;
-#endif /* CONFIG_IEEE80211BE */
-
 	ret = hostapd_driver_scan(iface->bss[0], &params);
 	iface->num_ht40_scan_tries++;
 	os_free(params.freqs);
@@ -585,11 +579,6 @@
 	else
 		ieee80211n_scan_channels_5g(iface, &params);
 
-	params.link_id = -1;
-#ifdef CONFIG_IEEE80211BE
-	if (iface->bss[0]->conf->mld_ap)
-		params.link_id = iface->bss[0]->mld_link_id;
-#endif /* CONFIG_IEEE80211BE */
 	ret = hostapd_driver_scan(iface->bss[0], &params);
 	os_free(params.freqs);
 
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index d4552f2..523e0a3 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -26,6 +26,7 @@
 #include "common/wpa_common.h"
 #include "common/wpa_ctrl.h"
 #include "common/ptksa_cache.h"
+#include "common/nan_de.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -116,68 +117,61 @@
 }
 
 
-u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf)
 {
-	u8 *pos = eid;
-	int i, num, count;
-	int h2e_required;
+	u8 *pos = buf;
+	int i;
 
-	if (hapd->iface->current_rates == NULL)
-		return eid;
+	if (!hapd->iface->current_rates)
+		return 0;
 
-	*pos++ = WLAN_EID_SUPP_RATES;
-	num = hapd->iface->num_rates;
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
-		num++;
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
-		num++;
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
-		num++;
-#endif /* CONFIG_IEEE80211AX */
-	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
-		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
-		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
-	if (h2e_required)
-		num++;
-	if (num > 8) {
-		/* rest of the rates are encoded in Extended supported
-		 * rates element */
-		num = 8;
-	}
-
-	*pos++ = num;
-	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
-	     i++) {
-		count++;
+	for (i = 0; i < hapd->iface->num_rates; i++) {
 		*pos = hapd->iface->current_rates[i].rate / 5;
 		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
 			*pos |= 0x80;
 		pos++;
 	}
 
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
-		count++;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
-	}
 
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
-		count++;
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
-	}
 
 #ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
-		count++;
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
-	}
 #endif /* CONFIG_IEEE80211AX */
 
-	if (h2e_required && count < 8) {
-		count++;
+#ifdef CONFIG_SAE
+	if ((hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
+	    hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+	    wpa_key_mgmt_only_sae(hapd->conf->wpa_key_mgmt))
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
-	}
+#endif /* CONFIG_SAE */
+
+	return pos - buf;
+}
+
+
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 buf[100];
+	size_t len;
+
+	len = hostapd_supp_rates(hapd, buf);
+	if (len == 0)
+		return eid;
+	/* Only up to first eight values in this element */
+	if (len > 8)
+		len = 8;
+
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = len;
+	os_memcpy(pos, buf, len);
+	pos += len;
 
 	return pos;
 }
@@ -186,72 +180,19 @@
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
 {
 	u8 *pos = eid;
-	int i, num, count;
-	int h2e_required;
+	u8 buf[100];
+	size_t len;
 
-	hapd->conf->xrates_supported = false;
-	if (hapd->iface->current_rates == NULL)
+	len = hostapd_supp_rates(hapd, buf);
+	/* Starting from the 9th value for this element */
+	if (len <= 8)
 		return eid;
 
-	num = hapd->iface->num_rates;
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
-		num++;
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
-		num++;
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
-		num++;
-#endif /* CONFIG_IEEE80211AX */
-	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
-		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
-		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
-	if (h2e_required)
-		num++;
-	if (num <= 8)
-		return eid;
-	num -= 8;
-
 	*pos++ = WLAN_EID_EXT_SUPP_RATES;
-	*pos++ = num;
-	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
-	     i++) {
-		count++;
-		if (count <= 8)
-			continue; /* already in SuppRates IE */
-		*pos = hapd->iface->current_rates[i].rate / 5;
-		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
-			*pos |= 0x80;
-		pos++;
-	}
+	*pos++ = len - 8;
+	os_memcpy(pos, &buf[8], len - 8);
+	pos += len - 8;
 
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
-	}
-
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
-	}
-
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
-	}
-#endif /* CONFIG_IEEE80211AX */
-
-	if (h2e_required) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
-	}
-
-	hapd->conf->xrates_supported = true;
 	return pos;
 }
 
@@ -308,11 +249,6 @@
 	if (hapd->conf->wpa)
 		privacy = 1;
 
-#ifdef CONFIG_HS20
-	if (hapd->conf->osen)
-		privacy = 1;
-#endif /* CONFIG_HS20 */
-
 	if (privacy)
 		capab |= WLAN_CAPABILITY_PRIVACY;
 
@@ -547,6 +483,147 @@
 }
 
 
+static bool in_mac_addr_list(const u8 *list, unsigned int num, const u8 *addr)
+{
+	unsigned int i;
+
+	for (i = 0; list && i < num; i++) {
+		if (ether_addr_equal(&list[i * ETH_ALEN], addr))
+			return true;
+	}
+
+	return false;
+}
+
+
+static struct sae_password_entry *
+sae_password_find_pw(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sae_password_entry *pw = NULL;
+
+	if (!sta->sae || !sta->sae->tmp || !sta->sae->tmp->used_pw)
+		return NULL;
+
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (pw == sta->sae->tmp->used_pw)
+			return pw;
+	}
+
+	return NULL;
+}
+
+
+static bool is_other_sae_password(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  struct sae_password_entry *used_pw)
+{
+	struct sae_password_entry *pw;
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (pw == used_pw ||
+		    pw->identifier ||
+		    !is_broadcast_ether_addr(pw->peer_addr))
+			continue;
+
+		if (in_mac_addr_list(pw->success_mac,
+				     pw->num_success_mac,
+				     sta->addr))
+			return true;
+
+		if (!in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
+				      sta->addr))
+			return true;
+	}
+
+	return false;
+}
+
+
+static bool has_sae_success_seen(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	struct sae_password_entry *pw;
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (pw->identifier ||
+		    !is_broadcast_ether_addr(pw->peer_addr))
+			continue;
+
+		if (in_mac_addr_list(pw->success_mac,
+				     pw->num_success_mac,
+				     sta->addr))
+			return true;
+	}
+
+	return false;
+}
+
+
+static void sae_password_track_success(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+	struct sae_password_entry *pw;
+
+	if (!hapd->conf->sae_track_password)
+		return;
+
+	pw = sae_password_find_pw(hapd, sta);
+	if (!pw)
+		return;
+
+	if (in_mac_addr_list(pw->success_mac,
+			     pw->num_success_mac,
+			     sta->addr))
+		return;
+
+	if (!pw->success_mac) {
+		pw->success_mac = os_zalloc(hapd->conf->sae_track_password *
+					    ETH_ALEN);
+		if (!pw->success_mac)
+			return;
+		pw->num_success_mac = hapd->conf->sae_track_password;
+	}
+
+	os_memcpy(&pw->success_mac[pw->next_success_mac * ETH_ALEN], sta->addr,
+		  ETH_ALEN);
+	pw->next_success_mac = (pw->next_success_mac + 1) % pw->num_success_mac;
+}
+
+
+static bool sae_password_track_fail(struct hostapd_data *hapd,
+				    struct sta_info *sta)
+{
+	struct sae_password_entry *pw;
+
+	if (!hapd->conf->sae_track_password)
+		return false;
+
+	pw = sae_password_find_pw(hapd, sta);
+	if (!pw)
+		return false;
+
+	if (in_mac_addr_list(pw->fail_mac,
+			     pw->num_fail_mac,
+			     sta->addr))
+		return is_other_sae_password(hapd, sta, pw);
+
+	if (!pw->fail_mac) {
+		pw->fail_mac = os_zalloc(hapd->conf->sae_track_password *
+					 ETH_ALEN);
+		if (!pw->fail_mac)
+			return false;
+		pw->num_fail_mac = hapd->conf->sae_track_password;
+	}
+
+	os_memcpy(&pw->fail_mac[pw->next_fail_mac * ETH_ALEN], sta->addr,
+		  ETH_ALEN);
+	pw->next_fail_mac = (pw->next_fail_mac + 1) % pw->num_fail_mac;
+
+	return is_other_sae_password(hapd, sta, pw);
+}
+
+
 const char * sae_get_password(struct hostapd_data *hapd,
 			      struct sta_info *sta,
 			      const char *rx_id,
@@ -560,6 +637,45 @@
 	const struct sae_pk *pk = NULL;
 	struct hostapd_sta_wpa_psk_short *psk = NULL;
 
+	/* With sae_track_password functionality enabled, try to first find the
+	 * next viable wildcard-address password if a password identifier was
+	 * not used. Select an wildcard-addr entry if the STA is known to have
+	 * used it successfully before. If no such entry exists, pick a
+	 * wildcard-addr entry that does not have a failed entry tracked for the
+	 * STA. */
+	if (!rx_id && sta && hapd->conf->sae_track_password) {
+		struct sae_password_entry *success = NULL, *no_fail = NULL;
+
+		for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+			if (pw->identifier ||
+			    !is_broadcast_ether_addr(pw->peer_addr))
+				continue;
+			if (in_mac_addr_list(pw->success_mac,
+					     pw->num_success_mac,
+					     sta->addr)) {
+				success = pw;
+				break;
+			}
+
+			if (!no_fail &&
+			    !in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
+					      sta->addr))
+				no_fail = pw;
+		}
+
+		pw = success ? success : no_fail;
+		if (pw) {
+			password = pw->password;
+			pt = pw->pt;
+			if (!(hapd->conf->mesh & MESH_ENABLED))
+				pk = pw->pk;
+			goto found;
+		}
+	}
+
+	/* If sae_track_password functionality is not enabled or no suitable
+	 * password entry was found with it, pick the first entry that matches
+	 * the STA MAC address and password identifier (if used). */
 	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
 		if (!is_broadcast_ether_addr(pw->peer_addr) &&
 		    (!sta ||
@@ -576,12 +692,12 @@
 			pk = pw->pk;
 		break;
 	}
-	if (!password) {
+	if (!password && !rx_id) {
 		password = hapd->conf->ssid.wpa_passphrase;
 		pt = hapd->conf->ssid.pt;
 	}
 
-	if (!password && sta) {
+	if (!password && sta && !rx_id) {
 		for (psk = sta->psk; psk; psk = psk->next) {
 			if (psk->is_passphrase) {
 				password = psk->passphrase;
@@ -590,6 +706,7 @@
 		}
 	}
 
+found:
 	if (pw_entry)
 		*pw_entry = pw;
 	if (s_pt)
@@ -620,7 +737,8 @@
 #endif /* CONFIG_IEEE80211BE */
 
 	if (sta->sae->tmp) {
-		rx_id = sta->sae->tmp->pw_id;
+		rx_id = sta->sae->tmp->parsed_pw_id ?
+			sta->sae->tmp->parsed_pw_id : sta->sae->tmp->pw_id;
 		use_pt = sta->sae->h2e;
 #ifdef CONFIG_SAE_PK
 		os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
@@ -655,6 +773,9 @@
 		return NULL;
 	}
 
+	if (pw && sta->sae->tmp)
+		sta->sae->tmp->used_pw = pw;
+
 	if (pw && pw->vlan_id) {
 		if (!sta->sae->tmp) {
 			wpa_printf(MSG_INFO,
@@ -712,7 +833,8 @@
 	u16 status;
 
 	data = auth_build_sae_commit(hapd, sta, update, status_code);
-	if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+	if (!data && sta->sae->tmp &&
+	    (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id))
 		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 	if (data == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -972,7 +1094,8 @@
 	sta->sae->peer_commit_scalar = NULL;
 	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
 			       sta->sae->pmk, sta->sae->pmk_len,
-			       sta->sae->pmkid, sta->sae->akmp);
+			       sta->sae->pmkid, sta->sae->akmp,
+			       ap_sta_is_mld(hapd, sta));
 	sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
 }
 
@@ -1001,7 +1124,10 @@
 	switch (sta->sae->state) {
 	case SAE_NOTHING:
 		if (auth_transaction == 1) {
-			if (sta->sae->tmp) {
+			struct sae_temporary_data *tmp = sta->sae->tmp;
+			bool immediate_confirm;
+
+			if (tmp) {
 				sta->sae->h2e =
 					(status_code ==
 					 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
@@ -1011,8 +1137,21 @@
 			}
 			ret = auth_sae_send_commit(hapd, sta,
 						   !allow_reuse, status_code);
+			if (ret == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER)
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+					MACSTR, MAC2STR(sta->addr));
 			if (ret)
 				return ret;
+
+			if (tmp && tmp->parsed_pw_id && !tmp->pw_id) {
+				tmp->pw_id = tmp->parsed_pw_id;
+				tmp->parsed_pw_id = NULL;
+				wpa_printf(MSG_DEBUG,
+					   "SAE: Known Password Identifier bound to this STA: '%s'",
+					   tmp->pw_id);
+			}
+
 			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
 
 			if (sae_process_commit(sta->sae) < 0)
@@ -1028,8 +1167,22 @@
 			 * overridden with explicit configuration so that the
 			 * infrastructure BSS case sends both frames together.
 			 */
-			if ((hapd->conf->mesh & MESH_ENABLED) ||
-			    hapd->conf->sae_confirm_immediate) {
+			immediate_confirm = (hapd->conf->mesh & MESH_ENABLED) ||
+				hapd->conf->sae_confirm_immediate;
+
+			/* If sae_track_password is enabled and the STA has not
+			 * yet been tracked to having successfully completed
+			 * SAE authentication with the password that the AP
+			 * tries to use, do not send Confirm immediately to
+			 * avoid an explicit indication on the STA side on
+			 * password mismatch. */
+			if (immediate_confirm &&
+			    hapd->conf->sae_track_password &&
+			    (!sta->sae->tmp || !sta->sae->tmp->parsed_pw_id) &&
+			    !has_sae_success_seen(hapd, sta))
+				immediate_confirm = false;
+
+			if (immediate_confirm) {
 				/*
 				 * Send both Commit and Confirm immediately
 				 * based on SAE finite state machine
@@ -1374,6 +1527,8 @@
 			resp = -1;
 			goto remove_sta;
 		}
+		if (!hostapd_sae_pw_id_in_use(hapd->conf))
+			sta->sae->no_pw_id = 1;
 		sae_set_state(sta, SAE_NOTHING, "Init");
 		sta->sae->sync = 0;
 	}
@@ -1475,7 +1630,9 @@
 			 * previously set parameters. */
 			pos = mgmt->u.auth.variable;
 			end = ((const u8 *) mgmt) + len;
-			if (end - pos >= (int) sizeof(le16) &&
+			if ((!sta->sae->tmp ||
+			     !sta->sae->tmp->try_other_password) &&
+			    end - pos >= (int) sizeof(le16) &&
 			    sae_group_allowed(sta->sae, groups,
 					      WPA_GET_LE16(pos)) ==
 			    WLAN_STATUS_SUCCESS) {
@@ -1512,6 +1669,8 @@
 			sae_clear_retransmit_timer(hapd, sta);
 			sae_set_state(sta, SAE_NOTHING,
 				      "Unknown Password Identifier");
+			if (sta->sae->state == SAE_NOTHING)
+				goto reply;
 			goto remove_sta;
 		}
 
@@ -1599,9 +1758,21 @@
 
 			if (sae_check_confirm(sta->sae, var, var_len,
 					      NULL) < 0) {
+				if (sae_password_track_fail(hapd, sta)) {
+					wpa_printf(MSG_DEBUG,
+						   "SAE: Reject mismatching Confirm so that another password can be attempted by "
+						   MACSTR,
+						   MAC2STR(sta->addr));
+					if (sta->sae->tmp)
+						sta->sae->tmp->
+							try_other_password = 1;
+					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+					goto reply;
+				}
 				resp = WLAN_STATUS_CHALLENGE_FAIL;
 				goto reply;
 			}
+			sae_password_track_success(hapd, sta);
 			sta->sae->rc = peer_send_confirm;
 		}
 		resp = sae_sm_step(hapd, sta, auth_transaction,
@@ -1634,14 +1805,6 @@
 				data ? wpabuf_head(data) : (u8 *) "",
 				data ? wpabuf_len(data) : 0, "auth-sae");
 		sae_sme_send_external_auth_status(hapd, sta, resp);
-		if (sta->sae && sta->sae->tmp && sta->sae->tmp->pw_id &&
-		    resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER &&
-		    auth_transaction == 1) {
-			wpa_printf(MSG_DEBUG,
-				   "SAE: Clear stored password identifier since this SAE commit was not accepted");
-			os_free(sta->sae->tmp->pw_id);
-			sta->sae->tmp->pw_id = NULL;
-		}
 	}
 
 remove_sta:
@@ -1961,7 +2124,8 @@
 				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
 				  elems.rsnxe ? elems.rsnxe - 2 : NULL,
 				  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
-				  elems.mdie, elems.mdie_len, NULL, 0, NULL);
+				  elems.mdie, elems.mdie_len, NULL, 0, NULL,
+				  ap_sta_is_mld(hapd, sta));
 	resp = wpa_res_to_status_code(res);
 	if (resp != WLAN_STATUS_SUCCESS)
 		goto fail;
@@ -2238,7 +2402,7 @@
 				    sta->fils_erp_pmkid,
 				    session_timeout,
 				    wpa_auth_sta_key_mgmt(sta->wpa_sm),
-				    NULL) < 0) {
+				    NULL, ap_sta_is_mld(hapd, sta)) < 0) {
 				wpa_printf(MSG_ERROR,
 					   "FILS: Failed to add PMKSA cache entry based on ERP");
 			}
@@ -2506,6 +2670,8 @@
 	fils->erp_resp = erp_resp;
 	ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
 				    WLAN_STATUS_SUCCESS);
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
 	fils->erp_resp = NULL;
 
 	if (ret) {
@@ -2819,6 +2985,32 @@
 			     const struct ieee80211_mgmt *mgmt, size_t len,
 			     u16 trans_seq, u16 status)
 {
+	int ret;
+#ifdef CONFIG_P2P
+	struct ieee802_11_elems elems;
+
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "PASN: Too short Management frame");
+		return;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return;
+	}
+
+	if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    (P2P_ENABLED | P2P_GROUP_OWNER) &&
+	    hapd->p2p && elems.p2p2_ie && elems.p2p2_ie_len) {
+		p2p_pasn_auth_rx(hapd->p2p, mgmt, len, hapd->iface->freq);
+		return;
+	}
+#endif /* CONFIG_P2P */
+
 	if (hapd->conf->wpa != WPA_PROTO_RSN) {
 		wpa_printf(MSG_INFO, "PASN: RSN is not configured");
 		return;
@@ -2850,8 +3042,11 @@
 		hapd_initialize_pasn(hapd, sta);
 
 		hapd_pasn_update_params(hapd, sta, mgmt, len);
-		if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
-				       sta->addr, mgmt, len, false) < 0)
+		ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr,
+					 mgmt, len, false);
+		wpabuf_free(sta->pasn->frame);
+		sta->pasn->frame = NULL;
+		if (ret < 0)
 			ap_free_sta(hapd, sta);
 	} else if (trans_seq == 3) {
 		if (!sta->pasn) {
@@ -3369,7 +3564,10 @@
 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
 		return 0;
 
-	num_bss_nontx = hapd->iface->num_bss - 1;
+	if (hapd->iface->conf->mbssid_max > 0)
+		num_bss_nontx = hapd->iface->conf->mbssid_max - 1;
+	else
+		num_bss_nontx = hapd->iface->conf->num_bss - 1;
 	while (num_bss_nontx > 0) {
 		max_bssid_ind++;
 		num_bss_nontx >>= 1;
@@ -3781,7 +3979,8 @@
 	wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
 	wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
 	wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
-			    sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL);
+			    sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE,
+			    NULL, ap_sta_is_mld(hapd, sta));
 
 	return WLAN_STATUS_SUCCESS;
 }
@@ -3861,7 +4060,8 @@
 	rsn_ie_len += 2;
 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 				  hapd->iface->freq, rsn_ie, rsn_ie_len,
-				  NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
+				  NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL,
+				  ap_sta_is_mld(hapd, sta));
 	status = wpa_res_to_status_code(res);
 	if (status != WLAN_STATUS_SUCCESS)
 		goto end;
@@ -3913,8 +4113,58 @@
 #endif /* CONFIG_OWE */
 
 
+static bool hapd_is_known_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			      const u8 *ies, size_t ies_len)
+{
+	const u8 *ie, *pos, *end, *timestamp_pos, *mic;
+	u64 timestamp;
+	u8 mic_len;
+
+	if (!hapd->conf->known_sta_identification)
+		return false;
+
+	ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION);
+	if (!ie)
+		return false;
+
+	pos = ie + 3;
+	end = &ie[2 + ie[1]];
+	if (end - pos < 8 + 1)
+		return false; /* truncated element */
+	timestamp_pos = pos;
+	timestamp = WPA_GET_LE64(pos);
+	pos += 8;
+	mic_len = *pos++;
+	if (mic_len > end - pos)
+		return false; /* truncated element */
+	mic = pos;
+
+	wpa_printf(MSG_DEBUG, "RSN: STA " MACSTR
+		   " included Known STA Identification element: Timestamp=0x%llx mic_len=%u",
+		   MAC2STR(sta->addr), (unsigned long long) timestamp, mic_len);
+
+	if (timestamp <= sta->last_known_sta_id_timestamp) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Ignore reused or old Known STA Identification");
+		return false;
+	}
+
+	if (!wpa_auth_sm_known_sta_identification(sta->wpa_sm, timestamp_pos,
+						  mic, mic_len)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Ignore Known STA Identification with invalid MIC or due to KCK not available");
+		return false;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Valid Known STA Identification");
+	sta->last_known_sta_id_timestamp = timestamp;
+
+	return true;
+}
+
+
 static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
-			   int reassoc)
+			   int reassoc, const u8 *ies, size_t ies_len)
 {
 	if ((sta->flags &
 	     (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
@@ -3926,6 +4176,9 @@
 
 	if (!sta->sa_query_timed_out &&
 	    (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+		if (hapd_is_known_sta(hapd, sta, ies, ies_len))
+			return false;
+
 		/*
 		 * STA has already been associated with MFP and SA Query timeout
 		 * has not been reached. Reject the association attempt
@@ -4164,7 +4417,8 @@
 					  0,
 					  elems->mdie, elems->mdie_len,
 					  elems->owe_dh, elems->owe_dh_len,
-					  assoc_sta ? assoc_sta->wpa_sm : NULL);
+					  assoc_sta ? assoc_sta->wpa_sm : NULL,
+					  ap_sta_is_mld(hapd, sta));
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
@@ -4174,6 +4428,11 @@
 		else
 			sta->flags &= ~WLAN_STA_MFP;
 
+		if (wpa_auth_uses_spp_amsdu(sta->wpa_sm))
+			sta->flags |= WLAN_STA_SPP_AMSDU;
+		else
+			sta->flags &= ~WLAN_STA_SPP_AMSDU;
+
 #ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			if (!reassoc) {
@@ -4292,34 +4551,12 @@
 			ieee802_11_rsnx_capab_len(
 				elems->rsnxe, elems->rsnxe_len,
 				WLAN_RSNX_CAPAB_SSID_PROTECTION));
-#ifdef CONFIG_HS20
-	} else if (hapd->conf->osen) {
-		if (!elems->osen) {
-			hostapd_logger(
-				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-				HOSTAPD_LEVEL_INFO,
-				"No HS 2.0 OSEN element in association request");
-			return WLAN_STATUS_INVALID_IE;
-		}
-
-		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
-		if (sta->wpa_sm == NULL)
-			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-							sta->addr, NULL);
-		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
-				   "state machine");
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
-		}
-		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
-				      elems->osen - 2, elems->osen_len + 2) < 0)
-			return WLAN_STATUS_INVALID_IE;
-#endif /* CONFIG_HS20 */
 	} else
 		wpa_auth_sta_no_wpa(sta->wpa_sm);
 
 #ifdef CONFIG_P2P
-	p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+	if (ies && ies_len)
+		p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_HS20
@@ -4441,6 +4678,10 @@
 				hapd->conf->max_acceptable_idle_period;
 	}
 
+	if (elems->wfa_capab)
+		hostapd_wfa_capab(hapd, sta, elems->wfa_capab,
+				  elems->wfa_capab + elems->wfa_capab_len);
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -4565,9 +4806,8 @@
 	if (!mlbuf)
 		goto out;
 
-	if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
-					    hapd->mld_link_id, true) ==
-	    ParseFailed) {
+	if (ieee802_11_parse_link_assoc_req(&elems, mlbuf, hapd->mld_link_id,
+					    true) == ParseFailed) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: link: Failed to parse association request Multi-Link element");
 		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -4576,6 +4816,7 @@
 
 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
 	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+	ap_sta_set_mld(sta, true);
 
 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
 	if (status != WLAN_STATUS_SUCCESS) {
@@ -4583,8 +4824,6 @@
 		goto out;
 	}
 
-	ap_sta_set_mld(sta, true);
-
 	os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
 	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
 		struct mld_link_info *li = &sta->mld_info.links[i];
@@ -4752,6 +4991,7 @@
 	int set = 1;
 	const u8 *mld_link_addr = NULL;
 	bool mld_link_sta = false;
+	u16 eml_cap = 0;
 
 #ifdef CONFIG_IEEE80211BE
 	if (ap_sta_is_mld(hapd, sta)) {
@@ -4762,6 +5002,7 @@
 
 		if (hapd->mld_link_id != sta->mld_assoc_link_id)
 			set = 0;
+		eml_cap = sta->mld_info.common_info.eml_capa;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -4842,7 +5083,7 @@
 			    sta->he_6ghz_capab,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
-			    set, mld_link_addr, mld_link_sta)) {
+			    set, mld_link_addr, mld_link_sta, eml_cap)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
 			       "Could not %s STA to kernel driver",
@@ -5564,7 +5805,7 @@
 	}
 #endif /* CONFIG_MBO */
 
-	if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc)) {
+	if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc, pos, left)) {
 		resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 		goto fail;
 	}
@@ -5606,7 +5847,12 @@
 	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
 	if (resp != WLAN_STATUS_SUCCESS)
 		goto fail;
-	omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+#ifdef CONFIG_IEEE80211R_AP
+	if (reassoc && sta->auth_alg == WLAN_AUTH_FT)
+		omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+#endif /* CONFIG_IEEE80211R_AP */
+	if (hapd->conf->rsn_override_omit_rsnxe)
+		omit_rsnxe = 1;
 
 	if (hostapd_get_aid(hapd, sta) < 0) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -5998,10 +6244,51 @@
 }
 
 
+static int hostapd_action_vs(struct hostapd_data *hapd,
+			     struct sta_info *sta,
+			     const struct ieee80211_mgmt *mgmt, size_t len,
+			     unsigned int freq, bool protected)
+{
+	const u8 *pos, *end;
+	u32 oui_type;
+
+	pos = (const u8 *) &mgmt->u.action;
+	end = ((const u8 *) mgmt) + len;
+
+	if (end - pos < 1 + 4)
+		return -1;
+	pos++;
+
+	oui_type = WPA_GET_BE32(pos);
+	pos += 4;
+
+	switch (oui_type) {
+	case WFA_CAPAB_VENDOR_TYPE:
+		hostapd_wfa_capab(hapd, sta, pos, end);
+		return 0;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "Ignore unknown Vendor Specific Action frame OUI/type %08x%s",
+			   oui_type, protected ? " (protected)" : "");
+		break;
+	}
+
+	return -1;
+}
+
+
 static int robust_action_frame(u8 category)
 {
 	return category != WLAN_ACTION_PUBLIC &&
-		category != WLAN_ACTION_HT;
+		category != WLAN_ACTION_HT &&
+		category != WLAN_ACTION_UNPROTECTED_WNM &&
+		category != WLAN_ACTION_SELF_PROTECTED &&
+		category != WLAN_ACTION_UNPROTECTED_DMG &&
+		category != WLAN_ACTION_VHT &&
+		category != WLAN_ACTION_UNPROTECTED_S1G &&
+		category != WLAN_ACTION_HE &&
+		category != WLAN_ACTION_EHT &&
+		category != WLAN_ACTION_VENDOR_SPECIFIC;
 }
 
 
@@ -6148,8 +6435,8 @@
 			pos = mgmt->u.action.u.vs_public_action.variable;
 			end = ((const u8 *) mgmt) + len;
 			pos++;
-			hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, freq,
-					       pos, end - pos);
+			hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
+					       freq, pos, end - pos);
 			return 1;
 		}
 #endif /* CONFIG_NAN_USD */
@@ -6170,6 +6457,14 @@
 						   (u8 *) mgmt, len, freq) == 0)
 				return 1;
 		}
+		if (sta &&
+		    hostapd_action_vs(hapd, sta, mgmt, len, freq, false) == 0)
+			return 1;
+		break;
+	case WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED:
+		if (sta &&
+		    hostapd_action_vs(hapd, sta, mgmt, len, freq, true) == 0)
+			return 1;
 		break;
 #ifndef CONFIG_NO_RRM
 	case WLAN_ACTION_RADIO_MEASUREMENT:
@@ -6263,6 +6558,8 @@
 #ifdef CONFIG_NAN_USD
 	static const u8 nan_network_id[ETH_ALEN] =
 		{ 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
+	static const u8 p2p_network_id[ETH_ALEN] =
+		{ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 };
 #endif /* CONFIG_NAN_USD */
 
 	if (len < 24)
@@ -6295,6 +6592,10 @@
 	}
 
 	if (!is_broadcast_ether_addr(mgmt->bssid) &&
+#ifdef CONFIG_NAN_USD
+	    !nan_de_is_nan_network_id(mgmt->bssid) &&
+	    !nan_de_is_p2p_network_id(mgmt->bssid) &&
+#endif /* CONFIG_NAN_USD */
 #ifdef CONFIG_P2P
 	    /* Invitation responses can be sent with the peer MAC as BSSID */
 	    !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
@@ -6332,6 +6633,7 @@
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_NAN_USD
 	    !ether_addr_equal(mgmt->da, nan_network_id) &&
+	    !ether_addr_equal(mgmt->da, p2p_network_id) &&
 #endif /* CONFIG_NAN_USD */
 	    !ether_addr_equal(mgmt->da, hapd->own_addr)) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -6627,8 +6929,7 @@
 		new_assoc = 0;
 	sta->flags |= WLAN_STA_ASSOC;
 	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
-	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
-	     !hapd->conf->osen) ||
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
 	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
 	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
 	    sta->auth_alg == WLAN_AUTH_FILS_PK ||
@@ -7254,32 +7555,29 @@
 {
 	u8 bw;
 
-	/* bandwidth: 0: 40, 1: 80, 160, 80+80, 4: 320 as per
-	 * IEEE P802.11-REVme/D4.0, 9.4.2.159 and Table 9-314. */
+	/* bandwidth: 0: 40, 1: 80, 160, 80+80, 4 to 255 reserved as per
+	 * IEEE P802.11-REVme/D7.0, 9.4.2.159 and Table 9-316.
+	 */
 	switch (hapd->cs_freq_params.bandwidth) {
-	case 40:
-		bw = 0;
-		break;
-	case 80:
-		bw = 1;
-		break;
-	case 160:
-		bw = 1;
-		break;
 	case 320:
-		bw = 4;
-		break;
-	default:
-		/* not valid VHT bandwidth or not in CSA */
-		return eid;
-	}
+		/* As per IEEE P802.11be/D7.0, 35.15.3,
+		 * For EHT BSS operating channel width wider than 160 MHz,
+		 * the announced BSS bandwidth in the Wide Bandwidth
+		 * Channel Switch element is less than the BSS bandwidth
+		 * in the Bandwidth Indication element
+		 */
 
-	*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
-	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
-	*eid++ = bw; /* New Channel Width */
-	if (hapd->cs_freq_params.bandwidth == 160) {
+		/* Modifying the center frequency to 160 MHz */
+		if (hapd->cs_freq_params.channel < chan1)
+			chan1 -= 16;
+		else
+			chan1 += 16;
+
+		/* fallthrough */
+	case 160:
 		/* Update the CCFS0 and CCFS1 values in the element based on
-		 * IEEE P802.11-REVme/D4.0, Table 9-314 */
+		 * IEEE P802.11-REVme/D7.0, Table 9-316
+		 */
 
 		/* CCFS1 - The channel center frequency index of the 160 MHz
 		 * channel. */
@@ -7291,7 +7589,23 @@
 			chan1 -= 8;
 		else
 			chan1 += 8;
+
+		bw = 1;
+		break;
+	case 80:
+		bw = 1;
+		break;
+	case 40:
+		bw = 0;
+		break;
+	default:
+		/* not valid VHT bandwidth or not in CSA */
+		return eid;
 	}
+
+	*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
+	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+	*eid++ = bw; /* New Channel Width */
 	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
 	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
 
@@ -7305,9 +7619,9 @@
 static u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid,
 				      u8 chan1, u8 chan2)
 {
-	u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
+	u16 punct_bitmap = hapd->cs_freq_params.punct_bitmap;
 	struct ieee80211_bw_ind_element *bw_ind_elem;
-	size_t elen = 3;
+	size_t elen = 4;
 
 	if (hapd->cs_freq_params.bandwidth <= 160 && !punct_bitmap)
 		return eid;
@@ -7551,7 +7865,8 @@
 
 		/* If no TBTT was found, adjust the len and total_len since it
 		 * would have incremented before we checked all BSSs. */
-		if (!tbtt_count) {
+		if (!tbtt_count && len >= RNR_TBTT_HEADER_LEN &&
+		    total_len >= RNR_TBTT_HEADER_LEN) {
 			len -= RNR_TBTT_HEADER_LEN;
 			total_len -= RNR_TBTT_HEADER_LEN;
 		}
@@ -7561,7 +7876,8 @@
 
 	/* This is possible when in the re-built case and no suitable TBTT was
 	 * found. Adjust the length accordingly. */
-	if (!tbtt_count && total_tbtt_count) {
+	if (!tbtt_count && total_tbtt_count && len >= RNR_TBTT_HEADER_LEN &&
+	    total_len >= RNR_TBTT_HEADER_LEN) {
 		len -= RNR_TBTT_HEADER_LEN;
 		total_len -= RNR_TBTT_HEADER_LEN;
 	}
@@ -7940,8 +8256,8 @@
 }
 
 
-u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
-				size_t *current_len)
+static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+				       size_t *current_len)
 {
 	struct hostapd_iface *iface;
 	size_t i;
@@ -7965,8 +8281,8 @@
 }
 
 
-u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
-			 u8 *eid, size_t *current_len)
+static u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+				u8 *eid, size_t *current_len)
 {
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_iface *iface;
@@ -8077,8 +8393,8 @@
 					  size_t known_bss_len)
 {
 	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
-	size_t len, i;
-	u8 ext_capa[20];
+	size_t len, i, tx_xrate_len;
+	u8 ext_capa[20], buf[100];
 
 	/* Element ID: 1 octet
 	 * Length: 1 octet
@@ -8091,10 +8407,12 @@
 	 */
 	len = 1;
 
+	tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
+
 	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
 		struct hostapd_data *bss = hapd->iface->bss[i];
 		const u8 *auth, *rsn = NULL, *rsnx = NULL;
-		size_t nontx_profile_len, auth_len;
+		size_t nontx_profile_len, auth_len, xrate_len;
 		u8 ie_count = 0;
 
 		if (!bss || !bss->conf || !bss->started ||
@@ -8132,12 +8450,15 @@
 			ie_count++;
 		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
 			ie_count++;
-		if (bss->conf->xrates_supported)
-			nontx_profile_len += 8;
-		else if (hapd->conf->xrates_supported)
+
+		xrate_len = hostapd_eid_ext_supp_rates(bss, buf) - buf;
+
+		if (xrate_len)
+			nontx_profile_len += xrate_len;
+		else if (tx_xrate_len)
 			ie_count++;
 		if (ie_count)
-			nontx_profile_len += 4 + ie_count;
+			nontx_profile_len += 4 + ie_count + 1;
 
 		if (len + nontx_profile_len > 255)
 			break;
@@ -8211,21 +8532,26 @@
 				    const u8 *known_bss, size_t known_bss_len)
 {
 	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
-	size_t i;
+	size_t i, tx_xrate_len;
 	u8 *eid_len_offset, *max_bssid_indicator_offset;
+	u8 buf[100];
 
 	*eid++ = WLAN_EID_MULTIPLE_BSSID;
 	eid_len_offset = eid++;
 	max_bssid_indicator_offset = eid++;
 
+	tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
+
 	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
 		struct hostapd_data *bss = hapd->iface->bss[i];
 		struct hostapd_bss_config *conf;
+		struct hostapd_bss_config *tx_conf = tx_bss->conf;
 		u8 *eid_len_pos, *nontx_bss_start = eid;
 		const u8 *auth, *rsn = NULL, *rsnx = NULL;
 		u8 ie_count = 0, non_inherit_ie[3];
-		size_t auth_len = 0;
+		size_t auth_len = 0, xrate_len;
 		u16 capab_info;
+		u8 mbssindex = i;
 
 		if (!bss || !bss->conf || !bss->started ||
 		    mbssid_known_bss(i, known_bss, known_bss_len))
@@ -8246,10 +8572,14 @@
 		os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
 		eid += conf->ssid.ssid_len;
 
+		if (conf->mbssid_index &&
+		    conf->mbssid_index > tx_conf->mbssid_index)
+			mbssindex = conf->mbssid_index - tx_conf->mbssid_index;
+
 		*eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
 		if (frame_type == WLAN_FC_STYPE_BEACON) {
 			*eid++ = 3;
-			*eid++ = i; /* BSSID Index */
+			*eid++ = mbssindex; /* BSSID Index */
 			if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
 			    (conf->dtim_period % elem_count))
 				conf->dtim_period = elem_count;
@@ -8265,7 +8595,7 @@
 			/* Probe Request frame does not include DTIM Period and
 			 * DTIM Count fields. */
 			*eid++ = 1;
-			*eid++ = i; /* BSSID Index */
+			*eid++ = mbssindex; /* BSSID Index */
 		}
 
 		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
@@ -8284,12 +8614,13 @@
 		}
 
 		eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid);
+		xrate_len = hostapd_eid_ext_supp_rates(bss, eid) - eid;
+		eid += xrate_len;
 
 		/* List of Element ID values in increasing order */
 		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
 			non_inherit_ie[ie_count++] = WLAN_EID_RSN;
-		if (hapd->conf->xrates_supported &&
-		    !bss->conf->xrates_supported)
+		if (tx_xrate_len && !xrate_len)
 			non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
 		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
 			non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index abf48ab..2bcc29e 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -130,6 +130,8 @@
 int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
 				 enum ieee80211_op_mode mode);
 bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd);
+void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *pos, const u8 *end);
 u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 		       const u8 *buf, size_t len, int ack);
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index aea69ab..0d0a009 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -503,7 +503,7 @@
 
 	mld_cap = hapd->iface->mld_mld_capa;
 	max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
-	active_links = hapd->mld->num_links - 1;
+	active_links = hostapd_get_active_links(hapd);
 
 	if (active_links > max_simul_links) {
 		wpa_printf(MSG_ERROR,
@@ -1127,29 +1127,7 @@
 
 	/* Common Info Length and MLD MAC Address must always be present */
 	common_info_len = 1 + ETH_ALEN;
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
-		wpa_printf(MSG_DEBUG, "MLD: Link ID Info not expected");
-		goto out;
-	}
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
-		wpa_printf(MSG_DEBUG,
-			   "MLD: BSS Parameters Change Count not expected");
-		goto out;
-	}
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
-		wpa_printf(MSG_DEBUG,
-			   "MLD: Medium Synchronization Delay Information not expected");
-		goto out;
-	}
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
-		common_info_len += 2;
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA)
-		common_info_len += 2;
+	/* Ignore optional fields */
 
 	if (sizeof(*ml) + common_info_len > ml_len) {
 		wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
@@ -1159,7 +1137,7 @@
 	common_info = (struct eht_ml_basic_common_info *) ml->variable;
 
 	/* Common information length includes the length octet */
-	if (common_info->len != common_info_len) {
+	if (common_info->len < common_info_len) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: Invalid common info len=%u", common_info->len);
 		goto out;
@@ -1185,9 +1163,10 @@
 	size_t ml_len, common_info_len;
 	struct mld_link_info *link_info;
 	struct mld_info *info = &sta->mld_info;
-	const u8 *pos;
+	const u8 *pos, *end;
 	int ret = -1;
 	u16 ml_control;
+	const u8 *ml_end;
 
 	mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
 	if (!mlbuf)
@@ -1195,6 +1174,7 @@
 
 	ml = wpabuf_head(mlbuf);
 	ml_len = wpabuf_len(mlbuf);
+	ml_end = ((const u8 *) ml) + ml_len;
 
 	ml_control = le_to_host16(ml->ml_control);
 	if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
@@ -1236,6 +1216,12 @@
 		goto out;
 	}
 
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
+		common_info_len += 2;
+	} else {
+		wpa_printf(MSG_DEBUG, "MLD: EXT ML capabilities not present");
+	}
+
 	wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%zu",
 		   common_info_len);
 
@@ -1247,7 +1233,7 @@
 	common_info = (const struct eht_ml_basic_common_info *) ml->variable;
 
 	/* Common information length includes the length octet */
-	if (common_info->len != common_info_len) {
+	if (common_info->len < common_info_len) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: Invalid common info len=%u (expected %zu)",
 			   common_info->len, common_info_len);
@@ -1255,6 +1241,7 @@
 	}
 
 	pos = common_info->variable;
+	end = ((const u8 *) common_info) + common_info->len;
 
 	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
 		info->common_info.eml_capa = WPA_GET_LE16(pos);
@@ -1266,6 +1253,10 @@
 	info->common_info.mld_capa = WPA_GET_LE16(pos);
 	pos += 2;
 
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
+		pos += 2;
+	}
+
 	wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
 		   MAC2STR(info->common_info.mld_addr),
 		   info->common_info.eml_capa, info->common_info.mld_capa);
@@ -1283,21 +1274,22 @@
 
 	info->links[hapd->mld_link_id].valid = 1;
 
-	/* Parse the link info field */
-	ml_len -= sizeof(*ml) + common_info_len;
-
-	while (ml_len > 2) {
+	/* Parse the Link Info field that starts after the end of the variable
+	 * length Common Info field. */
+	pos = end;
+	while (ml_end - pos > 2) {
 		size_t sub_elem_len = *(pos + 1);
 		size_t sta_info_len;
 		u16 control;
+		const u8 *sub_elem_end;
 
 		wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
 			   sub_elem_len);
 
-		if (2 + sub_elem_len > ml_len) {
+		if (2 + sub_elem_len > (size_t) (ml_end - pos)) {
 			wpa_printf(MSG_DEBUG,
 				   "MLD: Invalid link info len: %zu %zu",
-				   2 + sub_elem_len, ml_len);
+				   2 + sub_elem_len, ml_end - pos);
 			goto out;
 		}
 
@@ -1306,7 +1298,6 @@
 				   "MLD: Skip vendor specific subelement");
 
 			pos += 2 + sub_elem_len;
-			ml_len -= 2 + sub_elem_len;
 			continue;
 		}
 
@@ -1315,16 +1306,15 @@
 				   "MLD: Skip unknown Multi-Link element subelement ID=%u",
 				   *pos);
 			pos += 2 + sub_elem_len;
-			ml_len -= 2 + sub_elem_len;
 			continue;
 		}
 
 		/* Skip the subelement ID and the length */
 		pos += 2;
-		ml_len -= 2;
+		sub_elem_end = pos + sub_elem_len;
 
 		/* Get the station control field */
-		if (sub_elem_len < 2) {
+		if (sub_elem_end - pos < 2) {
 			wpa_printf(MSG_DEBUG,
 				   "MLD: Too short Per-STA Profile subelement");
 			goto out;
@@ -1333,8 +1323,6 @@
 		link_info = &info->links[control &
 					 EHT_PER_STA_CTRL_LINK_ID_MSK];
 		pos += 2;
-		ml_len -= 2;
-		sub_elem_len -= 2;
 
 		if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
 			wpa_printf(MSG_DEBUG,
@@ -1367,15 +1355,19 @@
 
 		sta_info_len += link_info->nstr_bitmap_len;
 
-		if (sta_info_len > ml_len || sta_info_len != *pos ||
-		    sta_info_len > sub_elem_len) {
+		if (sta_info_len > (size_t) (sub_elem_end - pos) ||
+		    sta_info_len > *pos ||
+		    *pos > sub_elem_end - pos ||
+		    sta_info_len > (size_t) (sub_elem_end - pos)) {
 			wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
 			goto out;
 		}
 
+		sta_info_len = *pos;
+		end = pos + sta_info_len;
+
 		/* skip the length */
 		pos++;
-		ml_len--;
 
 		/* get the link address */
 		os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
@@ -1385,27 +1377,20 @@
 			   MAC2STR(link_info->peer_addr));
 
 		pos += ETH_ALEN;
-		ml_len -= ETH_ALEN;
 
 		/* Get the NSTR bitmap */
 		if (link_info->nstr_bitmap_len) {
 			os_memcpy(link_info->nstr_bitmap, pos,
 				  link_info->nstr_bitmap_len);
 			pos += link_info->nstr_bitmap_len;
-			ml_len -= link_info->nstr_bitmap_len;
 		}
 
-		sub_elem_len -= sta_info_len;
+		pos = end;
 
-		wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
-		if (sub_elem_len > ml_len)
-			goto out;
-
-		if (sub_elem_len > 2)
+		if (sub_elem_end - pos >= 2)
 			link_info->capability = WPA_GET_LE16(pos);
 
-		pos += sub_elem_len;
-		ml_len -= sub_elem_len;
+		pos = sub_elem_end;
 
 		wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
 			   ", nstr bitmap len=%zu",
@@ -1415,12 +1400,6 @@
 		link_info->valid = true;
 	}
 
-	if (ml_len) {
-		wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
-			   ml_len);
-		goto out;
-	}
-
 	ret = hostapd_mld_validate_assoc_info(hapd, sta);
 out:
 	wpabuf_free(mlbuf);
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index a2deda6..cc731b9 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -221,7 +221,7 @@
 
 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
 		enum oper_chan_width oper_chwidth =
-			hostapd_get_oper_chwidth(hapd->iconf);
+			hapd->iconf->he_oper_chwidth;
 		u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
 		u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
 		u8 control;
@@ -229,6 +229,9 @@
 		u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
 
 		if (punct_bitmap) {
+			oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+			seg0 = hostapd_get_oper_centr_freq_seg0_idx(
+				hapd->iconf);
 			punct_update_legacy_bw(punct_bitmap,
 					       hapd->iconf->channel,
 					       &oper_chwidth, &seg0, &seg1);
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 4c39e40..9bc0461 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -457,6 +457,7 @@
 	iface->num_sta_ht40_intolerant--;
 
 	if (iface->num_sta_ht40_intolerant == 0 &&
+	    iface->conf->obss_interval &&
 	    (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
 		unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 3dd3a6a..986b7b8 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -476,6 +476,17 @@
 			*pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
 #endif /* CONFIG_SAE_PK */
 		break;
+	case 12: /* Bits 96-103 */
+		if (hapd->iconf->peer_to_peer_twt)
+			*pos |= 0x10; /* Bit 100 - Peer to Peer TWT */
+		if (hapd->conf->known_sta_identification)
+			*pos |= 0x40; /* Bit 102 - Known STA Identification
+				       * Enabled */
+		break;
+	case 13: /* Bits 104-111 */
+		if (hapd->iconf->channel_usage)
+			*pos |= 0x01; /* Bit 104 - Channel Usage support */
+		break;
 	}
 }
 
@@ -1034,7 +1045,8 @@
 	int requested_bw;
 
 	if (sta->ht_capabilities)
-		ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
+		ht_40mhz = !!(le_to_host16(sta->ht_capabilities->
+					   ht_capabilities_info) &
 			      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
 
 	if (sta->vht_operation) {
@@ -1070,9 +1082,9 @@
 		 * normal clients), use it to determine the supported channel
 		 * bandwidth.
 		 */
-		vht_chanwidth = capab->vht_capabilities_info &
+		vht_chanwidth = le_to_host32(capab->vht_capabilities_info) &
 			VHT_CAP_SUPP_CHAN_WIDTH_MASK;
-		vht_80p80 = capab->vht_capabilities_info &
+		vht_80p80 = le_to_host32(capab->vht_capabilities_info) &
 			VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 
 		/* TODO: Also take into account Extended NSS BW Support field */
@@ -1128,6 +1140,9 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 	if (hapd->conf->ssid_protection)
 		capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+	if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) &&
+	    hapd->conf->spp_amsdu)
+		capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
 
 	if (!capab)
 		return eid; /* no supported extended RSN capabilities */
@@ -1226,3 +1241,47 @@
 		(hapd->iface->drv_flags2 &
 		 WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER);
 }
+
+
+static void hostapd_wfa_gen_capab(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  const u8 *capab, size_t len)
+{
+	char *hex;
+	size_t buflen;
+
+	wpa_printf(MSG_DEBUG,
+		   "WFA: Received indication of generational capabilities from "
+		   MACSTR, MAC2STR(sta->addr));
+	wpa_hexdump(MSG_DEBUG, "WFA: Generational Capabilities", capab, len);
+
+	buflen = 2 * len + 1;
+	hex = os_zalloc(buflen);
+	if (!hex)
+		return;
+	wpa_snprintf_hex(hex, buflen, capab, len);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WFA_GEN_CAPAB_RX MACSTR " %s",
+		MAC2STR(sta->addr), hex);
+	os_free(hex);
+}
+
+
+void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *pos, const u8 *end)
+{
+	u8 capab_len;
+	const u8 *gen_capa;
+
+	if (end - pos < 1)
+		return;
+	capab_len = *pos++;
+	if (capab_len > end - pos)
+		return;
+	pos += capab_len; /* skip the Capabilities field */
+
+	/* Wi-Fi Alliance Capabilities attributes use a header that is similar
+	 * to the one used in Information Elements. */
+	gen_capa = get_ie(pos, end - pos, WFA_CAPA_ATTR_GENERATIONAL_CAPAB);
+	if (gen_capa)
+		hostapd_wfa_gen_capab(hapd, sta, gen_capa + 2, gen_capa[1]);
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index f4103ac..efdf607 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -45,7 +45,7 @@
 #endif /* CONFIG_HS20 */
 static bool ieee802_1x_finished(struct hostapd_data *hapd,
 				struct sta_info *sta, int success,
-				int remediation, bool logoff);
+				bool logoff);
 
 
 static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@@ -149,7 +149,7 @@
 					     bool authorized)
 {
 #ifdef CONFIG_IEEE80211BE
-	unsigned int i, link_id;
+	unsigned int i;
 
 	if (!hostapd_is_mld_ap(hapd))
 		return;
@@ -161,32 +161,30 @@
 	if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id)
 		return;
 
-	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-		struct mld_link_info *link = &sta->mld_info.links[link_id];
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		struct sta_info *tmp_sta;
+		struct mld_link_info *link;
+		struct hostapd_data *tmp_hapd =
+			hapd->iface->interfaces->iface[i]->bss[0];
 
+		if (!hostapd_is_ml_partner(hapd, tmp_hapd))
+			continue;
+
+		link = &sta->mld_info.links[tmp_hapd->mld_link_id];
 		if (!link->valid)
 			continue;
 
-		for (i = 0; i < hapd->iface->interfaces->count; i++) {
-			struct sta_info *tmp_sta;
-			struct hostapd_data *tmp_hapd =
-				hapd->iface->interfaces->iface[i]->bss[0];
-
-			if (!hostapd_is_ml_partner(hapd, tmp_hapd))
+		for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+		     tmp_sta = tmp_sta->next) {
+			if (tmp_sta == sta ||
+			    tmp_sta->mld_assoc_link_id !=
+			    sta->mld_assoc_link_id ||
+			    tmp_sta->aid != sta->aid)
 				continue;
 
-			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
-			     tmp_sta = tmp_sta->next) {
-				if (tmp_sta == sta ||
-				    tmp_sta->mld_assoc_link_id !=
-				    sta->mld_assoc_link_id ||
-				    tmp_sta->aid != sta->aid)
-					continue;
-
-				ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
-							  authorized, true);
-				break;
-			}
+			ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
+						  authorized, true);
+			break;
 		}
 	}
 #endif /* CONFIG_IEEE80211BE */
@@ -453,8 +451,7 @@
 		return -1;
 	}
 
-	suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
-				     hapd->conf->osen) ?
+	suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2)) ?
 				    WPA_PROTO_RSN : WPA_PROTO_WPA,
 				    hapd->conf->wpa_group);
 	if (!hostapd_config_get_radius_attr(req_attr,
@@ -583,7 +580,7 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
-	if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
+	if (hapd->conf->wpa && sta->wpa_sm &&
 	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
 		return -1;
 
@@ -1125,7 +1122,7 @@
 	struct rsn_pmksa_cache_entry *pmksa;
 	int key_mgmt;
 
-	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
 	    !hapd->conf->wps_state)
 		return;
 
@@ -1185,7 +1182,7 @@
 		return;
 	}
 
-	if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
+	if (!hapd->conf->ieee802_1x &&
 	    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
 		wpa_printf(MSG_DEBUG,
 			   "IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used");
@@ -1252,6 +1249,29 @@
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "received EAPOL-Start from STA");
+#ifdef CONFIG_IEEE80211R_AP
+		if (hapd->conf->wpa &&
+		    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm &&
+		    ((wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) &&
+		      (sta->flags & WLAN_STA_AUTHORIZED)) ||
+		     sta->auth_alg == WLAN_AUTH_FT)) {
+			/* When FT is used, reauthentication to generate a new
+			 * PMK-R0 would be complicated since the current AP
+			 * might not be the one with which the currently used
+			 * PMK-R0 was generated. IEEE Std 802.11-2020, 13.4.2
+			 * (FT initial mobility domain association in an RSN)
+			 * mandates STA to perform a new FT initial mobility
+			 * domain association whenever its Supplicant would
+			 * trigger sending of an EAPOL-Start frame. As such,
+			 * this EAPOL-Start frame should not have been sent.
+			 * Discard it to avoid unexpected behavior. */
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "discard unexpected EAPOL-Start from STA that uses FT");
+			break;
+		}
+#endif /* CONFIG_IEEE80211R_AP */
 		sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
 		pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 		if (pmksa) {
@@ -1337,7 +1357,7 @@
 	}
 #endif /* CONFIG_WPS */
 
-	if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
+	if (!force_1x && !hapd->conf->ieee802_1x) {
 		wpa_printf(MSG_DEBUG,
 			   "IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS");
 		/*
@@ -1471,10 +1491,6 @@
 {
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-#ifdef CONFIG_HS20
-	eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
-#endif /* CONFIG_HS20 */
-
 	if (sta->pending_eapol_rx) {
 		wpabuf_free(sta->pending_eapol_rx->buf);
 		os_free(sta->pending_eapol_rx);
@@ -1750,32 +1766,6 @@
 
 #ifdef CONFIG_HS20
 
-static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
-{
-	sta->remediation = 1;
-	os_free(sta->remediation_url);
-	if (len > 2) {
-		sta->remediation_url = os_malloc(len);
-		if (!sta->remediation_url)
-			return;
-		sta->remediation_method = pos[0];
-		os_memcpy(sta->remediation_url, pos + 1, len - 1);
-		sta->remediation_url[len - 1] = '\0';
-		wpa_printf(MSG_DEBUG,
-			   "HS 2.0: Subscription remediation needed for "
-			   MACSTR " - server method %u URL %s",
-			   MAC2STR(sta->addr), sta->remediation_method,
-			   sta->remediation_url);
-	} else {
-		sta->remediation_url = NULL;
-		wpa_printf(MSG_DEBUG,
-			   "HS 2.0: Subscription remediation needed for "
-			   MACSTR, MAC2STR(sta->addr));
-	}
-	/* TODO: assign the STA into remediation VLAN or add filtering */
-}
-
-
 static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
 				       struct sta_info *sta, const u8 *pos,
 				       size_t len)
@@ -1891,7 +1881,6 @@
 	size_t len;
 
 	buf = NULL;
-	sta->remediation = 0;
 	sta->hs20_deauth_requested = 0;
 	sta->hs20_deauth_on_ack = 0;
 
@@ -1916,9 +1905,6 @@
 			continue; /* invalid WFA VSA */
 
 		switch (type) {
-		case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
-			ieee802_1x_hs20_sub_rem(sta, pos, sublen);
-			break;
 		case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
 			ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
 			break;
@@ -2347,7 +2333,7 @@
 
 
 static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
-				 int preauth, int remediation, bool logoff)
+				 int preauth, bool logoff)
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
@@ -2357,7 +2343,7 @@
 		return false;
 	}
 
-	return ieee802_1x_finished(hapd, sta, success, remediation, logoff);
+	return ieee802_1x_finished(hapd, sta, success, logoff);
 }
 
 
@@ -2399,7 +2385,6 @@
 	user->force_version = eap_user->force_version;
 	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
-	user->remediation = eap_user->remediation;
 	rv = 0;
 
 out:
@@ -2557,8 +2542,6 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
-	dl_list_init(&hapd->erp_keys);
-
 	os_memset(&conf, 0, sizeof(conf));
 	conf.eap_cfg = hapd->eap_cfg;
 	conf.ctx = hapd;
@@ -3041,17 +3024,6 @@
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
 
-	if (sta->remediation) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
-			   MACSTR " to indicate Subscription Remediation",
-			   MAC2STR(sta->addr));
-		hs20_send_wnm_notification(hapd, sta->addr,
-					   sta->remediation_method,
-					   sta->remediation_url);
-		os_free(sta->remediation_url);
-		sta->remediation_url = NULL;
-	}
-
 	if (sta->hs20_deauth_req) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
 			   MACSTR " to indicate imminent deauthentication",
@@ -3074,7 +3046,7 @@
 
 static bool ieee802_1x_finished(struct hostapd_data *hapd,
 				struct sta_info *sta, int success,
-				int remediation, bool logoff)
+				bool logoff)
 {
 	const u8 *key;
 	size_t len;
@@ -3084,16 +3056,7 @@
 	struct os_reltime now, remaining;
 
 #ifdef CONFIG_HS20
-	if (remediation && !sta->remediation) {
-		sta->remediation = 1;
-		os_free(sta->remediation_url);
-		sta->remediation_url =
-			os_strdup(hapd->conf->subscr_remediation_url);
-		sta->remediation_method = 1; /* SOAP-XML SPP */
-	}
-
-	if (success && (sta->remediation || sta->hs20_deauth_req ||
-			sta->hs20_t_c_filtering)) {
+	if (success && (sta->hs20_deauth_req || sta->hs20_t_c_filtering)) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
 			   MACSTR " in 100 ms", MAC2STR(sta->addr));
 		eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
@@ -3114,7 +3077,7 @@
 	} else {
 		session_timeout = dot11RSNAConfigPMKLifetime;
 	}
-	if (success && key && len >= PMK_LEN && !sta->remediation &&
+	if (success && key && len >= PMK_LEN &&
 	    !sta->hs20_deauth_requested &&
 	    wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
 			       sta->eapol_sm) == 0) {
diff --git a/src/ap/nan_usd_ap.c b/src/ap/nan_usd_ap.c
index 570abfc..4623f67 100644
--- a/src/ap/nan_usd_ap.c
+++ b/src/ap/nan_usd_ap.c
@@ -29,8 +29,10 @@
 		   wpabuf_len(buf));
 
 	/* TODO: Force use of OFDM */
-	return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
-				       wpabuf_head(buf), wpabuf_len(buf));
+	return hostapd_drv_send_action_forced_addr3(hapd, hapd->iface->freq, 0,
+						    dst, bssid,
+						    wpabuf_head(buf),
+						    wpabuf_len(buf));
 }
 
 
@@ -158,7 +160,7 @@
 	cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
 	cb.receive = hostapd_nan_de_receive;
 
-	hapd->nan_de = nan_de_init(hapd->own_addr, false, true, &cb);
+	hapd->nan_de = nan_de_init(hapd->own_addr, false, true, 0, &cb);
 	if (!hapd->nan_de)
 		return -1;
 	return 0;
@@ -173,11 +175,12 @@
 
 
 void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
-			    unsigned int freq, const u8 *buf, size_t len)
+			    const u8 *a3, unsigned int freq,
+			    const u8 *buf, size_t len)
 {
 	if (!hapd->nan_de)
 		return;
-	nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
+	nan_de_rx_sdf(hapd->nan_de, src, a3, freq, buf, len);
 }
 
 
@@ -258,7 +261,8 @@
 int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
 			     const struct wpabuf *ssi,
 			     const struct wpabuf *elems,
-			     const u8 *peer_addr, u8 req_instance_id)
+			     const u8 *peer_addr,
+			     u8 req_instance_id)
 {
 	if (!hapd->nan_de)
 		return -1;
diff --git a/src/ap/nan_usd_ap.h b/src/ap/nan_usd_ap.h
index 0571643..b7e8b76 100644
--- a/src/ap/nan_usd_ap.h
+++ b/src/ap/nan_usd_ap.h
@@ -16,6 +16,7 @@
 int hostapd_nan_usd_init(struct hostapd_data *hapd);
 void hostapd_nan_usd_deinit(struct hostapd_data *hapd);
 void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
+			    const u8 *a3,
 			    unsigned int freq, const u8 *buf, size_t len);
 void hostapd_nan_usd_flush(struct hostapd_data *hapd);
 int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 2fce838..0715540 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -644,29 +644,25 @@
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
  * @buf: Buffer for the list
  * @len: Length of the buffer
+ * @index: Externally stored index counter
  * Returns: Number of bytes written to buffer
  *
  * This function is used to generate a text format representation of the
  * current PMKSA cache contents for the ctrl_iface PMKSA command.
  */
-int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len,
+			  int *index)
 {
-	int i, ret;
+	int ret;
 	char *pos = buf;
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
 
 	os_get_reltime(&now);
-	ret = os_snprintf(pos, buf + len - pos,
-			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
-	if (os_snprintf_error(buf + len - pos, ret))
-		return pos - buf;
-	pos += ret;
-	i = 0;
 	entry = pmksa->pmksa;
 	while (entry) {
 		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
-				  i, MAC2STR(entry->spa));
+				  *index, MAC2STR(entry->spa));
 		if (os_snprintf_error(buf + len - pos, ret))
 			return pos - buf;
 		pos += ret;
@@ -679,6 +675,7 @@
 			return pos - buf;
 		pos += ret;
 		entry = entry->next;
+		(*index)++;
 	}
 	return pos - buf;
 }
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index e38e7ec..ade1c49 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -75,7 +75,8 @@
 			    struct rsn_pmksa_cache_entry *entry);
 int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
 					   struct radius_das_attrs *attr);
-int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len,
+			  int *index);
 void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
 int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
 			       char *buf, size_t len);
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index aa7e156..8aa96d2 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -334,6 +334,7 @@
 	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
 	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
+	ap_sta_clear_assoc_timeout(hapd, sta);
 	sae_clear_retransmit_timer(hapd, sta);
 
 	ieee802_1x_free_station(hapd, sta);
@@ -418,7 +419,6 @@
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
-	os_free(sta->remediation_url);
 	os_free(sta->t_c_url);
 	wpabuf_free(sta->hs20_deauth_req);
 	os_free(sta->hs20_session_info_url);
@@ -791,6 +791,25 @@
 }
 
 
+static void ap_sta_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	if (sta->flags & WLAN_STA_ASSOC)
+		return;
+
+	wpa_printf(MSG_DEBUG, "STA " MACSTR
+		   " did not complete association in time - remove it",
+		   MAC2STR(sta->addr));
+	if (sta->flags & WLAN_STA_AUTH)
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+	else
+		ap_free_sta(hapd, sta);
+}
+
+
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
 {
 	struct sta_info *sta;
@@ -856,6 +875,9 @@
 				      &sta->probe_ie_taxonomy);
 #endif /* CONFIG_TAXONOMY */
 
+	if (!(hapd->conf->mesh & MESH_ENABLED))
+		eloop_register_timeout(60, 0, ap_sta_assoc_timeout, hapd, sta);
+
 	return sta;
 }
 
@@ -924,7 +946,8 @@
 
 
 static void ap_sta_disconnect_common(struct hostapd_data *hapd,
-				     struct sta_info *sta, unsigned int timeout)
+				     struct sta_info *sta, unsigned int timeout,
+				     bool free_1x)
 {
 	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 
@@ -938,7 +961,8 @@
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(timeout, 0, ap_handle_timer, hapd, sta);
 	accounting_sta_stop(hapd, sta);
-	ieee802_1x_free_station(hapd, sta);
+	if (free_1x)
+		ieee802_1x_free_station(hapd, sta);
 #ifdef CONFIG_IEEE80211BE
 	if (!hapd->conf->mld_ap ||
 	    hapd->mld_link_id == sta->mld_assoc_link_id) {
@@ -953,6 +977,18 @@
 }
 
 
+static void ap_sta_disassociate_common(struct hostapd_data *hapd,
+				       struct sta_info *sta, u16 reason)
+{
+	sta->disassoc_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
 static void ap_sta_handle_disassociate(struct hostapd_data *hapd,
 				       struct sta_info *sta, u16 reason)
 {
@@ -970,14 +1006,9 @@
 		sta->timeout_next = STA_DEAUTH;
 	}
 
-	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC);
-
-	sta->disassoc_reason = reason;
-	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
-	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
-	eloop_register_timeout(hapd->iface->drv_flags &
-			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
-			       ap_sta_disassoc_cb_timeout, hapd, sta);
+	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC,
+				 true);
+	ap_sta_disassociate_common(hapd, sta, reason);
 }
 
 
@@ -993,25 +1024,9 @@
 }
 
 
-static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+static void ap_sta_deauthenticate_common(struct hostapd_data *hapd,
 					 struct sta_info *sta, u16 reason)
 {
-	if (hapd->iface->current_mode &&
-	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
-		/* Deauthentication is not used in DMG/IEEE 802.11ad;
-		 * disassociate the STA instead. */
-		ap_sta_disassociate(hapd, sta, reason);
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
-		   hapd->conf->iface, MAC2STR(sta->addr));
-
-	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
-
-	sta->timeout_next = STA_REMOVE;
-	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
-
 	sta->deauth_reason = reason;
 	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
@@ -1021,9 +1036,48 @@
 }
 
 
+static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+					 struct sta_info *sta, u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+
+	sta->timeout_next = STA_REMOVE;
+	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH,
+				 true);
+	ap_sta_deauthenticate_common(hapd, sta, reason);
+}
+
+
+static void ap_sta_handle_disconnect(struct hostapd_data *hapd,
+				     struct sta_info *sta, u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: disconnect STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	sta->timeout_next = STA_REMOVE;
+
+	sta->deauth_reason = reason;
+	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH,
+				 false);
+	ap_sta_deauthenticate_common(hapd, sta, reason);
+}
+
+
+enum ap_sta_disconnect_op {
+	AP_STA_DEAUTHENTICATE,
+	AP_STA_DISASSOCIATE,
+	AP_STA_DISCONNECT
+};
+
 static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
 				 struct sta_info *sta, u16 reason,
-				 bool disassoc)
+				 enum ap_sta_disconnect_op op)
 {
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_data *assoc_hapd, *tmp_hapd;
@@ -1072,25 +1126,30 @@
 				    tmp_sta->aid != assoc_sta->aid)
 					continue;
 
-				if (disassoc)
+				if (op == AP_STA_DISASSOCIATE)
 					ap_sta_handle_disassociate(tmp_hapd,
 								   tmp_sta,
 								   reason);
-				else
+				else if (op == AP_STA_DEAUTHENTICATE)
 					ap_sta_handle_deauthenticate(tmp_hapd,
 								     tmp_sta,
 								     reason);
-
+				else
+					ap_sta_handle_disconnect(tmp_hapd,
+								 tmp_sta,
+								 reason);
 				break;
 			}
 		}
 	}
 
 	/* Disconnect the station on which the association was performed. */
-	if (disassoc)
+	if (op == AP_STA_DISASSOCIATE)
 		ap_sta_handle_disassociate(assoc_hapd, assoc_sta, reason);
-	else
+	else if (op == AP_STA_DEAUTHENTICATE)
 		ap_sta_handle_deauthenticate(assoc_hapd, assoc_sta, reason);
+	else
+		ap_sta_handle_disconnect(assoc_hapd, assoc_sta, reason);
 
 	return true;
 #else /* CONFIG_IEEE80211BE */
@@ -1102,7 +1161,7 @@
 void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
 			 u16 reason)
 {
-	if (ap_sta_ml_disconnect(hapd, sta, reason, true))
+	if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISASSOCIATE))
 		return;
 
 	ap_sta_handle_disassociate(hapd, sta, reason);
@@ -1112,7 +1171,15 @@
 void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 reason)
 {
-	if (ap_sta_ml_disconnect(hapd, sta, reason, false))
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		ap_sta_disassociate(hapd, sta, reason);
+		return;
+	}
+
+	if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DEAUTHENTICATE))
 		return;
 
 	ap_sta_handle_deauthenticate(hapd, sta, reason);
@@ -1596,12 +1663,13 @@
 }
 
 
-void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+bool ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 			   int authorized)
 {
 	if (!ap_sta_set_authorized_flag(hapd, sta, authorized))
-		return;
+		return false;
 	ap_sta_set_authorized_event(hapd, sta, authorized);
+	return true;
 }
 
 
@@ -1625,41 +1693,19 @@
 
 	if (sta == NULL)
 		return;
-	sta->deauth_reason = reason;
-	ap_sta_set_authorized(hapd, sta, 0);
-	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-	hostapd_set_sta_flags(hapd, sta);
-	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
-	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
-	wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout "
-		   "for " MACSTR " (%d seconds - "
-		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
-		   hapd->conf->iface, __func__, MAC2STR(sta->addr),
-		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
-	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
-			       ap_handle_timer, hapd, sta);
-	sta->timeout_next = STA_REMOVE;
 
 	if (hapd->iface->current_mode &&
 	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
 		/* Deauthentication is not used in DMG/IEEE 802.11ad;
 		 * disassociate the STA instead. */
-		sta->disassoc_reason = reason;
-		sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
-		eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
-		eloop_register_timeout(hapd->iface->drv_flags &
-				       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
-				       2 : 0, 0, ap_sta_disassoc_cb_timeout,
-				       hapd, sta);
+		ap_sta_disassociate_common(hapd, sta, reason);
 		return;
 	}
 
-	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
-	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
-	eloop_register_timeout(hapd->iface->drv_flags &
-			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
-			       ap_sta_deauth_cb_timeout, hapd, sta);
+	if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISCONNECT))
+		return;
+
+	ap_sta_handle_disconnect(hapd, sta, reason);
 }
 
 
@@ -1712,13 +1758,20 @@
 }
 
 
+void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
+				struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_sta_assoc_timeout, hapd, sta);
+}
+
+
 int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
 {
 	int res;
 
 	buf[0] = '\0';
 	res = os_snprintf(buf, buflen,
-			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
 			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
 			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1741,6 +1794,7 @@
 			  (flags & WLAN_STA_EHT ? "[EHT]" : ""),
 			  (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
 			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+			  (flags & WLAN_STA_SPP_AMSDU ? "[SPP-A-MSDU]" : ""),
 			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
 			   "[WNM_SLEEP_MODE]" : ""));
 	if (os_snprintf_error(buflen, res))
@@ -1826,6 +1880,7 @@
 {
 	const u8 *mld_link_addr = NULL;
 	bool mld_link_sta = false;
+	u16 eml_cap = 0;
 
 	/*
 	 * If a station that is already associated to the AP, is trying to
@@ -1841,6 +1896,7 @@
 
 		mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
 		mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+		eml_cap = sta->mld_info.common_info.eml_capa;
 
 		/*
 		 * In case the AP is affiliated with an AP MLD, we need to
@@ -1859,7 +1915,7 @@
 			    sta->supported_rates_len,
 			    0, NULL, NULL, NULL, 0, NULL, 0, NULL,
 			    sta->flags, 0, 0, 0, 0,
-			    mld_link_addr, mld_link_sta)) {
+			    mld_link_addr, mld_link_sta, eml_cap)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_NOTICE,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 5b01c1e..1730742 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -22,6 +22,7 @@
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
 #define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_SPP_AMSDU BIT(2)
 #define WLAN_STA_AUTHORIZED BIT(5)
 #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
 #define WLAN_STA_SHORT_PREAMBLE BIT(7)
@@ -131,7 +132,6 @@
 	unsigned int ht_20mhz_set:1;
 	unsigned int no_p2p_set:1;
 	unsigned int qos_map_enabled:1;
-	unsigned int remediation:1;
 	unsigned int hs20_deauth_requested:1;
 	unsigned int hs20_deauth_on_ack:1;
 	unsigned int session_timeout_set:1;
@@ -217,8 +217,6 @@
 	struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
 	/* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
 	struct wpabuf *roaming_consortium;
-	u8 remediation_method;
-	char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
 	char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
 	struct wpabuf *hs20_deauth_req;
 	char *hs20_session_info_url;
@@ -322,6 +320,8 @@
 
 	u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
 			      * units of 1000 TUs */
+
+	u64 last_known_sta_id_timestamp;
 };
 
 
@@ -386,7 +386,7 @@
 				int authorized);
 void ap_sta_set_authorized_event(struct hostapd_data *hapd,
 				 struct sta_info *sta, int authorized);
-void ap_sta_set_authorized(struct hostapd_data *hapd,
+bool ap_sta_set_authorized(struct hostapd_data *hapd,
 			   struct sta_info *sta, int authorized);
 static inline int ap_sta_is_authorized(struct sta_info *sta)
 {
@@ -397,6 +397,8 @@
 void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
 				      struct sta_info *sta);
+void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
+				struct sta_info *sta);
 
 int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
 void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3af3404..9295dc6 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -38,6 +38,7 @@
 #define STATE_MACHINE_DATA struct wpa_state_machine
 #define STATE_MACHINE_DEBUG_PREFIX "WPA"
 #define STATE_MACHINE_ADDR wpa_auth_get_spa(sm)
+#define KDE_ALL_LINKS 0xffff
 
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -133,11 +134,8 @@
 		return;
 
 	for_each_sm_auth(sm, link_id) {
-		if (link_id == release_link_id) {
-			wpa_group_put(sm->mld_links[link_id].wpa_auth,
-				      sm->mld_links[link_id].wpa_auth->group);
+		if (link_id == release_link_id)
 			sm->mld_links[link_id].wpa_auth = NULL;
-		}
 	}
 }
 
@@ -368,6 +366,25 @@
 #endif /* CONFIG_MESH */
 
 
+static inline int wpa_auth_get_drv_flags(struct wpa_authenticator *wpa_auth,
+					 u64 *drv_flags, u64 *drv_flags2)
+{
+	if (!wpa_auth->cb->get_drv_flags)
+		return -1;
+	return wpa_auth->cb->get_drv_flags(wpa_auth->cb_ctx, drv_flags,
+					   drv_flags2);
+}
+
+
+static bool wpa_auth_4way_handshake_offload(struct wpa_authenticator *wpa_auth)
+{
+	u64 drv_flags = 0, drv_flags2 = 0;
+
+	return wpa_auth_get_drv_flags(wpa_auth, &drv_flags, &drv_flags2) == 0 &&
+		(drv_flags2 &  WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK);
+}
+
+
 int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
 			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
 			  void *cb_ctx)
@@ -722,6 +739,19 @@
 }
 
 
+static void wpa_deinit_groups(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group, *prev;
+
+	group = wpa_auth->group;
+	while (group) {
+		prev = group;
+		group = group->next;
+		bin_clear_free(prev, sizeof(*prev));
+	}
+}
+
+
 /**
  * wpa_init - Initialize WPA authenticator
  * @addr: Authenticator address
@@ -757,36 +787,48 @@
 
 	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
 		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
-		os_free(wpa_auth);
-		return NULL;
+		goto fail;
 	}
 
 	wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
-	if (!wpa_auth->group) {
-		os_free(wpa_auth->wpa_ie);
-		os_free(wpa_auth);
-		return NULL;
-	}
+	if (!wpa_auth->group)
+		goto fail;
 
+	/* Per-link PMKSA cache */
 	wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
 						wpa_auth);
 	if (!wpa_auth->pmksa) {
 		wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
-		os_free(wpa_auth->group);
-		os_free(wpa_auth->wpa_ie);
-		os_free(wpa_auth);
-		return NULL;
+		goto fail;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	/* MLD-level PMKSA cache */
+	if (wpa_auth->is_ml && wpa_auth->primary_auth) {
+		wpa_auth->ml_pmksa = pmksa_cache_auth_init(
+			wpa_auth_pmksa_free_cb, wpa_auth);
+		if (!wpa_auth->ml_pmksa) {
+			wpa_printf(MSG_ERROR,
+				   "MLD-level PMKSA cache initialization failed.");
+			goto fail;
+		}
+	} else if (wpa_auth->is_ml) {
+		struct wpa_authenticator *pa = wpa_get_primary_auth(wpa_auth);
+
+		if (!pa) {
+			wpa_printf(MSG_ERROR,
+				   "Could not find primary authenticator.");
+			goto fail;
+		}
+		wpa_auth->ml_pmksa = pa->ml_pmksa;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_IEEE80211R_AP
 	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
 	if (!wpa_auth->ft_pmk_cache) {
 		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
-		os_free(wpa_auth->group);
-		os_free(wpa_auth->wpa_ie);
-		pmksa_cache_auth_deinit(wpa_auth->pmksa);
-		os_free(wpa_auth);
-		return NULL;
+		goto fail;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -829,6 +871,17 @@
 	}
 
 	return wpa_auth;
+
+fail:
+	wpa_deinit_groups(wpa_auth);
+	os_free(wpa_auth->wpa_ie);
+	pmksa_cache_auth_deinit(wpa_auth->pmksa);
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->primary_auth)
+		pmksa_cache_auth_deinit(wpa_auth->ml_pmksa);
+#endif /* CONFIG_IEEE80211BE */
+	os_free(wpa_auth);
+	return NULL;
 }
 
 
@@ -864,16 +917,35 @@
  */
 void wpa_deinit(struct wpa_authenticator *wpa_auth)
 {
-	struct wpa_group *group, *prev;
+#ifdef CONFIG_IEEE80211BE
+	struct wpa_authenticator *next_pa;
+#endif /* CONFIG_IEEE80211BE */
 
 	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
-
-	/* TODO: Assign ML primary authenticator to next link authenticator and
-	 * start rekey timer. */
 	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
 
 	pmksa_cache_auth_deinit(wpa_auth->pmksa);
 
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->is_ml && wpa_auth->primary_auth) {
+		next_pa = wpa_auth->cb->next_primary_auth(wpa_auth->cb_ctx);
+
+		if (!next_pa) {
+			/* Deinit PMKSA entry list if last link */
+			pmksa_cache_auth_deinit(wpa_auth->ml_pmksa);
+		} else {
+			/* Assign ML primary authenticator to the next link
+			 * authenticator and start rekey timer.
+			 */
+			next_pa->primary_auth = true;
+			if (next_pa->conf.wpa_group_rekey)
+				eloop_register_timeout(
+					next_pa->conf.wpa_group_rekey,
+					0, wpa_rekey_gtk, next_pa, NULL);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
@@ -884,16 +956,8 @@
 	bitfield_free(wpa_auth->ip_pool);
 #endif /* CONFIG_P2P */
 
-
 	os_free(wpa_auth->wpa_ie);
-
-	group = wpa_auth->group;
-	while (group) {
-		prev = group;
-		group = group->next;
-		bin_clear_free(prev, sizeof(*prev));
-	}
-
+	wpa_deinit_groups(wpa_auth);
 	wpa_auth_free_conf(&wpa_auth->conf);
 	os_free(wpa_auth);
 }
@@ -1003,7 +1067,13 @@
 	if (wpa_sm_step(sm) == 1)
 		return 1; /* should not really happen */
 	sm->Init = false;
-	sm->AuthenticationRequest = true;
+
+	if (wpa_auth_4way_handshake_offload(sm->wpa_auth))
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"Skip EAPOL for 4-way handshake offload case");
+	else
+		sm->AuthenticationRequest = true;
+
 	return wpa_sm_step(sm);
 }
 
@@ -1049,11 +1119,8 @@
 	os_free(sm->rsnxe);
 	os_free(sm->rsn_selection);
 #ifdef CONFIG_IEEE80211BE
-	for_each_sm_auth(sm, link_id) {
-		wpa_group_put(sm->mld_links[link_id].wpa_auth,
-			      sm->mld_links[link_id].wpa_auth->group);
+	for_each_sm_auth(sm, link_id)
 		sm->mld_links[link_id].wpa_auth = NULL;
-	}
 #endif /* CONFIG_IEEE80211BE */
 	wpa_group_put(sm->wpa_auth, sm->group);
 #ifdef CONFIG_DPP2
@@ -2068,8 +2135,9 @@
 		os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (conf->eapol_key_reserved_random)
-		random_get_bytes(key->key_id, sizeof(key->key_id));
+	if (conf->eapol_key_reserved_random &&
+	    random_get_bytes(key->key_id, sizeof(key->key_id)) < 0)
+		os_memset(key->key_id, 0x11, sizeof(key->key_id));
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	if (kde && !encr) {
@@ -2361,7 +2429,12 @@
 			if (wpa_sm_step(sm) == 1)
 				return 1; /* should not really happen */
 			sm->Init = false;
-			sm->AuthenticationRequest = true;
+
+			if (wpa_auth_4way_handshake_offload(sm->wpa_auth))
+				wpa_printf(MSG_DEBUG,
+					   "Skip EAPOL for 4-way handshake offload case");
+			else
+				sm->AuthenticationRequest = true;
 			break;
 		}
 
@@ -2768,8 +2841,7 @@
 	if (sm->wpa == WPA_VERSION_WPA2 &&
 	    (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
 	     (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
-	     wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
-	    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
+	     wpa_key_mgmt_sae(sm->wpa_key_mgmt))) {
 		pmkid = buf;
 		kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
 		pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
@@ -3416,7 +3488,7 @@
 	/* GTK KDE */
 	gtk = gsm->GTK[gsm->GN - 1];
 	gtk_len = gsm->GTK_len;
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -3820,7 +3892,6 @@
 		key_data_buf_len = key_data_length;
 		if (aes_unwrap(PTK.kek, PTK.kek_len, key_data_length / 8,
 			       key_data, key_data_buf)) {
-			bin_clear_free(key_data_buf, key_data_buf_len);
 			wpa_printf(MSG_INFO,
 				   "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
 			goto out;
@@ -3838,9 +3909,6 @@
 	if (kde.rsn_ie) {
 		eapol_key_ie = kde.rsn_ie;
 		eapol_key_ie_len = kde.rsn_ie_len;
-	} else if (kde.osen) {
-		eapol_key_ie = kde.osen;
-		eapol_key_ie_len = kde.osen_len;
 	} else {
 		eapol_key_ie = kde.wpa_ie;
 		eapol_key_ie_len = kde.wpa_ie_len;
@@ -4094,7 +4162,7 @@
 	else
 		os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
 	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use of
 		 * IGTK in the BSS.
@@ -4125,14 +4193,6 @@
 	else
 		os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
 	os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
-		/*
-		 * Provide unique random BIGTK to each OSEN STA to prevent use
-		 * of BIGTK in the BSS.
-		 */
-		if (random_get_bytes(bigtk.bigtk, len) < 0)
-			return pos;
-	}
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
 			  (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
 			  NULL, 0);
@@ -4278,7 +4338,8 @@
 }
 
 
-static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm,
+					 u16 req_links)
 {
 	struct wpa_authenticator *wpa_auth;
 	size_t kde_len = 0;
@@ -4291,6 +4352,9 @@
 		if (!sm->mld_links[link_id].valid)
 			continue;
 
+		if (!(req_links & BIT(link_id)))
+			continue;
+
 		wpa_auth = sm->mld_links[link_id].wpa_auth;
 		if (!wpa_auth || !wpa_auth->group)
 			continue;
@@ -4326,7 +4390,8 @@
 }
 
 
-static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos,
+				   u16 req_links)
 {
 	struct wpa_auth_ml_key_info ml_key_info;
 	unsigned int i, link_id;
@@ -4335,7 +4400,6 @@
 
 	/* First fetch the key information from all the authenticators */
 	os_memset(&ml_key_info, 0, sizeof(ml_key_info));
-	ml_key_info.n_mld_links = sm->n_mld_affiliated_links + 1;
 
 	/*
 	 * Assume that management frame protection and beacon protection are the
@@ -4348,13 +4412,19 @@
 		if (!sm->mld_links[link_id].valid)
 			continue;
 
+		if (!(req_links & BIT(link_id)))
+			continue;
+
 		ml_key_info.links[i++].link_id = link_id;
 	}
+	ml_key_info.n_mld_links = i;
 
 	wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info, rekey);
 
 	/* Add MLO GTK KDEs */
-	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+	for (i = 0; i < ml_key_info.n_mld_links; i++) {
+		link_id = ml_key_info.links[i].link_id;
+
 		if (!sm->mld_links[link_id].valid ||
 		    !ml_key_info.links[i].gtk_len)
 			continue;
@@ -4379,8 +4449,6 @@
 		os_memcpy(pos, ml_key_info.links[i].gtk,
 			  ml_key_info.links[i].gtk_len);
 		pos += ml_key_info.links[i].gtk_len;
-
-		i++;
 	}
 
 	if (!sm->mgmt_frame_prot) {
@@ -4390,7 +4458,9 @@
 	}
 
 	/* Add MLO IGTK KDEs */
-	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+	for (i = 0; i < ml_key_info.n_mld_links; i++) {
+		link_id = ml_key_info.links[i].link_id;
+
 		if (!sm->mld_links[link_id].valid ||
 		    !ml_key_info.links[i].igtk_len)
 			continue;
@@ -4422,8 +4492,6 @@
 		os_memcpy(pos, ml_key_info.links[i].igtk,
 			  ml_key_info.links[i].igtk_len);
 		pos += ml_key_info.links[i].igtk_len;
-
-		i++;
 	}
 
 	if (!sm->wpa_auth->conf.beacon_prot) {
@@ -4433,7 +4501,9 @@
 	}
 
 	/* Add MLO BIGTK KDEs */
-	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+	for (i = 0; i < ml_key_info.n_mld_links; i++) {
+		link_id = ml_key_info.links[i].link_id;
+
 		if (!sm->mld_links[link_id].valid ||
 		    !ml_key_info.links[i].bigtk ||
 		    !ml_key_info.links[i].igtk_len)
@@ -4466,8 +4536,6 @@
 		os_memcpy(pos, ml_key_info.links[i].bigtk,
 			  ml_key_info.links[i].igtk_len);
 		pos += ml_key_info.links[i].igtk_len;
-
-		i++;
 	}
 
 	wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", pos - start);
@@ -4534,7 +4602,7 @@
 			kde_len += 2 + ie[1];
 	}
 
-	kde_len += wpa_auth_ml_group_kdes_len(sm);
+	kde_len += wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS);
 #endif /* CONFIG_IEEE80211BE */
 
 	return kde_len;
@@ -4662,7 +4730,7 @@
 	wpa_printf(MSG_DEBUG,
 		   "RSN: MLO Link KDEs and RSN Override Link KDEs len = %ld",
 		   pos - start);
-	pos = wpa_auth_ml_group_kdes(sm, pos);
+	pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS);
 #endif /* CONFIG_IEEE80211BE */
 
 	return pos;
@@ -4785,6 +4853,20 @@
 			return;
 		}
 
+		if (!sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
+		    wpa_auth_set_key(sm->wpa_auth, 0,
+				     wpa_cipher_to_alg(sm->pairwise),
+				     sm->addr, 0, sm->PTK.tk,
+				     wpa_cipher_key_len(sm->pairwise),
+				     KEY_FLAG_PAIRWISE_NEXT)) {
+			/* Continue anyway since the many drivers do not support
+			 * configuration of the TK for RX-only purposes for
+			 * cases where multiple keys might be in use in parallel
+			 * and this being an optional optimization to avoid race
+			 * condition during TK changes that could result in some
+			 * protected frames getting discarded. */
+		}
+
 #ifdef CONFIG_PASN
 		if (sm->wpa_auth->conf.secure_ltf &&
 		    ieee802_11_rsnx_capab(sm->rsnxe,
@@ -4804,8 +4886,7 @@
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
 		gtk_len = gsm->GTK_len;
-		if (conf->disable_gtk ||
-		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		if (conf->disable_gtk) {
 			/*
 			 * Provide unique random GTK to each STA to prevent use
 			 * of GTK in the BSS.
@@ -5360,7 +5441,7 @@
 			"sending 1/2 msg of Group Key Handshake");
 
 	gtk = gsm->GTK[gsm->GN - 1];
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -5391,14 +5472,14 @@
 		kde_len = pos - kde;
 #ifdef CONFIG_IEEE80211BE
 	} else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) {
-		kde_len = wpa_auth_ml_group_kdes_len(sm);
+		kde_len = wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS);
 		if (kde_len) {
 			kde_buf = os_malloc(kde_len);
 			if (!kde_buf)
 				return;
 
 			kde = pos = kde_buf;
-			pos = wpa_auth_ml_group_kdes(sm, pos);
+			pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS);
 			kde_len = pos - kde_buf;
 		}
 #endif /* CONFIG_IEEE80211BE */
@@ -5573,7 +5654,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "GTK",
 			group->GTK[group->GN - 1], group->GTK_len);
 
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+	if (wpa_auth_pmf_enabled(conf)) {
 		len = wpa_cipher_key_len(conf->group_mgmt_cipher);
 		os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
 		inc_byte_array(group->Counter, WPA_NONCE_LEN);
@@ -5586,7 +5667,7 @@
 	}
 
 	if (!wpa_auth->non_tx_beacon_prot &&
-	    conf->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+	     !wpa_auth_pmf_enabled(conf))
 		return ret;
 	if (!conf->beacon_prot)
 		return ret;
@@ -5741,7 +5822,7 @@
 		return 0;
 	pos += 8;
 	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -5780,7 +5861,7 @@
 	pos += 6;
 
 	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use
 		 * of IGTK in the BSS.
@@ -5819,14 +5900,6 @@
 	pos += 6;
 
 	os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
-		/*
-		 * Provide unique random BIGTK to each STA to prevent use
-		 * of BIGTK in the BSS.
-		 */
-		if (random_get_bytes(pos, len) < 0)
-			return 0;
-	}
 	pos += len;
 
 	wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
@@ -5907,7 +5980,7 @@
 			     KEY_FLAG_GROUP_TX_DEFAULT) < 0)
 		ret = -1;
 
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+	if (wpa_auth_pmf_enabled(conf)) {
 		enum wpa_alg alg;
 		size_t len;
 
@@ -6463,16 +6536,27 @@
 
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			   int akmp)
+			   int akmp, bool is_ml)
 {
+	struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa;
+	const u8 *aa = wpa_auth->addr;
+
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
 	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len);
 	if (!akmp)
 		akmp = WPA_KEY_MGMT_SAE;
-	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
-				 NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp))
+
+#ifdef CONFIG_IEEE80211BE
+	if (is_ml) {
+		pmksa = wpa_auth->ml_pmksa;
+		aa = wpa_auth->mld_addr;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, aa, addr,
+				 0, NULL, akmp))
 		return 0;
 
 	return -1;
@@ -6488,17 +6572,27 @@
 
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			int session_timeout, int akmp, const u8 *dpp_pkhash)
+			int session_timeout, int akmp, const u8 *dpp_pkhash,
+			bool is_ml)
 {
+	struct rsn_pmksa_cache *pmksa;
+	const u8 *aa;
 	struct rsn_pmksa_cache_entry *entry;
 
 	if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
 	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
-	entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
-				 NULL, 0, wpa_auth->addr, addr, session_timeout,
-				 NULL, akmp);
+	pmksa = wpa_auth->pmksa;
+	aa = wpa_auth->addr;
+#ifdef CONFIG_IEEE80211BE
+	if (is_ml) {
+		pmksa = wpa_auth->ml_pmksa;
+		aa = wpa_auth->mld_addr;
+	}
+#endif /* CONFIG_IEEE80211BE */
+	entry = pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, aa,
+				     addr, session_timeout, NULL, akmp);
 	if (!entry)
 		return -1;
 
@@ -6516,28 +6610,66 @@
 
 	if (!wpa_auth || !wpa_auth->pmksa)
 		return;
+
 	pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
 	if (pmksa) {
 		wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
 			   MACSTR " based on request", MAC2STR(sta_addr));
 		pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
 	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->ml_pmksa) {
+		pmksa = pmksa_cache_auth_get(wpa_auth->ml_pmksa,
+					     sta_addr, NULL);
+		if (pmksa) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Remove PMKSA cache entry for " MACSTR
+				   " based on request (MLD)",
+				   MAC2STR(sta_addr));
+			pmksa_cache_free_entry(wpa_auth->ml_pmksa, pmksa);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
 }
 
 
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
 			size_t len)
 {
+	int ret, index;
+	char *pos = buf, *end = buf + len;
+
 	if (!wpa_auth || !wpa_auth->pmksa)
 		return 0;
-	return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+
+	ret = os_snprintf(pos, len,
+			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	index = 0;
+	pos += pmksa_cache_auth_list(wpa_auth->pmksa, pos, end - pos, &index);
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->ml_pmksa)
+		pos += pmksa_cache_auth_list(wpa_auth->ml_pmksa,
+					     pos, end - pos, &index);
+#endif /* CONFIG_IEEE80211BE */
+
+	return pos - buf;
 }
 
 
 void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
 {
-	if (wpa_auth && wpa_auth->pmksa)
+	if (wpa_auth && wpa_auth->pmksa) {
 		pmksa_cache_auth_flush(wpa_auth->pmksa);
+#ifdef CONFIG_IEEE80211BE
+		if (wpa_auth->ml_pmksa && wpa_auth->primary_auth)
+			pmksa_cache_auth_flush(wpa_auth->ml_pmksa);
+#endif /* CONFIG_IEEE80211BE */
+	}
 }
 
 
@@ -6613,6 +6745,26 @@
 }
 
 
+int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, const u8 **pmk, size_t *pmk_len,
+			   const u8 **pmkid)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	pmksa = wpa_auth_pmksa_get(wpa_auth, sta_addr, NULL);
+	if (!pmksa) {
+		wpa_printf(MSG_DEBUG, "RSN: Failed to get PMKSA for " MACSTR,
+			   MAC2STR(sta_addr));
+		return -1;
+	}
+
+	*pmk = pmksa->pmk;
+	*pmk_len = pmksa->pmk_len;
+	*pmkid = pmksa->pmkid;
+	return 0;
+}
+
+
 void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
 			      struct wpa_state_machine *sm,
 			      struct wpa_authenticator *wpa_auth,
@@ -7467,11 +7619,8 @@
 			ctx.wpa_auth = NULL;
 			wpa_auth_for_each_auth(sm->wpa_auth,
 					       wpa_get_link_sta_auth, &ctx);
-			if (ctx.wpa_auth) {
+			if (ctx.wpa_auth)
 				sm_link->wpa_auth = ctx.wpa_auth;
-				wpa_group_get(sm_link->wpa_auth,
-					      sm_link->wpa_auth->group);
-			}
 		} else {
 			sm_link->wpa_auth = sm->wpa_auth;
 		}
@@ -7485,3 +7634,49 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 }
+
+
+bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm,
+					  const u8 *timestamp,
+					  const u8 *mic, size_t mic_len)
+{
+	size_t exp_mic_len;
+	u8 exp_mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	int ver;
+
+	if (!sm)
+		return false;
+
+	if (!sm->PTK_valid || !mic_len || sm->PTK.kck_len == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: No KCK to verify Known STA Identification");
+		return false;
+	}
+
+	exp_mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+	if (mic_len != exp_mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: MIC length mismatch in Known STA Identification (received %zu, expected %zu)",
+			   mic_len, exp_mic_len);
+		return false;
+	}
+
+	if (wpa_use_akm_defined(sm->wpa_key_mgmt))
+		ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+	else if (wpa_use_cmac(sm->wpa_key_mgmt))
+		ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+	else if (sm->pairwise != WPA_CIPHER_TKIP)
+		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, sm->wpa_key_mgmt,
+			      ver, timestamp, 8, exp_mic) ||
+	    os_memcmp_const(mic, exp_mic, exp_mic_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Invalid MIC in Known STA Identification");
+		return false;
+	}
+
+	return true;
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 0b692ad..45c8dd6 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -321,6 +321,8 @@
 	bool ssid_protection;
 
 	int rsn_override_omit_rsnxe;
+
+	bool spp_amsdu;
 };
 
 typedef enum {
@@ -428,6 +430,7 @@
 #ifdef CONFIG_IEEE80211BE
 	int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info,
 			       bool rekey);
+	struct wpa_authenticator * (*next_primary_auth)(void *ctx);
 #endif /* CONFIG_IEEE80211BE */
 	int (*get_drv_flags)(void *ctx, u64 *drv_flags, u64 *drv_flags2);
 };
@@ -456,11 +459,13 @@
 		    const u8 *rsnxe, size_t rsnxe_len,
 		    const u8 *mdie, size_t mdie_len,
 		    const u8 *owe_dh, size_t owe_dh_len,
-		    struct wpa_state_machine *assoc_sm);
+		    struct wpa_state_machine *assoc_sm,
+		    bool is_ml);
 int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
 		      struct wpa_state_machine *sm,
 		      const u8 *osen_ie, size_t osen_ie_len);
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm);
 void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
 int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
 struct wpa_state_machine *
@@ -508,11 +513,12 @@
 			       struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			   int akmp);
+			   int akmp, bool is_ml);
 void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			int session_timeout, int akmp, const u8 *dpp_pkhash);
+			int session_timeout, int akmp, const u8 *dpp_pkhash,
+			bool is_ml);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
@@ -531,6 +537,9 @@
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid);
+int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, const u8 **pmk, size_t *pmk_len,
+			   const u8 **pmkid);
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
 				 const u8 *sta_addr, const u8 *pmkid);
@@ -689,4 +698,15 @@
 		    sm->mld_links[link_id].wpa_auth &&			\
 		    sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
 
+static inline bool wpa_auth_pmf_enabled(struct wpa_auth_config *conf)
+{
+	return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION;
+}
+
+bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm,
+					  const u8 *timestamp,
+					  const u8 *mic, size_t mic_len);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index de16c31..d5400a9 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2247,8 +2247,7 @@
 		pad_len += 8;
 	if (pad_len && key_len < sizeof(keybuf)) {
 		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
-		if (conf->disable_gtk ||
-		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		if (conf->disable_gtk) {
 			/*
 			 * Provide unique random GTK to each STA to prevent use
 			 * of GTK in the BSS.
@@ -2260,7 +2259,7 @@
 		keybuf[key_len] = 0xdd;
 		key_len += pad_len;
 		key = keybuf;
-	} else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	} else if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use of GTK
 		 * in the BSS.
@@ -2339,7 +2338,7 @@
 	pos += 6;
 	*pos++ = igtk_len;
 	igtk = gsm->IGTK[gsm->GN_igtk - 4];
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use of
 		 * IGTK in the BSS.
@@ -2372,7 +2371,6 @@
 	const u8 *kek, *bigtk;
 	size_t kek_len;
 	size_t bigtk_len;
-	u8 stub_bigtk[WPA_IGTK_MAX_LEN];
 
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
 		kek = sm->PTK.kek2;
@@ -2400,17 +2398,6 @@
 	pos += 6;
 	*pos++ = bigtk_len;
 	bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
-		/*
-		 * Provide unique random BIGTK to each OSEN STA to prevent use
-		 * of BIGTK in the BSS.
-		 */
-		if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) {
-			os_free(subelem);
-			return NULL;
-		}
-		bigtk = stub_bigtk;
-	}
 	if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
 		wpa_printf(MSG_DEBUG,
 			   "FT: BIGTK subelem encryption failed: kek_len=%d",
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 2323a59..94cec78 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -35,7 +35,8 @@
 #include "wpa_auth_glue.h"
 
 
-static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+static void hostapd_wpa_auth_conf(struct hostapd_iface *iface,
+				  struct hostapd_bss_config *conf,
 				  struct hostapd_config *iconf,
 				  struct wpa_auth_config *wconf)
 {
@@ -109,17 +110,6 @@
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_HS20
 	wconf->disable_gtk = conf->disable_dgaf;
-	if (conf->osen) {
-		wconf->disable_gtk = 1;
-		wconf->wpa = WPA_PROTO_OSEN;
-		wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
-		wconf->wpa_pairwise = 0;
-		wconf->wpa_group = WPA_CIPHER_CCMP;
-		wconf->rsn_pairwise = WPA_CIPHER_CCMP;
-		wconf->rsn_preauth = 0;
-		wconf->disable_pmksa_caching = 1;
-		wconf->ieee80211w = 1;
-	}
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_TESTING_OPTIONS
 	wconf->corrupt_gtk_rekey_mic_probability =
@@ -280,6 +270,8 @@
 		conf->no_disconnect_on_group_keyerror;
 
 	wconf->rsn_override_omit_rsnxe = conf->rsn_override_omit_rsnxe;
+	wconf->spp_amsdu = conf->spp_amsdu &&
+		(iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU);
 }
 
 
@@ -512,6 +504,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	const char *ifname = hapd->conf->iface;
+	int set_tx = !(key_flag & KEY_FLAG_NEXT);
 
 	if (vlan_id > 0) {
 		ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
@@ -564,8 +557,8 @@
 		hapd->last_gtk_len = key_len;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, 1,
-				   NULL, 0, key, key_len, key_flag);
+	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id,
+				   set_tx, NULL, 0, key, key_len, key_flag);
 }
 
 
@@ -1641,6 +1634,21 @@
 	return 0;
 }
 
+
+static struct wpa_authenticator * hostapd_next_primary_auth(void *cb_ctx)
+{
+	struct hostapd_data *hapd = cb_ctx, *bss;
+
+	for_each_mld_link(bss, hapd) {
+		if (bss == hapd)
+			continue;
+		if (bss->wpa_auth)
+			return bss->wpa_auth;
+	}
+
+	return NULL;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
@@ -1710,6 +1718,7 @@
 #endif /* CONFIG_PASN */
 #ifdef CONFIG_IEEE80211BE
 		.get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
+		.next_primary_auth = hostapd_next_primary_auth,
 #endif /* CONFIG_IEEE80211BE */
 		.get_drv_flags = hostapd_wpa_auth_get_drv_flags,
 	};
@@ -1717,7 +1726,7 @@
 	size_t wpa_ie_len;
 	struct hostapd_data *tx_bss;
 
-	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+	hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf, &_conf);
 	_conf.msg_ctx = hapd->msg_ctx;
 	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
 	if (tx_bss != hapd)
@@ -1843,7 +1852,9 @@
 void hostapd_reconfig_wpa(struct hostapd_data *hapd)
 {
 	struct wpa_auth_config wpa_auth_conf;
-	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
+
+	hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf,
+			      &wpa_auth_conf);
 	wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
 }
 
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index cb902e4..0aa25b9 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -97,6 +97,7 @@
 #endif /* CONFIG_IEEE80211R_AP */
 	unsigned int is_wnmsleep:1;
 	unsigned int pmkid_set:1;
+	unsigned int spp_amsdu:1;
 
 	unsigned int ptkstart_without_success;
 
@@ -266,6 +267,8 @@
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_IEEE80211BE
+	/* MLD-level PMKSA cache for non-AP MLD entries only. */
+	struct rsn_pmksa_cache *ml_pmksa;
 	bool is_ml;
 	u8 mld_addr[ETH_ALEN];
 	u8 link_id;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 43d9c1d..d56eeaa 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -296,13 +296,6 @@
 		num_suites++;
 	}
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-	if (key_mgmt & WPA_KEY_MGMT_OSEN) {
-		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_PASN
 	if (key_mgmt & WPA_KEY_MGMT_PASN) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
@@ -507,6 +500,8 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 	if (conf->ssid_protection)
 		capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+	if (conf->spp_amsdu)
+		capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
 
 	return capab;
 }
@@ -579,57 +574,6 @@
 }
 
 
-static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
-{
-	u8 *len;
-	u16 capab;
-
-	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
-	len = eid++; /* to be filled */
-	WPA_PUT_BE24(eid, OUI_WFA);
-	eid += 3;
-	*eid++ = HS20_OSEN_OUI_TYPE;
-
-	/* Group Data Cipher Suite */
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
-	eid += RSN_SELECTOR_LEN;
-
-	/* Pairwise Cipher Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
-	eid += RSN_SELECTOR_LEN;
-
-	/* AKM Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
-	eid += RSN_SELECTOR_LEN;
-
-	/* RSN Capabilities */
-	capab = 0;
-	if (conf->wmm_enabled) {
-		/* 4 PTKSA replay counters when using WMM */
-		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
-	}
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-		capab |= WPA_CAPABILITY_MFPC;
-		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
-			capab |= WPA_CAPABILITY_MFPR;
-	}
-#ifdef CONFIG_OCV
-	if (conf->ocv)
-		capab |= WPA_CAPABILITY_OCVC;
-#endif /* CONFIG_OCV */
-	WPA_PUT_LE16(eid, capab);
-	eid += 2;
-
-	*len = eid - len - 1;
-
-	return eid;
-}
-
-
 int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 {
 	u8 *pos, buf[1500];
@@ -654,9 +598,6 @@
 
 	pos = buf;
 
-	if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
-		pos = wpa_write_osen(&wpa_auth->conf, pos);
-	}
 	if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
 #ifdef CONFIG_TESTING_OPTIONS
 		if (wpa_auth->conf.rsne_override_set) {
@@ -850,6 +791,32 @@
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+
+struct wpa_auth_link_iter_data {
+	struct wpa_authenticator *wpa_auth;
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+static int wpa_auth_pmksa_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_link_iter_data *data = ctx;
+
+	if (a == data->wpa_auth ||
+	    !ether_addr_equal(a->mld_addr, data->wpa_auth->mld_addr))
+		return 0;
+
+	data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+	if (data->pmksa)
+		return 1;
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
 enum wpa_validate_result
 wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 		    struct wpa_state_machine *sm, int freq,
@@ -857,7 +824,7 @@
 		    const u8 *rsnxe, size_t rsnxe_len,
 		    const u8 *mdie, size_t mdie_len,
 		    const u8 *owe_dh, size_t owe_dh_len,
-		    struct wpa_state_machine *assoc_sm)
+		    struct wpa_state_machine *assoc_sm, bool is_ml)
 {
 	struct wpa_auth_config *conf = &wpa_auth->conf;
 	struct wpa_ie_data data;
@@ -958,10 +925,6 @@
 		else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
 			selector = RSN_AUTH_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-		else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
-			selector = RSN_AUTH_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_SHA384
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384)
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA384;
@@ -1082,10 +1045,6 @@
 	else if (key_mgmt & WPA_KEY_MGMT_DPP)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-	else if (key_mgmt & WPA_KEY_MGMT_OSEN)
-		sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
 	else
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 
@@ -1156,7 +1115,7 @@
 	}
 #endif /* CONFIG_OCV */
 
-	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+	if (!wpa_auth_pmf_enabled(conf) ||
 	    !(data.capabilities & WPA_CAPABILITY_MFPC))
 		sm->mgmt_frame_prot = 0;
 	else
@@ -1169,6 +1128,14 @@
 		    return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
 	}
 
+	if (wpa_auth->conf.spp_amsdu &&
+	    ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SPP_A_MSDU) &&
+	    (ciphers & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+			WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP)))
+		sm->spp_amsdu = 1;
+	else
+		sm->spp_amsdu = 0;
+
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
@@ -1243,10 +1210,39 @@
 
 	sm->pmksa = NULL;
 	for (i = 0; i < data.num_pmkid; i++) {
+		struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa;
+
 		wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
 			    &data.pmkid[i * PMKID_LEN], PMKID_LEN);
-		sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+#ifdef CONFIG_IEEE80211BE
+		if (is_ml)
+			pmksa = wpa_auth->ml_pmksa;
+#endif /* CONFIG_IEEE80211BE */
+		sm->pmksa = pmksa_cache_auth_get(pmksa, sm->addr,
 						 &data.pmkid[i * PMKID_LEN]);
+#ifdef CONFIG_IEEE80211BE
+		if (!sm->pmksa && !is_ml && wpa_auth->is_ml)
+			sm->pmksa = pmksa_cache_auth_get(
+				wpa_auth->ml_pmksa, sm->addr,
+				&data.pmkid[i * PMKID_LEN]);
+		if (!sm->pmksa && is_ml) {
+			struct wpa_auth_link_iter_data idata;
+
+			idata.wpa_auth = wpa_auth;
+			idata.pmksa = NULL;
+			idata.spa = sm->addr;
+			idata.pmkid = &data.pmkid[i * PMKID_LEN];
+			wpa_auth_for_each_auth(wpa_auth,
+					       wpa_auth_pmksa_iter,
+					       &idata);
+			if (idata.pmksa)
+				sm->pmksa = idata.pmksa;
+		}
+#endif /* CONFIG_IEEE80211BE */
+		if (!sm->pmksa && !is_zero_ether_addr(sm->p2p_dev_addr))
+			sm->pmksa = pmksa_cache_auth_get(
+				wpa_auth->pmksa, sm->p2p_dev_addr,
+				&data.pmkid[i * PMKID_LEN]);
 		if (sm->pmksa) {
 			pmkid = sm->pmksa->pmkid;
 			break;
@@ -1297,7 +1293,21 @@
 				!!(drv_flags2 &
 				   WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP);
 
-		if (!ap_sae_offload && data.num_pmkid && !sm->pmksa) {
+		/* Authenticator needs to have a PMKSA corresponding to a
+		 * PMKID (if present) included by the STA in (Re)Association
+		 * Request frame if PMKSA caching is attempted to be used. In
+		 * case of SAE, this follows Open System authentication. IEEE
+		 * Std 802.11 mandates the AP to reject (re)association trying
+		 * to use PMKSA caching for SAE authentication. While the
+		 * PMKID (if any) in the RSNE in (Re)Association Request frame
+		 * following SAE authentication (i.e., in the case of no PMKSA
+		 * caching) is not really supposed to include an unknown PMKID,
+		 * the standard does not require the AP to reject association.
+		 * The PMKSA that was just derived using SAE authentication
+		 * can be used regardless of which PMKID(s) are indicated in the
+		 * (Re)Association Request frame. */
+		if (!ap_sae_offload && data.num_pmkid && !sm->pmksa &&
+		    sm->auth_alg == WLAN_AUTH_OPEN) {
 			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 					 "No PMKSA cache entry found for SAE");
 			return WPA_INVALID_PMKID;
@@ -1358,42 +1368,17 @@
 }
 
 
-#ifdef CONFIG_HS20
-int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
-		      struct wpa_state_machine *sm,
-		      const u8 *osen_ie, size_t osen_ie_len)
-{
-	if (wpa_auth == NULL || sm == NULL)
-		return -1;
-
-	/* TODO: parse OSEN element */
-	sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
-	sm->mgmt_frame_prot = 1;
-	sm->pairwise = WPA_CIPHER_CCMP;
-	sm->wpa = WPA_VERSION_WPA2;
-
-	if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
-		os_free(sm->wpa_ie);
-		sm->wpa_ie = os_malloc(osen_ie_len);
-		if (sm->wpa_ie == NULL)
-			return -1;
-	}
-
-	os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
-	sm->wpa_ie_len = osen_ie_len;
-
-	return 0;
-}
-
-#endif /* CONFIG_HS20 */
-
-
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
 {
 	return sm ? sm->mgmt_frame_prot : 0;
 }
 
 
+int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm)
+{
+	return sm ? sm->spp_amsdu : 0;
+}
+
 #ifdef CONFIG_OCV
 
 void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
index 625f405..20a5aaa 100644
--- a/src/ap/wpa_auth_kay.c
+++ b/src/ap/wpa_auth_kay.c
@@ -331,6 +331,7 @@
 				  hapd->conf->macsec_port,
 				  hapd->conf->mka_priority,
 				  hapd->conf->macsec_csindex,
+				  hapd->conf->macsec_icv_indicator,
 				  hapd->conf->iface,
 				  hapd->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
@@ -477,7 +478,7 @@
 	cak->len = hapd->conf->mka_cak_len;
 	os_memcpy(cak->key, hapd->conf->mka_cak, cak->len);
 
-	ckn->len = hapd->conf->mka_ckn_len;;
+	ckn->len = hapd->conf->mka_ckn_len;
 	os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len);
 
 	res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, true);
diff --git a/src/common/defs.h b/src/common/defs.h
index 754c4e4..5147f32 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -39,7 +39,6 @@
 #define WPA_KEY_MGMT_WAPI_PSK BIT(12)
 #define WPA_KEY_MGMT_WAPI_CERT BIT(13)
 #define WPA_KEY_MGMT_CCKM BIT(14)
-#define WPA_KEY_MGMT_OSEN BIT(15)
 #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
 #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
 #define WPA_KEY_MGMT_FILS_SHA256 BIT(18)
@@ -69,7 +68,6 @@
 			 WPA_KEY_MGMT_FT_IEEE8021X |
 			 WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
 			 WPA_KEY_MGMT_CCKM |
-			 WPA_KEY_MGMT_OSEN |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
 			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
 			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
@@ -129,6 +127,15 @@
 			 WPA_KEY_MGMT_FT_SAE_EXT_KEY));
 }
 
+static inline int wpa_key_mgmt_only_sae(int akm)
+{
+	return wpa_key_mgmt_sae(akm) &&
+		!(akm & ~(WPA_KEY_MGMT_SAE |
+			  WPA_KEY_MGMT_SAE_EXT_KEY |
+			  WPA_KEY_MGMT_FT_SAE |
+			  WPA_KEY_MGMT_FT_SAE_EXT_KEY));
+}
+
 static inline int wpa_key_mgmt_fils(int akm)
 {
 	return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 |
@@ -144,7 +151,6 @@
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
 			 WPA_KEY_MGMT_SAE |
 			 WPA_KEY_MGMT_FT_SAE |
-			 WPA_KEY_MGMT_OSEN |
 			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
 			 WPA_KEY_MGMT_FILS_SHA256 |
 			 WPA_KEY_MGMT_FT_FILS_SHA256));
@@ -196,7 +202,6 @@
 #define WPA_PROTO_WPA BIT(0)
 #define WPA_PROTO_RSN BIT(1)
 #define WPA_PROTO_WAPI BIT(2)
-#define WPA_PROTO_OSEN BIT(3)
 
 #define WPA_AUTH_ALG_OPEN BIT(0)
 #define WPA_AUTH_ALG_SHARED BIT(1)
@@ -481,6 +486,7 @@
 	KEY_FLAG_GROUP			= BIT(4),
 	KEY_FLAG_PAIRWISE		= BIT(5),
 	KEY_FLAG_PMK			= BIT(6),
+	KEY_FLAG_NEXT			= BIT(7),
 	/* Used flag combinations */
 	KEY_FLAG_RX_TX			= KEY_FLAG_RX | KEY_FLAG_TX,
 	KEY_FLAG_GROUP_RX_TX		= KEY_FLAG_GROUP | KEY_FLAG_RX_TX,
@@ -493,8 +499,10 @@
 	KEY_FLAG_PAIRWISE_RX		= KEY_FLAG_PAIRWISE | KEY_FLAG_RX,
 	KEY_FLAG_PAIRWISE_RX_TX_MODIFY	= KEY_FLAG_PAIRWISE_RX_TX |
 					  KEY_FLAG_MODIFY,
+	KEY_FLAG_PAIRWISE_NEXT		= KEY_FLAG_PAIRWISE_RX | KEY_FLAG_NEXT,
 	/* Max allowed flags for each key type */
-	KEY_FLAG_PAIRWISE_MASK		= KEY_FLAG_PAIRWISE_RX_TX_MODIFY,
+	KEY_FLAG_PAIRWISE_MASK		= KEY_FLAG_PAIRWISE_RX_TX_MODIFY |
+					  KEY_FLAG_NEXT,
 	KEY_FLAG_GROUP_MASK		= KEY_FLAG_GROUP_RX_TX_DEFAULT,
 	KEY_FLAG_PMK_MASK		= KEY_FLAG_PMK,
 };
@@ -537,6 +545,12 @@
 	SAE_PWE_NOT_SET = 4,
 };
 
+enum wpa_p2p_mode {
+	WPA_P2P_MODE_WFD_R1	= 0,
+	WPA_P2P_MODE_WFD_R2	= 1,
+	WPA_P2P_MODE_WFD_PCC	= 2,
+};
+
 #define USEC_80211_TU 1024
 
 #define USEC_TO_TU(m) ((m) / USEC_80211_TU)
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 46f2551..22998ab 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -299,7 +299,8 @@
 }
 
 
-int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version)
+static int dpp_parse_uri_version(struct dpp_bootstrap_info *bi,
+				 const char *version)
 {
 #ifdef CONFIG_DPP2
 	if (!version || DPP_VERSION < 2)
diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c
index fb3f776..25c0bd5 100644
--- a/src/common/dpp_backup.c
+++ b/src/common/dpp_backup.c
@@ -161,7 +161,7 @@
 	/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
 	attr = dpp_build_attribute(auth->conf);
 	attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
-	if (!priv_key || !attr || !alg)
+	if (!attr || !alg)
 		goto fail;
 
 	/*
@@ -515,6 +515,7 @@
 	wpabuf_free(enc_cont_info);
 	return env;
 fail:
+	wpa_printf(MSG_INFO, "DPP: Failed to build DPPEnvelopedData");
 	wpabuf_free(env);
 	env = NULL;
 	goto out;
diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c
index 452c502..e55789c 100644
--- a/src/common/dpp_reconfig.c
+++ b/src/common/dpp_reconfig.c
@@ -569,7 +569,7 @@
 }
 
 
-struct wpabuf *
+static struct wpabuf *
 dpp_reconfig_build_conf(struct dpp_authentication *auth)
 {
 	struct wpabuf *msg = NULL, *clear;
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index bffb440..d9276b9 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -432,6 +432,27 @@
 }
 
 
+static void punct_update_legacy_bw_320(u16 bitmap, u8 pri,
+				       enum oper_chan_width *width, u8 *seg0)
+{
+	if (pri < *seg0) {
+		*seg0 -= 16;
+		if (bitmap & 0x00FF) {
+			*width = 1;
+			punct_update_legacy_bw_160(bitmap & 0xFF, pri, width,
+						   seg0);
+		}
+	} else {
+		*seg0 += 16;
+		if (bitmap & 0xFF00) {
+			*width = 1;
+			punct_update_legacy_bw_160((bitmap & 0xFF00) >> 8,
+						   pri, width, seg0);
+		}
+	}
+}
+
+
 void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
 			    u8 *seg0, u8 *seg1)
 {
@@ -446,7 +467,10 @@
 		punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
 	}
 
-	/* TODO: 320 MHz */
+	if (*width == CONF_OPER_CHWIDTH_320MHZ && (bitmap & 0xFFFF)) {
+		*width = CONF_OPER_CHWIDTH_160MHZ;
+		punct_update_legacy_bw_320(bitmap & 0xFFFF, pri, width, seg0);
+	}
 }
 
 
@@ -481,6 +505,7 @@
 	data->sec_channel_offset = sec_channel_offset;
 	data->center_freq1 = freq + sec_channel_offset * 10;
 	data->center_freq2 = 0;
+	data->punct_bitmap = punct_bitmap;
 	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
 		data->bandwidth = 80;
 	else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
@@ -569,7 +594,8 @@
 
 	if (data->eht_enabled) switch (oper_chwidth) {
 	case CONF_OPER_CHWIDTH_320MHZ:
-		if (!(eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+		if (eht_cap &&
+		    !(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");
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index c9b2d37..c0d5265 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -111,11 +111,6 @@
 			elems->hs20 = pos;
 			elems->hs20_len = elen;
 			break;
-		case HS20_OSEN_OUI_TYPE:
-			/* Hotspot 2.0 OSEN */
-			elems->osen = pos;
-			elems->osen_len = elen;
-			break;
 		case MBO_OUI_TYPE:
 			/* MBO-OCE */
 			elems->mbo = pos;
@@ -140,6 +135,10 @@
 			elems->sae_pk = pos + 4;
 			elems->sae_pk_len = elen - 4;
 			break;
+		case WFA_CAPA_OUI_TYPE:
+			elems->wfa_capab = pos + 4;
+			elems->wfa_capab_len = elen - 4;
+			break;
 		case WFA_RSNE_OVERRIDE_OUI_TYPE:
 			elems->rsne_override = pos;
 			elems->rsne_override_len = elen;
@@ -148,6 +147,10 @@
 			elems->rsne_override_2 = pos;
 			elems->rsne_override_2_len = elen;
 			break;
+		case WFA_RSNXE_OVERRIDE_OUI_TYPE:
+			elems->rsnxe_override = pos;
+			elems->rsnxe_override_len = elen;
+			break;
 		case WFA_RSN_SELECTION_OUI_TYPE:
 			if (elen < 4 + 1) {
 				wpa_printf(MSG_DEBUG,
@@ -993,14 +996,14 @@
 }
 
 
-ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
-					 struct ieee802_11_elems *elems,
+ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
 					 struct wpabuf *mlbuf,
 					 u8 link_id, bool show_errors)
 {
 	const struct ieee80211_eht_ml *ml;
 	const u8 *pos;
 	ParseRes res = ParseFailed;
+	size_t len;
 
 	pos = wpabuf_head(mlbuf);
 	len = wpabuf_len(mlbuf);
@@ -2578,6 +2581,9 @@
 {
 	const struct element *elem;
 
+	if (!ies)
+		return NULL;
+
 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
 		if (elem->datalen >= 4 &&
 		    vendor_type == WPA_GET_BE32(elem->data))
@@ -3152,7 +3158,7 @@
 	if (flen > 4)
 		flen = 4;
 	for (i = 0; i < flen; i++)
-		capabs |= rsnxe[i] << (8 * i);
+		capabs |= (u32) rsnxe[i] << (8 * i);
 
 	return !!(capabs & BIT(capab));
 }
@@ -3403,7 +3409,7 @@
 struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem)
 {
 	struct wpabuf *buf;
-	const u8 *pos, *end = data + len;
+	const u8 *pos, *end;
 	size_t min_defrag_len = ext_elem ? 255 : 256;
 
 	if (!data || !len)
@@ -3417,6 +3423,7 @@
 		return NULL;
 
 	pos = &data[min_defrag_len - 1];
+	end = data + len;
 	len -= min_defrag_len - 1;
 	while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) {
 		int ret;
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 62090ce..127375d 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -75,7 +75,6 @@
 	const u8 *ext_capab;
 	const u8 *bss_max_idle_period;
 	const u8 *ssid_list;
-	const u8 *osen;
 	const u8 *mbo;
 	const u8 *ampe;
 	const u8 *mic;
@@ -120,7 +119,9 @@
 	const u8 *mbssid;
 	const u8 *rsne_override;
 	const u8 *rsne_override_2;
+	const u8 *rsnxe_override;
 	const u8 *rsn_selection;
+	const u8 *wfa_capab;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -149,7 +150,6 @@
 	u8 hs20_len;
 	u8 ext_capab_len;
 	u8 ssid_list_len;
-	u8 osen_len;
 	u8 mbo_len;
 	u8 ampe_len;
 	u8 mic_len;
@@ -188,7 +188,9 @@
 	u8 mbssid_len;
 	size_t rsne_override_len;
 	size_t rsne_override_2_len;
+	size_t rsnxe_override_len;
 	size_t rsn_selection_len;
+	u8 wfa_capab_len;
 
 	struct mb_ies_info mb_ies;
 
@@ -210,8 +212,7 @@
 				const u8 *ids, size_t num);
 void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
 				    const u8 *ids, size_t num);
-ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
-					 struct ieee802_11_elems *elems,
+ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
 					 struct wpabuf *mlbuf,
 					 u8 link_id, bool show_errors);
 int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 7ce7591..ca4ff88 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -526,6 +526,7 @@
 #define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
 #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
 #define WLAN_EID_EXT_BANDWIDTH_INDICATION 135
+#define WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION 136
 #define WLAN_EID_EXT_PASN_ENCRYPTED_DATA 140
 
 /* Extended Capabilities field */
@@ -609,6 +610,7 @@
 #define WLAN_EXT_CAPAB_BEACON_PROTECTION 84
 #define WLAN_EXT_CAPAB_MSCS 85
 #define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88
+#define WLAN_EXT_CAPAB_KNOWN_STA_IDENTIFICATION 102
 
 /* Extended RSN Capabilities */
 /* bits 0-3: Field length (n-1) */
@@ -618,7 +620,9 @@
 #define WLAN_RSNX_CAPAB_SECURE_LTF 8
 #define WLAN_RSNX_CAPAB_SECURE_RTT 9
 #define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
+#define WLAN_RSNX_CAPAB_SPP_A_MSDU 14
 #define WLAN_RSNX_CAPAB_URNM_MFPR 15
+#define WLAN_RSNX_CAPAB_KEK_IN_PASN 18
 #define WLAN_RSNX_CAPAB_SSID_PROTECTION 21
 
 /* Multiple BSSID element subelements */
@@ -777,6 +781,21 @@
 #define WLAN_PROT_FTM 2
 #define WLAN_PROT_FTM_REPORT 3
 
+/* Protected EHT Action field values */
+#define WLAN_PROT_EHT_T2L_MAPPING_REQUEST 0
+#define WLAN_PROT_EHT_T2L_MAPPING_RESPONSE 1
+#define WLAN_PROT_EHT_T2L_MAPPING_TEARDOWN 2
+#define WLAN_PROT_EHT_EPCS_ENABLE_REQUEST 3
+#define WLAN_PROT_EHT_EPCS_ENABLE_RESPONSE 4
+#define WLAN_PROT_EHT_EPCS_ENABLE_TEARDOWN 5
+#define WLAN_PROT_EHT_EML_OPMODE_NOTIF 6
+#define WLAN_PROT_EHT_LINK_RECOMMENDATION 7
+#define WLAN_PROT_EHT_MLO_UPDATE_REQUEST 8
+#define WLAN_PROT_EHT_MLO_UPDATE_RESPONSE 9
+#define WLAN_PROT_EHT_LINK_RECONFIG_NOTIFY 10
+#define WLAN_PROT_EHT_LINK_RECONFIG_REQUEST 11
+#define WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE 12
+
 /* Radio Measurement capabilities (from RM Enabled Capabilities element)
  * IEEE Std 802.11-2020, 9.4.2.44, Table 9-179 */
 /* byte 1 (out of 5) */
@@ -1438,7 +1457,6 @@
 #define WFD_IE_VENDOR_TYPE 0x506f9a0a
 #define WFD_OUI_TYPE 10
 #define HS20_IE_VENDOR_TYPE 0x506f9a10
-#define OSEN_IE_VENDOR_TYPE 0x506f9a12
 #define NAN_IE_VENDOR_TYPE 0x506f9a13
 #define NAN_SDF_VENDOR_TYPE 0x506f9a13
 #define NAN_OUI_TYPE 0x13
@@ -1597,7 +1615,6 @@
 
 #define HS20_INDICATION_OUI_TYPE 16
 #define HS20_ANQP_OUI_TYPE 17
-#define HS20_OSEN_OUI_TYPE 18
 #define HS20_ROAMING_CONS_SEL_OUI_TYPE 29
 #define HS20_STYPE_QUERY_LIST 1
 #define HS20_STYPE_CAPABILITY_LIST 2
@@ -1606,11 +1623,6 @@
 #define HS20_STYPE_CONNECTION_CAPABILITY 5
 #define HS20_STYPE_NAI_HOME_REALM_QUERY 6
 #define HS20_STYPE_OPERATING_CLASS 7
-#define HS20_STYPE_OSU_PROVIDERS_LIST 8
-#define HS20_STYPE_ICON_REQUEST 10
-#define HS20_STYPE_ICON_BINARY_FILE 11
-#define HS20_STYPE_OPERATOR_ICON_METADATA 12
-#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13
 
 #define HS20_DGAF_DISABLED 0x01
 #define HS20_PPS_MO_ID_PRESENT 0x02
@@ -1620,7 +1632,6 @@
 #endif /* HS20_VERSION */
 
 /* WNM-Notification WFA vendors specific subtypes */
-#define HS20_WNM_SUB_REM_NEEDED 0
 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
 #define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2
 #define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3
@@ -2800,15 +2811,16 @@
 #define MULTI_LINK_SUB_ELEM_ID_VENDOR			221
 #define MULTI_LINK_SUB_ELEM_ID_FRAGMENT			254
 
-/* IEEE P802.11be/D2.2, 9.4.2.312.2 - Basic Multi-Link element */
+/* IEEE P802.11be/D7.0, 9.4.2.322.2 - Basic Multi-Link element */
 
-/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
+/* Figure 9-1074o: Presence Bitmap subfield of the Basic Multi-Link element */
 #define BASIC_MULTI_LINK_CTRL_PRES_LINK_ID		0x0010
 #define BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT	0x0020
 #define BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO		0x0040
 #define BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA		0x0080
 #define BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA		0x0100
 #define BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID		0x0200
+#define BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP		0x0400
 
 /*
  * STA Control field definitions of Per-STA Profile subelement in Basic
@@ -2865,6 +2877,7 @@
 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL    0x0020
 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ONE    0x0040
 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK  0x0060
+#define EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK          0x0080
 #define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK         0x0f80
 #define EHT_ML_MLD_CAPA_AAR_SUPP                      0x1000
 
@@ -2918,6 +2931,9 @@
 /* IEEE P802.11be/D4.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
 
 #define RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR   0x0001
+#define RECONF_MULTI_LINK_CTRL_PRES_EML_CAPA       0x0002
+#define RECONF_MULTI_LINK_CTRL_PRES_MLD_CAPA       0x0004
+#define RECONF_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP    0x0008
 
 #define EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK        0x000f
 #define EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE   0x0010
@@ -2926,6 +2942,25 @@
 #define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK 0x0780
 #define EHT_PER_STA_RECONF_CTRL_OP_PARAMS          0x0800
 #define EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE   0x1000
+#define EHT_PER_STA_RECONF_CTRL_NSTR_INDICATION    0x2000
+
+/* IEEE P802.11be/D7.0, Figure 9-1074ad - Common Info field format of the
+ * Reconfiguration Multi-Link element */
+struct eht_ml_reconf_common_info {
+	u8 len;
+
+	/*
+	 * Followed by optional fields based on the multi link reconf presence
+	 * bitmap
+	 *
+	 * MLD MAC Address: 6 octets
+	 * EML Capabilities: 2 octets
+	 * MLD Capabilities and Operations: 2 octets
+	 * Extended MLD Capabilities and Operations: 2 octets
+	 */
+	u8 variable[];
+} STRUCT_PACKED;
+
 
 /* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */
 
@@ -3156,6 +3191,15 @@
 #endif /* (CONFIG_DRIVER_NL80211_BRCM && !WIFI_BRCM_OPEN_SOURCE_MULTI_AKM) ||
 	* CONFIG_DRIVER_NL80211_SYNA */
 
+/* Wi-Fi Alliance Capabilities attributes */
+enum wfa_capa_attr_id {
+	WFA_CAPA_ATTR_GENERATIONAL_CAPAB = 1,
+	WFA_CAPA_ATTR_VENDOR_SPECIFIC = 221,
+};
+
+/* Wi-Fi Alliance Capabilities frame */
+#define WFA_CAPAB_VENDOR_TYPE 0x506f9a1b
+
 struct ieee80211_neighbor_ap_info {
 	u8 tbtt_info_hdr;
 	u8 tbtt_info_len;
diff --git a/src/common/nan_de.c b/src/common/nan_de.c
index acde4f3..4f63adc 100644
--- a/src/common/nan_de.c
+++ b/src/common/nan_de.c
@@ -18,14 +18,18 @@
 
 static const u8 nan_network_id[ETH_ALEN] =
 { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
-static const u8 wildcard_bssid[ETH_ALEN] =
-{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 enum nan_de_service_type {
 	NAN_DE_PUBLISH,
 	NAN_DE_SUBSCRIBE,
 };
 
+static const u8 p2p_network_id[ETH_ALEN] =
+{ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 };
+
+static const u8 wildcard_bssid[ETH_ALEN] =
+{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
 struct nan_de_service {
 	int id;
 	enum nan_de_service_type type;
@@ -40,11 +44,12 @@
 	struct os_reltime end_time;
 	struct os_reltime last_multicast;
 	struct os_reltime first_discovered;
-	struct os_reltime last_followup;
 	bool needs_fsd;
 	unsigned int freq;
 	unsigned int default_freq;
 	int *freq_list;
+	u8 a3[ETH_ALEN];
+	bool a3_set;
 
 	/* pauseState information for Publish function */
 	struct os_reltime pause_state_end;
@@ -65,6 +70,7 @@
 	u8 nmi[ETH_ALEN];
 	bool offload;
 	bool ap;
+	unsigned int max_listen;
 	struct nan_callbacks cb;
 
 	struct nan_de_service *service[NAN_DE_MAX_SERVICE];
@@ -79,7 +85,20 @@
 };
 
 
+bool nan_de_is_nan_network_id(const u8 *addr)
+{
+	return ether_addr_equal(addr, nan_network_id);
+}
+
+
+bool nan_de_is_p2p_network_id(const u8 *addr)
+{
+	return ether_addr_equal(addr, p2p_network_id);
+}
+
+
 struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
+			    unsigned int max_listen,
 			    const struct nan_callbacks *cb)
 {
 	struct nan_de *de;
@@ -91,6 +110,7 @@
 	os_memcpy(de->nmi, nmi, ETH_ALEN);
 	de->offload = offload;
 	de->ap = ap;
+	de->max_listen = max_listen ? max_listen : 1000;
 	os_memcpy(&de->cb, cb, sizeof(*cb));
 
 	return de;
@@ -152,6 +172,9 @@
 	wpa_printf(MSG_DEBUG, "NAN: Start pauseState");
 	os_get_reltime(&srv->pause_state_end);
 	srv->pause_state_end.sec += 60;
+	if (os_reltime_initialized(&srv->end_time) &&
+	    os_reltime_before(&srv->end_time, &srv->pause_state_end))
+		srv->pause_state_end = srv->end_time;
 	os_memcpy(srv->sel_peer_addr, peer_addr, ETH_ALEN);
 	srv->sel_peer_id = peer_id;
 }
@@ -206,7 +229,7 @@
 static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
 			  unsigned int wait_time,
 			  enum nan_service_control_type type,
-			  const u8 *dst, u8 req_instance_id,
+			  const u8 *dst, const u8 *a3, u8 req_instance_id,
 			  const struct wpabuf *ssi)
 {
 	struct wpabuf *buf;
@@ -268,10 +291,7 @@
 		wpabuf_put_buf(buf, srv->elems);
 	}
 
-	/* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
-	 * but there is no synchronization in USD as as such, no NAN Cluster
-	 * either. Use Wildcard BSSID instead. */
-	nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, wildcard_bssid, buf);
+	nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, a3, buf);
 	wpabuf_free(buf);
 }
 
@@ -335,6 +355,8 @@
 {
 	enum nan_service_control_type type;
 	unsigned int wait_time = 100;
+	const u8 *network_id;
+	const u8 *bssid;
 
 	if (srv->type == NAN_DE_PUBLISH) {
 		int ms;
@@ -352,7 +374,15 @@
 		return;
 	}
 
-	nan_de_tx_sdf(de, srv, wait_time, type, nan_network_id,
+	if (srv->is_p2p) {
+		network_id = p2p_network_id;
+		bssid = wildcard_bssid;
+	} else {
+		network_id = nan_network_id;
+		bssid = nan_network_id;
+	}
+
+	nan_de_tx_sdf(de, srv, wait_time, type, network_id, bssid,
 		      req_instance_id, srv->ssi);
 	os_get_reltime(&srv->last_multicast);
 }
@@ -398,11 +428,6 @@
 			return false;
 		if (!srv->publish.fsd)
 			return true;
-		if (os_reltime_initialized(&srv->last_followup) &&
-		    !os_reltime_expired(now, &srv->last_followup, 1))
-			return false;
-		if (os_reltime_expired(now, &srv->last_multicast, 1))
-			return true;
 	}
 
 	if (srv->type == NAN_DE_SUBSCRIBE) {
@@ -412,11 +437,6 @@
 			return false;
 		if (!srv->needs_fsd)
 			return true;
-		if (os_reltime_initialized(&srv->last_followup) &&
-		    !os_reltime_expired(now, &srv->last_followup, 1))
-			return false;
-		if (os_reltime_expired(now, &srv->first_discovered, 1))
-			return true;
 	}
 
 	return false;
@@ -481,6 +501,16 @@
 			next = tmp;
 	}
 
+	if (srv->type == NAN_DE_PUBLISH &&
+	    srv->publish.fsd &&
+	    os_reltime_initialized(&srv->pause_state_end)) {
+		os_reltime_sub(&srv->pause_state_end, now, &diff);
+		tmp = os_reltime_in_ms(&diff);
+		if (next == -1 || tmp < next)
+			next = tmp;
+		return next;
+	}
+
 	tmp = nan_de_next_multicast(de, srv, now);
 	if (tmp >= 0 && (next == -1 || tmp < next))
 		next = tmp;
@@ -583,16 +613,37 @@
 
 		if (srv->type == NAN_DE_PUBLISH &&
 		    os_reltime_initialized(&srv->pause_state_end) &&
-		    (os_reltime_before(&srv->pause_state_end, &now) ||
-		     (srv->publish.fsd &&
-		      os_reltime_initialized(&srv->last_followup) &&
-		      os_reltime_expired(&now, &srv->last_followup, 1))))
+		    (os_reltime_before(&srv->pause_state_end, &now)))
 			nan_de_unpause_state(srv);
 
 		srv_next = nan_de_srv_time_to_next(de, srv, &now);
 		if (srv_next >= 0 && (next == -1 || srv_next < next))
 			next = srv_next;
 
+		if (srv->type == NAN_DE_PUBLISH &&
+		    srv->publish.fsd &&
+		    os_reltime_initialized(&srv->pause_state_end) &&
+		    de->tx_wait_end_freq == 0 &&
+		    de->listen_freq == 0 && de->ext_listen_freq == 0) {
+			struct os_reltime diff;
+			int duration;
+
+			os_reltime_sub(&srv->pause_state_end, &now, &diff);
+			duration = os_reltime_in_ms(&diff);
+			if (duration < 0)
+				continue;
+			if ((unsigned int) duration > de->max_listen)
+				duration = de->max_listen;
+			if (de->cb.listen(de->cb.ctx, srv->freq, duration) ==
+			    0) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Publisher in pauseState - started listen on %u MHz",
+					   srv->freq);
+				de->listen_freq = srv->freq;
+				return;
+			}
+		}
+
 		if (srv_next == 0 && !started && !de->offload &&
 		    de->listen_freq == 0 && de->ext_listen_freq == 0 &&
 		    de->tx_wait_end_freq == 0 &&
@@ -629,7 +680,7 @@
 
 	if (next == 0)
 		next = 1;
-	wpa_printf(MSG_DEBUG, "NAN: Next timer in %u ms", next);
+
 	eloop_register_timeout(next / 1000, (next % 1000) * 1000, nan_de_timer,
 			       de, NULL);
 }
@@ -679,6 +730,10 @@
 
 void nan_de_tx_wait_ended(struct nan_de *de)
 {
+	if (de->tx_wait_end_freq)
+		wpa_printf(MSG_DEBUG,
+			   "NAN: TX wait for response ended (freq=%u)",
+			   de->tx_wait_end_freq);
 	de->tx_wait_end_freq = 0;
 	nan_de_run_timer(de);
 }
@@ -806,7 +861,7 @@
 
 
 static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
-			      const u8 *peer_addr, u8 instance_id,
+			      const u8 *peer_addr, const u8 *a3, u8 instance_id,
 			      u8 req_instance_id, u16 sdea_control,
 			      enum nan_service_protocol_type srv_proto_type,
 			      const u8 *ssi, size_t ssi_len)
@@ -877,7 +932,8 @@
 
 
 static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv,
-				const u8 *peer_addr, u8 instance_id,
+				const u8 *peer_addr, const u8 *a3,
+				u8 instance_id,
 				const u8 *matching_filter,
 				size_t matching_filter_len,
 				enum nan_service_protocol_type srv_proto_type,
@@ -887,6 +943,7 @@
 	size_t len = 0, sda_len, sdea_len;
 	u8 ctrl = 0;
 	u16 sdea_ctrl = 0;
+	const u8 *network_id;
 
 	/* Publish function processing of a receive Subscribe message */
 
@@ -963,15 +1020,23 @@
 		wpabuf_put_buf(buf, srv->elems);
 	}
 
-	/* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
-	 * but there is no synchronization in USD as as such, no NAN Cluster
-	 * either. Use Wildcard BSSID instead. */
+	if (srv->is_p2p)
+		network_id = p2p_network_id;
+	else
+		network_id = nan_network_id;
+
+	if (srv->publish.solicited_multicast || !a3)
+		a3 = network_id;
+	else if (srv->is_p2p)
+		a3 = de->nmi;
+
 	nan_de_tx(de, srv->freq, 100,
-		  srv->publish.solicited_multicast ? nan_network_id : peer_addr,
-		  de->nmi, wildcard_bssid, buf);
+		  srv->publish.solicited_multicast ? network_id : peer_addr,
+		  de->nmi, a3, buf);
 	wpabuf_free(buf);
 
-	nan_de_pause_state(srv, peer_addr, instance_id);
+	if (!srv->is_p2p)
+		nan_de_pause_state(srv, peer_addr, instance_id);
 
 offload:
 	if (!srv->publish.disable_events && de->cb.replied)
@@ -981,8 +1046,8 @@
 
 
 static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv,
-				const u8 *peer_addr, u8 instance_id,
-				const u8 *ssi, size_t ssi_len)
+				const u8 *peer_addr, const u8 *a3,
+				u8 instance_id, const u8 *ssi, size_t ssi_len)
 {
 	/* Follow-up function processing of a receive Follow-up message for a
 	 * Subscribe or Publish instance */
@@ -997,18 +1062,19 @@
 		return;
 	}
 
-	os_get_reltime(&srv->last_followup);
-
 	if (srv->type == NAN_DE_PUBLISH && !ssi)
 		nan_de_pause_state(srv, peer_addr, instance_id);
 
+	os_memcpy(srv->a3, a3, ETH_ALEN);
+	srv->a3_set = true;
+
 	if (de->cb.receive)
 		de->cb.receive(de->cb.ctx, srv->id, instance_id, ssi, ssi_len,
 			       peer_addr);
 }
 
 
-static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
+static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
 			  unsigned int freq, const u8 *buf, size_t len,
 			  const u8 *sda, size_t sda_len)
 {
@@ -1135,20 +1201,20 @@
 
 		switch (type) {
 		case NAN_SRV_CTRL_PUBLISH:
-			nan_de_rx_publish(de, srv, peer_addr, instance_id,
+			nan_de_rx_publish(de, srv, peer_addr, a3, instance_id,
 					  req_instance_id,
 					  sdea_control, srv_proto_type,
 					  ssi, ssi_len);
 			break;
 		case NAN_SRV_CTRL_SUBSCRIBE:
-			nan_de_rx_subscribe(de, srv, peer_addr, instance_id,
+			nan_de_rx_subscribe(de, srv, peer_addr, a3, instance_id,
 					    matching_filter,
 					    matching_filter_len,
 					    srv_proto_type,
 					    ssi, ssi_len);
 			break;
 		case NAN_SRV_CTRL_FOLLOW_UP:
-			nan_de_rx_follow_up(de, srv, peer_addr, instance_id,
+			nan_de_rx_follow_up(de, srv, peer_addr, a3, instance_id,
 					    ssi, ssi_len);
 			break;
 		}
@@ -1156,8 +1222,8 @@
 }
 
 
-void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
-		   const u8 *buf, size_t len)
+void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
+		   unsigned int freq, const u8 *buf, size_t len)
 {
 	const u8 *sda;
 	u16 sda_len;
@@ -1179,7 +1245,7 @@
 		sda++;
 		sda_len = WPA_GET_LE16(sda);
 		sda += 2;
-		nan_de_rx_sda(de, peer_addr, freq, buf, len, sda, sda_len);
+		nan_de_rx_sda(de, peer_addr, a3, freq, buf, len, sda, sda_len);
 	}
 }
 
@@ -1360,6 +1426,32 @@
 }
 
 
+int nan_de_unpause_publish(struct nan_de *de, int publish_id,
+			   u8 peer_instance_id, const u8 *peer_addr)
+{
+	struct nan_de_service *srv;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: UnpausePublish(publish_id=%d, peer_instance_id=%d peer_addr="
+		   MACSTR ")",
+		   publish_id, peer_instance_id, MAC2STR(peer_addr));
+
+	if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE)
+		return -1;
+	srv = de->service[publish_id - 1];
+	if (!srv || srv->type != NAN_DE_PUBLISH)
+		return -1;
+
+	if (srv->sel_peer_id != peer_instance_id ||
+	    !ether_addr_equal(peer_addr, srv->sel_peer_addr) ||
+	    !os_reltime_initialized(&srv->pause_state_end))
+		return -1;
+
+	nan_de_unpause_state(srv);
+	return 0;
+}
+
+
 int nan_de_subscribe(struct nan_de *de, const char *service_name,
 		     enum nan_service_protocol_type srv_proto_type,
 		     const struct wpabuf *ssi, const struct wpabuf *elems,
@@ -1442,6 +1534,8 @@
 		    const u8 *peer_addr, u8 req_instance_id)
 {
 	struct nan_de_service *srv;
+	const u8 *a3;
+	const u8 *network_id;
 
 	if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
 		return -1;
@@ -1450,9 +1544,17 @@
 	if (!srv)
 		return -1;
 
-	nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
-		      peer_addr, req_instance_id, ssi);
+	if (srv->is_p2p)
+		network_id = p2p_network_id;
+	else
+		network_id = nan_network_id;
 
-	os_get_reltime(&srv->last_followup);
+	if (srv->a3_set)
+		a3 = srv->a3;
+	else
+		a3 = network_id;
+	nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
+		      peer_addr, a3, req_instance_id, ssi);
+
 	return 0;
 }
diff --git a/src/common/nan_de.h b/src/common/nan_de.h
index f369a57..41e294e 100644
--- a/src/common/nan_de.h
+++ b/src/common/nan_de.h
@@ -59,7 +59,10 @@
 				      unsigned int freq);
 };
 
+bool nan_de_is_nan_network_id(const u8 *addr);
+bool nan_de_is_p2p_network_id(const u8 *addr);
 struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
+			    unsigned int max_listen,
 			    const struct nan_callbacks *cb);
 void nan_de_flush(struct nan_de *de);
 void nan_de_deinit(struct nan_de *de);
@@ -70,8 +73,8 @@
 void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst);
 void nan_de_tx_wait_ended(struct nan_de *de);
 
-void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
-		   const u8 *buf, size_t len);
+void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
+		   unsigned int freq, const u8 *buf, size_t len);
 const u8 * nan_de_get_service_id(struct nan_de *de, int id);
 
 struct nan_publish_params {
@@ -117,6 +120,9 @@
 int nan_de_update_publish(struct nan_de *de, int publish_id,
 			  const struct wpabuf *ssi);
 
+int nan_de_unpause_publish(struct nan_de *de, int publish_id,
+			   u8 peer_instance_id, const u8 *peer_addr);
+
 struct nan_subscribe_params {
 	/* configuration_parameters */
 
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index ddf1966..3cc2f93 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1298,6 +1298,54 @@
  *
  *	The attributes used with this command are defined in
  *	enum qca_wlan_vendor_attr_chan_usage_req.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT: Vendor subcommand that can be
+ *	used to fetch the current snapshot of scan data stored by firmware
+ *	during the offload scans such as PNO (Preferred Network Offload), RTT,
+ *	and roaming scans when the Apps or host is in suspended state. This scan
+ *	data comprises of only limited information of the scanned BSSs due to
+ *	memory limits of the firmware. The BSS information stored in the
+ *	firmware may not be pushed to the kernel (cfg80211) scan cache after
+ *	Apps or host coming out from suspended state if full Beacon or Probe
+ *	Response frame information is not available.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_fw_scan_report.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN: If there are no active Wi-Fi
+ *	interfaces for a certain duration, the host driver might trigger idle
+ *	shutdown. The host driver rejects the user space commands between start
+ *	and completion of the idle shutdown. If a command is rejected, user
+ *	space can use this event to determine when to retry the specific
+ *	command.
+ *
+ *	This is a wiphy specific vendor event and it indicates user space that
+ *	the host driver has reached the idle timer and has started or completed
+ *	idle shutdown procedure.
+ *
+ *	The attributes used with this event are defined in
+ *	enum qca_wlan_vendor_attr_idle_shutdown.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE: Vendor subcommand that can
+ *	be used to trigger primary link migration from user space. Either just
+ *	one ML client or a bunch of clients can be migrated.
+ *
+ *	The attributes used with this subcommand are defined in
+ *	&enum qca_wlan_vendor_attr_pri_link_migrate.
+ *
+ *	@QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR and
+ *	@QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID are mutually
+ *	exclusive attributes. Migration should be requested for either one ML
+ *	client or a bunch of ML clients.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG: Vendor subcommand that
+ *	can be used to send periodic or on-demand directed Probe Response frames
+ *	to a connected peer.
+ *
+ *	This command is only applicable for AP/P2P GO mode.
+ *
+ *	The attributes used with this command are defined in
+ * 	enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1534,6 +1582,11 @@
 	QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT = 250,
 	QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE = 251,
 	QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ = 252,
+	QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT = 253,
+	QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN = 254,
+	/* 255 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE = 256,
+	QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG = 257,
 };
 
 /* Compatibility defines for previously used subcmd names.
@@ -2015,7 +2068,9 @@
  *
  * @QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST: Required and type is NLA_UNSPEC.
  * Used with command to configure channel list using an array of
- * channel numbers (u8).
+ * channel numbers (u8). This represents the list of allowed channels for
+ * the primary and non-primary channel operation. Channels which are not present
+ * in the specified list shouldn't be used as a primary or non-primary channel.
  * Note: If both the driver and user-space application supports the 6 GHz band,
  * the driver mandates use of QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST whereas
  * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST is optional.
@@ -2047,7 +2102,10 @@
  *
  * @QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST: Required and type is NLA_UNSPEC.
  * Used with command to configure the channel list using an array of channel
- * center frequencies in MHz (u32).
+ * center frequencies in MHz (u32). This represents the list of allowed
+ * frequencies for the primary and non-primary channel operation. Frequencies
+ * which are not present in the specified list shouldn't be used as a primary or
+ * non-primary channel.
  * Note: If both the driver and user-space application supports the 6 GHz band,
  * the driver first parses the frequency list and if it fails to get a frequency
  * list, parses the channel list specified using
@@ -2110,6 +2168,15 @@
  * Used with command to configure ACS operation for a specific link affiliated
  * to an AP MLD.
  *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY: Optional flag
+ * attribute. Used with command to indicate whether the driver is allowed to use
+ * a 6 GHz non-PSC channel as a primary channel. If this flag is indicated the
+ * driver shall not use 6 GHz non-PSC channels as a primary channel even if
+ * %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST includes 6 GHz non-PSC channels.
+ * However, the driver is still allowed to use 6 GHz non-PSC channels specified
+ * in %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST as non-primary channels. User space is
+ * allowed to specify this flag only when the driver indicates support for
+ * %QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC.
  */
 enum qca_wlan_vendor_attr_acs_offload {
 	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
@@ -2134,6 +2201,7 @@
 	QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED = 19,
 	QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME = 20,
 	QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID = 21,
+	QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY = 22,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
@@ -2271,6 +2339,17 @@
  * @QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD: Flag indicates that the driver
  *	supports Unsynchronized Service Discovery to be offloaded to it.
  *
+ * @QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC: Flag indicates that the driver
+ *	supports preferring 6 GHz PSC channel as a primary channel in ACS
+ *	result.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_P2P_V2: Flag indicates that the driver supports
+ *	P2P R2 functionality (P2P R2 Discovery, Pairing, TWT power save, etc).
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_PCC_MODE: Flag indicates that the driver supports
+ *	P2P Connection Compatibility mode in which GO allows connection
+ *	with both P2P R1 and R2 clients.
+ *
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -2301,6 +2380,9 @@
 	QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER = 24,
 	QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA = 25,
 	QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD = 26,
+	QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC = 27,
+	QCA_WLAN_VENDOR_FEATURE_P2P_V2 = 28,
+	QCA_WLAN_VENDOR_FEATURE_PCC_MODE = 29,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -3754,6 +3836,58 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_P2P_GO_BEACON_INTERVAL = 122,
 
+	/* 8-bit unsigned value. Disable DFS owner capability
+	 * 1: disable DFS owner capability in the driver.
+	 * 0: reset DFS owner capability to the default DFS owner capability of
+	 * the driver.
+	 *
+	 * If DFS owner capability is disabled, the driver will not start AP
+	 * mode operations on DFS channels, and all the features depending on
+	 * DFS owner functionality will not be supported.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DFS_OWNER_DISABLE = 123,
+
+	/* 16-bit unsigned value. For probing RSSI on other antennas, this
+	 * attribute specifies the number of WLAN probes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_WLAN = 124,
+
+	/* 16-bit unsigned value. For probing RSSI on other antennas, this
+	 * attribute specifies the number of BT probes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_BT = 125,
+
+	/* 16-bit unsigned value. This attribute specifies the WLAN RSSI
+	 * threshold. The firmware will start to probe RSSI on other antenna
+	 * if WLAN RSSI is lower than the threshold.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_WLAN_RSSI_THRESHOLD = 126,
+
+	/* 16-bit unsigned value. This attribute specifies the BT RSSI
+	 * threshold. The firmware will start to probe RSSI on other antenna
+	 * if BT RSSI is lower than the threshold.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_BT_RSSI_THRESHOLD = 127,
+
+	/* 16-bit unsigned value. This attribute specifies the WLAN RSSI
+	 * difference. The firmware will select a better antenna if WLAN RSSI
+	 * difference is larger than the value.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_WLAN_RSSI_DIFF = 128,
+
+	/* 16-bit unsigned value. This attribute specifies the BT RSSI
+	 * difference. The firmware will select a better antenna if WLAN RSSI
+	 * difference larger than the value.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_BT_RSSI_DIFF = 129,
+
+	/* 8-bit unsigned value to enable/disable setup link Reconfiguration
+	 * feature support in STA mode.
+	 * 1 - Enable
+	 * 0 - Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_SETUP_LINK_RECONFIG_SUPPORT = 130,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -5823,6 +5957,11 @@
  * @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered
  *	based on the scan results obtained from an external scan (not triggered
  *	to aim roaming).
+ * @QCA_ROAM_TRIGGER_REASON_WTC: Set if the roam has to be triggered
+ *	due to Wireless to Cellular BSS Transition Management (BTM) request.
+ * @QCA_ROAM_TRIGGER_REASON_BT_ACTIVITY: Set if the roam has to be triggered
+ *	due to Bluetooth connection is established when the station is connected
+ *	in the 2.4 GHz band.
  *
  * Set the corresponding roam trigger reason bit to consider it for roam
  * trigger.
@@ -5843,6 +5982,8 @@
 	QCA_ROAM_TRIGGER_REASON_IDLE		= 1 << 10,
 	QCA_ROAM_TRIGGER_REASON_TX_FAILURES	= 1 << 11,
 	QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN	= 1 << 12,
+	QCA_ROAM_TRIGGER_REASON_WTC	        = 1 << 13,
+	QCA_ROAM_TRIGGER_REASON_BT_ACTIVITY	= 1 << 14,
 };
 
 /*
@@ -10490,6 +10631,40 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RSNE_ADD_RANDOM_PMKIDS = 75,
 
+	/* 8-bit unsigned value to configure Triggered SU Beamforming Feedback
+	 * support in the EHT capabilities of an Association Request frame.
+	 * 1-enable, 0-disable
+	 *
+	 * This attribute is used for testing purposes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_TRIG_SU_BFORMING_FEEDBACK = 76,
+
+	/* 8-bit unsigned value to configure the extra EHT-LTFs support in the
+	 * EHT capabilities of an Association Request frame.
+	 * 1-enable, 0-disable
+	 *
+	 * This attribute is used for testing purposes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_EXTRA_LTF = 77,
+
+	/* 8-bit unsigned integer to configure the firmware to reject AP's BSS
+	 * Transition Management (BTM) request frame by sending a BTM response
+	 * with error status code.
+	 *
+	 * 1 - STA rejects AP's BTM request frame
+	 * 0 - STA accepts AP's BTM request frame
+	 *
+	 * This attribute is used for testing purposes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_REJECT = 78,
+
+	/* Nested attribute to control the response of the driver upon receiving
+	 * a BTM request from the AP.
+	 * Uses the enum qca_wlan_vendor_attr_btm_req_resp attributes.
+	 * This attribute is used to configure the STA.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_RESP = 79,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -10783,6 +10958,42 @@
 };
 
 /**
+ * enum qca_wlan_twt_session_suspendable: The values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE: TWT session cannot be suspended.
+ * @QCA_WLAN_TWT_SESSION_SUSPENDABLE: TWT session can be suspended.
+ */
+enum qca_wlan_twt_session_suspendable {
+	QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE = 0,
+	QCA_WLAN_TWT_SESSION_SUSPENDABLE = 1,
+};
+
+/**
+ * enum qca_wlan_twt_session_updatable: Define the values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_UPDATABLE: TWT session cannot be updated.
+ * @QCA_WLAN_TWT_SESSION_UPDATABLE: TWT session can be updated.
+ */
+enum qca_wlan_twt_session_updatable {
+	QCA_WLAN_TWT_SESSION_NOT_UPDATABLE = 0,
+	QCA_WLAN_TWT_SESSION_UPDATABLE = 1,
+};
+
+/**
+ * enum qca_wlan_twt_session_implicit: Define the values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_IMPLICIT: TWT session cannot be implicit.
+ * @QCA_WLAN_TWT_SESSION_IMPLICIT: TWT session can be implicit.
+ */
+enum qca_wlan_twt_session_implicit {
+	QCA_WLAN_TWT_SESSION_NOT_IMPLICIT = 0,
+	QCA_WLAN_TWT_SESSION_IMPLICIT = 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
  * TWT (Target Wake Time) setup request. These attributes are sent as part of
  * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
@@ -10974,9 +11185,10 @@
  * The Broadcast TWT Recommendation subfield contains a value that indicates
  * recommendations on the types of frames that are transmitted by TWT
  * scheduled STAs and scheduling AP during the broadcast TWT SP.
- * The allowed values are 0 - 3.
+ * The allowed values are 0 - 4.
  * This parameter is used for
  * 1. TWT SET Request
+ * 2. R-TWT SET Request
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE: Optional (u8)
  * This attribute is used to configure Broadcast TWT Persistence.
@@ -11011,6 +11223,38 @@
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD: Attribute used for padding for 64-bit
  * alignment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE: Optional (u8)
+ * This attribute indicates whether the TWT session being negotiated can be
+ * suspended.
+ * Refers the enum qca_wlan_twt_session_suspendable.
+ * This parameter is used for
+ * 1. TWT SET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP: Optional (u32)
+ * This attribute is used to configure downlink TIDs for R-TWT scheduling.
+ * This attribute only applicable when requesting R-TWT schedules.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP: Optional (u32)
+ * This attribute is used to configure uplink TIDs for R-TWT scheduling.
+ * This attribute only applicable when requesting R-TWT schedules.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE: Optional (u8)
+ * This attribute indicates whether the parameters of the TWT session being
+ * negotiated (like wake interval, wake duration, etc.) can be updated after
+ * session setup.
+ * Refers the enum qca_wlan_twt_session_updatable.
+ * This parameter is used for
+ * 1. TWT SET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT: Optional (u8)
+ * This attribute indicates whether the TWT session being negotiated is
+ * an implicit TWT, where the requesting STA calculates the start time of the
+ * next TWT service period, or an explicit TWT, where the responding STA
+ * calculates the start time of the next TWT service period.
+ * Refers the enum qca_wlan_twt_session_implicit.
+ * This parameter is used for
+ * 1. TWT SET Response
  */
 enum qca_wlan_vendor_attr_twt_setup {
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
@@ -11048,6 +11292,11 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT = 26,
 
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD = 27,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE = 28,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP = 29,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP = 30,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE = 31,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT = 32,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
@@ -11103,6 +11352,16 @@
  * 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.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE: FW terminated
+ * the TWT session due to more than one MLO link becoming active. Used on the
+ * TWT_TERMINATE notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED: TWT session already in
+ * resumed state. Used on the TWT_RESUME notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED: Requested TWT operation is
+ * rejected by the peer. Used on the TWT_SET notification from the
+ * driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT: Requested TWT operation has timed out.
+ * Used on the TWT_SET, TWT_TERMINATE notification from the driver/firmware.
  */
 enum qca_wlan_vendor_twt_status {
 	QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
@@ -11130,6 +11389,10 @@
 	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,
+	QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE = 25,
+	QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED = 26,
+	QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED = 27,
+	QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT = 28,
 };
 
 /**
@@ -11312,6 +11575,16 @@
  * Status of the TWT GET STATISTICS request.
  * This contains status values in enum qca_wlan_vendor_twt_status
  * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US: Optional (u32)
+ * Average of duration of the early terminated TWT service periods
+ * in micro seconds.
+ * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT: Optional (u32)
+ * Number of early terminated TWT service periods observed over
+ * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS.
+ * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
  */
 enum qca_wlan_vendor_attr_twt_stats {
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_INVALID = 0,
@@ -11327,6 +11600,8 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE = 10,
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE = 11,
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS = 12,
+	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US = 13,
+	QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT = 14,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST,
@@ -11384,12 +11659,28 @@
  * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER: (u16).
  * Peer TWT capabilities. Carries a bitmap of TWT capabilities specified in
  * enum qca_wlan_twt_capa.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL: (u32).
+ * Minimum tolerance limit of wake interval parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL: (u32).
+ * Maximum tolerance limit of wake interval parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION: (u32).
+ * Minimum tolerance limit of wake duration parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION: (u32).
+ * Maximum tolerance limit of wake duration parameter in microseconds.
  */
 enum qca_wlan_vendor_attr_twt_capability {
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR = 1,
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF = 2,
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER = 3,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL = 4,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL = 5,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION = 6,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION = 7,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST,
@@ -11458,11 +11749,31 @@
  * which the STA may accept.
  * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any
  * alternate parameters than the requested ones.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING: TWT responding STA suggests TWT
+ * group parameters that are different from the suggested or demanded TWT
+ * parameters of the TWT requesting STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT: TWT responding STA or TWT scheduling
+ * AP accepts the TWT request with the TWT parameters indicated in the TWT
+ * element transmitted by the TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT: TWT responding STA or TWT
+ * scheduling AP suggests TWT parameters that are different from those suggested
+ * by the TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT: TWT responding STA or TWT scheduling
+ * dictates TWT parameters that are different from those suggested by the
+ * TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT: A TWT responding STA or TWT scheduling
+ * AP rejects setup or terminates an existing broadcast TWT, or a TWT scheduled
+ * STA terminates its membership in a broadcast TWT.
  */
 enum qca_wlan_vendor_twt_setup_req_type {
 	QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1,
 	QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2,
 	QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3,
+	QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING = 4,
+	QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT = 5,
+	QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT = 6,
+	QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT = 7,
+	QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT = 8,
 };
 
 /**
@@ -14496,6 +14807,22 @@
 	QCA_WLAN_VENDOR_MONITOR_CTRL_TRIGGER_FRAME = BIT(1),
 };
 
+/*
+ * enum qca_wlan_vendor_monitor_operating_type: Attributes used by vendor
+ * attribute %QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE
+ *
+ * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC: Local packet capture.
+ * Capture frames sent and received by the current client interface from the
+ * BSS.
+ *
+ * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC: Operating channel capture.
+ * Capture all frames on the current operating channel of client interface.
+ */
+enum qca_wlan_vendor_monitor_operating_type {
+	QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC = 0,
+	QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC = 1,
+};
+
 /**
  * enum qca_wlan_vendor_attr_set_monitor_mode - Used by the
  * vendor command QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE to set the
@@ -14532,6 +14859,12 @@
  * Represents the interval in milliseconds only for the connected Beacon frames,
  * expecting the connected BSS's Beacon frames to be sent on the monitor
  * interface at this specific interval.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE: u32 attribute.
+ * Represents the monitor operating type (u32). These operating types are
+ * defined in enum qca_wlan_vendor_monitor_operating_type.
+ * If this attribute is not included, default operating type LPC ("local
+ * packet capture") used.
  */
 enum qca_wlan_vendor_attr_set_monitor_mode {
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_INVALID = 0,
@@ -14542,6 +14875,7 @@
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_TX_FRAME_TYPE = 5,
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_RX_FRAME_TYPE = 6,
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CONNECTED_BEACON_INTERVAL = 7,
+	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE = 8,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_AFTER_LAST,
@@ -14832,6 +15166,56 @@
  *	If present, it indicates the successful PASN handshake with the peer. If
  *	this flag is not present, it indicates that the PASN handshake with the
  *	peer device failed.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM: Optional u32 attribute. It indicates the
+ *	AKM suite that is preferred in the PASN handshake in the event from the
+ *	driver to userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ *	%QCA_WLAN_VENDOR_PASN_ACTION_AUTH. In the status report from userspace
+ *	to the driver, it indicates the actual AKM suite used in the handshake.
+ *	Userspace can select the AKM based on the AP's capabilities, if the
+ *	given AKM suite is not applicable. Possible values are defined in
+ *	IEEE Std 802.11-2020, 9.4.2.24.3 (AKM suites) (e.g., 0x000FAC04)
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER: Optional u32 attribute. It indicates
+ *	the pairwise cipher suite that is preferred in the PASN handshake in
+ *	the event from the driver to userspace when
+ *	%QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ *	%QCA_WLAN_VENDOR_PASN_ACTION_AUTH. In the status report from userspace
+ *	to the driver, it indicates the actual cipher used in the handshake.
+ *	Userspace can select the cipher suite based on the capabilities of the
+ *	P, if the given cipher suite is not applicable. Possible values are
+ *	defined in IEEE Std 802.11-2020, 9.4.2.24.2 (Cipher suites)
+ *	(e.g., 0x000FAC04).
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD: This is a variable length byte
+ *	array attribute. This attribute is present if the AKM suite specified
+ *	in %QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM requires a password. The
+ *	password is used in PASN handshake request in an event from the driver
+ *	to userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ *	%QCA_WLAN_VENDOR_PASN_ACTION_AUTH.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_PMKID: This is a byte array attribute with a
+ *	size of 16 bytes. When this attribute is present this PMKSA caching
+ *	using the PMKSA identified by this PMKID is preferred to be used with
+ *	PASN. This attribute is sent along with PASN handshake request in an
+ *	event from the driver to userspace when
+ *	%QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ *	%QCA_WLAN_VENDOR_PASN_ACTION_AUTH.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER: u16 attribute in units for
+ *	TUs (1024 microseconds). This attribute is sent from userspace along
+ *	with the attribute %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE to the
+ *	driver in the status report using the %QCA_NL80211_VENDOR_SUBCMD_PASN
+ *	subcommand when the AP request PASN to be retried later.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE: This is a variable length byte array
+ *	attribute. In case an AP refused PASN temporarily, the STA can retry
+ *	PASN handshake by attaching this attribute data to PASN request after
+ *	the time period mentioned in the attribute
+ *	%QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER.
+ *	In case the AP refused the PASN handshake temporarily, cookie data is
+ *	received from the AP and it is sent from userspace to the driver along
+ *	with the attribute %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER in
+ *	the status report using the %QCA_NL80211_VENDOR_SUBCMD_PASN subcommand.
+ *	When the driver wants to retry PASN with the same AP after having
+ *	received this information, this attribute must be sent along with PASN
+ *	handshake request in an event from the driver to
+ *	userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ *	%QCA_WLAN_VENDOR_PASN_ACTION_AUTH.
  */
 enum qca_wlan_vendor_attr_pasn_peer {
 	QCA_WLAN_VENDOR_ATTR_PASN_PEER_INVALID = 0,
@@ -14839,6 +15223,12 @@
 	QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR = 2,
 	QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED = 3,
 	QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS = 4,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM = 5,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER = 6,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD = 7,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_PMKID = 8,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER = 9,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE = 10,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_PASN_PEER_AFTER_LAST,
@@ -17547,6 +17937,8 @@
  * @QCA_TRAFFIC_TYPE_SCREEN_SHARE: Traffic type is screen share
  * @QCA_TRAFFIC_TYPE_UNKNOWN: Traffic type is unknown
  * @QCA_TRAFFIC_TYPE_INVALID: Invalid traffic type
+ * @QCA_TRAFFIC_TYPE_BROWSING: Traffic type is browsing website
+ * @QCA_TRAFFIC_TYPE_APERIODIC_BURSTS: Traffic type is aperiodic bursts
  */
 enum qca_traffic_type {
 	QCA_TRAFFIC_TYPE_STREAMING = 0,
@@ -17556,6 +17948,8 @@
 	QCA_TRAFFIC_TYPE_SCREEN_SHARE = 4,
 	QCA_TRAFFIC_TYPE_UNKNOWN = 5,
 	QCA_TRAFFIC_TYPE_INVALID = 6,
+	QCA_TRAFFIC_TYPE_BROWSING = 7,
+	QCA_TRAFFIC_TYPE_APERIODIC_BURSTS = 8,
 };
 
 /**
@@ -18380,4 +18774,296 @@
 	QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_fw_scan_bss_flags - Flags for
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_HT_OPS: This indicates HT Operation element
+ * (IEEE Std 802.11-2020, 9.4.2.56) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_VHT_OPS: This indicates VHT Operation element
+ * (IEEE Std 802.11-2020, 9.4.2.158) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_HE_OPS: This indicates HE Operation element
+ * (IEEE Std 802.11ax-2021, 9.4.2.249) is present in the Beacon or Probe
+ * Response frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_EHT_OPS: This indicates EHT Operation element
+ * (IEEE P802.11be/D7.0, 9.4.2.321) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER: This indicates Fine Timing Measurement
+ * Responder bit is set to 1 in the Extended Capabilities field of the Extended
+ * Capabilities element (IEEE Std 802.11-2020, 9.4.2.26) in the Beacon or Probe
+ * Response frame of the BSS.
+ *
+ * @NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS: Number of assigned feature bits.
+ */
+enum qca_wlan_fw_scan_bss_flags {
+	QCA_WLAN_FW_SCAN_BSS_HT_OPS = 0,
+	QCA_WLAN_FW_SCAN_BSS_VHT_OPS = 1,
+	QCA_WLAN_FW_SCAN_BSS_HE_OPS = 2,
+	QCA_WLAN_FW_SCAN_BSS_EHT_OPS = 3,
+	QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER = 4,
+
+	NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS /* keep last */
+};
+
+/* enum qca_wlan_vendor_attr_fw_scan_bss: Attributes used inside
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO: Required (u32). Indicates how many
+ * milliseconds ago from %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP this BSS
+ * was last scanned (i.e., Beacon or Probe Response frame received).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID: Required (6-byte MAC address). BSSID
+ * of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID: Required (binary attribute,
+ * 0..32 octets). SSID of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI: Required (s8). RSSI of the last
+ * received Beacon or Probe Response frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY: Required (CPU byte order, u16).
+ * The Capability Information field from the last received Beacon or Probe
+ * Response frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS: BSS capability flags contained in a
+ * byte array. The flags are identified by their bit index (see &enum
+ * qca_wlan_fw_scan_bss_flags) with the first byte being the least significant
+ * one and the last one being the most significant one. This information will be
+ * populated from the last received Beacon or Probe Response frame. This is a
+ * mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ: Required (u32). Indicates
+ * primary 20 MHz channel center frequency in MHz of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH: Required (u8). Indicates
+ * channel width of the BSS. This uses values defined in
+ * enum nl80211_chan_width.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1: Required (u32). Indicates the
+ * center frequency (MHz) of the first segment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2: Optional (u32). Indicates the
+ * center frequency (MHz) of the second segment. Used only for
+ * %NL80211_CHAN_WIDTH_80P80 value in
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH.
+ */
+enum qca_wlan_vendor_attr_fw_scan_bss {
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO = 1,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID = 2,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID = 3,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI = 4,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY = 5,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS = 6,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ = 7,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH = 8,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1 = 9,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2 = 10,
+
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MAX =
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST - 1,
+};
+
+/* enum qca_wlan_vendor_attr_fw_scan_report: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP: 64-bit unsigned value to
+ * indicate the timestamp when this report is generated, timestamp in
+ * microseconds from system boot. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST: Nested attribute of u32
+ * attributes. This indicates the list of frequencies that were scanned. This is
+ * an optional attribute. If this is not specified, all frequencies allowed in
+ * the current regulatory domain were scanned.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST: Nested attribute.
+ * This indicates information of the scanned BSSs by the firmware. This is an
+ * optional attribute.
+ *
+ * The attributes defined in enum qca_wlan_vendor_attr_fw_scan_bss are nested
+ * in this attribute.
+ */
+enum qca_wlan_vendor_attr_fw_scan_report {
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP = 1,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST = 2,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST = 3,
+
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_MAX =
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_idle_shutdown_status: Represents idle shutdown status.
+ *
+ * @QCA_WLAN_IDLE_SHUTDOWN_STARTED: Indicates idle shutdown is started in the
+ * host driver.
+ * @QCA_WLAN_IDLE_SHUTDOWN_COMPLETED: Indicates idle shutdown is completed in
+ * the host driver.
+ */
+enum qca_wlan_idle_shutdown_status {
+	QCA_WLAN_IDLE_SHUTDOWN_STARTED = 0,
+	QCA_WLAN_IDLE_SHUTDOWN_COMPLETED = 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_idle_shutdown: Attributes used by vendor event
+ * %QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS: Required u8 attribute. Indicates
+ * the status of the idle shutdown from one of the values in enum
+ * qca_wlan_idle_shutdown_status.
+ */
+enum qca_wlan_vendor_attr_idle_shutdown {
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS = 1,
+
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_MAX =
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_pri_link_migrate: Attributes used by the vendor
+ * 	subcommand %QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR: 6 byte MAC address. When
+ *	specified, indicates that primary link migration will occur only for
+ *	the ML client with the given MLD MAC address.
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID: Optional u8
+ *	attribute. When specified, all ML clients having their current primary
+ *	link as specified will be considered for migration.
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_NEW_PRI_LINK_ID: Optional u8 attribute.
+ *	Indicates the new primary link to which the selected ML clients
+ *	should be migrated to. If not provided, the driver will select a
+ *	suitable primary link on its own.
+ */
+enum qca_wlan_vendor_attr_pri_link_migrate {
+	QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR = 1,
+	QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID = 2,
+	QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_NEW_PRI_LINK_ID = 3,
+
+	/* keep this last */
+	QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MAX =
+	QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_btm_req_resp_type: Represents response types to follow
+ * upon receiving BTM request from AP.
+ *
+ * @QCA_WLAN_BTM_REQ_RESP_DEFAULT: Reset to default behavior.
+ * @QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME: Send link reconfiguration request
+ * frames with specified info.
+ * @QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME: Send TTLM request frame.
+ * @QCA_WLAN_BTM_REQ_RESP_REASSOC_FRAME: Send Reassociation Request frame.
+ */
+enum qca_wlan_vendor_btm_req_resp_type {
+	QCA_WLAN_BTM_REQ_RESP_DEFAULT = 0,
+	QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME = 1,
+	QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME = 2,
+	QCA_WLAN_BTM_REQ_RESP_REASSOC_FRAME = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_reconfig_frame_info - Attribute used by
+ * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RECONFIG_ADD_LINKS_BITMASK: u16 attribute. Bitmask of
+ * link IDs to be added.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RECONFIG_DELETE_LINKS_BITMASK: u16 attribute bitmask of
+ * link IDs to be removed.
+ */
+enum qca_wlan_vendor_attr_reconfig_frame_info {
+	QCA_WLAN_VENDOR_ATTR_RECONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_RECONFIG_ADD_LINKS_BITMASK = 1,
+	QCA_WLAN_VENDOR_ATTR_RECONFIG_DELETE_LINKS_BITMASK = 2,
+
+	QCA_WLAN_VENDOR_ATTR_RECONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_RECONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_RECONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_btm_req_resp - Attribute used by
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_RESP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE: u8 attribute. Indicates type of
+ * response to send. Possible values for this attribute are defined in
+ * enum qca_wlan_vendor_btm_req_resp_type. This is a mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO: Array of nested
+ * attributes containing information about one or more setup link
+ * reconfiguration request frames, each set represents one link reconfiguration
+ * frame information. The driver shall send a separate link reconfiguration
+ * frame for each nested attribute set. It takes attributes as defined in enum
+ * qca_wlan_vendor_attr_reconfig_frame_info. This attribute must be present
+ * when %QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME specified in
+ * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TTLM_MAP: TID to Link Mapping to
+ * be used in TTLM request frame. This nested attribute with
+ * %NL80211_ATTR_MLO_TTLM_DLINK and %NL80211_ATTR_MLO_TTLM_ULINK is used to
+ * specify the TID to Link mapping for downlink/uplink traffic. This attribute
+ * must be present when %QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME specified in
+ * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE attribute.
+ */
+enum qca_wlan_vendor_attr_btm_req_resp {
+	QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE = 1,
+	QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO = 2,
+	QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TTLM_MAP = 3,
+
+	QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_MAX =
+	QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg: Attributes used
+ * by vendor subcmd QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PEER_MAC_ADDR: Connected peer
+ * MAC address to which Probe Response frames are to be sent.
+ * Multicast/Broadcast addresses are not supported.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD: 32-bit unsigned value.
+ * This attribute specifies the interval (in microseconds) in which directed
+ * Probe Response frames are sent periodically to the peer as specified in
+ * attribute QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PEER_MAC_ADDR. When the peer
+ *is in power save, sending of the frames might be delayed until the device
+ * comes out of power save. Attribute value can be in the range of minimum value
+ * of 50000 and maximum value of 1500000.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_COUNT: 8-bit unsigned value.
+ * Specifies number of directed Probe Responses frames that can be sent as per
+ * interval defined in QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD. When
+ * attribute value is 255, directed Probe Response frames are sent continuously
+ * until this attribute is sent as 0 in the command to disable period
+ * transmission. When the attribute value is 1, one directed Probe Response
+ * frame will be sent and the attribute
+ * QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD will not be considered.
+ */
+enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg {
+	QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_PEER_MAC_ADDR = 1,
+	QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_PERIOD = 2,
+	QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_COUNT = 3,
+
+	QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_MAX =
+	QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index a65da61..801f363 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -114,6 +114,7 @@
 	wpabuf_free(tmp->own_rejected_groups);
 	wpabuf_free(tmp->peer_rejected_groups);
 	os_free(tmp->pw_id);
+	os_free(tmp->parsed_pw_id);
 	bin_clear_free(tmp, sizeof(*tmp));
 	sae->tmp = NULL;
 }
@@ -121,12 +122,16 @@
 
 void sae_clear_data(struct sae_data *sae)
 {
+	unsigned int no_pw_id;
+
 	if (sae == NULL)
 		return;
 	sae_clear_temp_data(sae);
 	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
 	crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0);
+	no_pw_id = sae->no_pw_id;
 	os_memset(sae, 0, sizeof(*sae));
+	sae->no_pw_id = no_pw_id;
 }
 
 
@@ -361,8 +366,11 @@
 		const_time_select_bin(found, stub_password, password,
 				      password_len, tmp_password);
 		if (hmac_sha256_vector(addrs, sizeof(addrs), 2,
-				       addr, len, pwd_seed) < 0)
+				       addr, len, pwd_seed) < 0) {
+			wpa_printf(MSG_INFO,
+				   "SAE: hmac_sha256_vector() failed - cannot derive PWE");
 			break;
+		}
 
 		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
 					    prime, qr_bin, qnr_bin, x_cand_bin);
@@ -1093,12 +1101,13 @@
 }
 
 
-struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
+struct sae_pt * sae_derive_pt(const int *groups,
+			      const u8 *ssid, size_t ssid_len,
 			      const u8 *password, size_t password_len,
 			      const char *identifier)
 {
 	struct sae_pt *pt = NULL, *last = NULL, *tmp;
-	int default_groups[] = { 19, 0 };
+	const int default_groups[] = { 19, 0 };
 	int i;
 
 	if (!groups)
@@ -1877,8 +1886,6 @@
 				      const u8 *pos, const u8 *end,
 				      const u8 **token, size_t *token_len)
 {
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    pos, end - pos);
 	if (!sae_is_token_container_elem(pos, end))
 		return;
 	*token = pos + 3;
@@ -2045,14 +2052,12 @@
 }
 
 
-static int sae_parse_password_identifier(struct sae_data *sae,
+static int sae_parse_password_identifier(struct sae_data *sae, bool h2e,
 					 const u8 **pos, const u8 *end)
 {
 	const u8 *epos;
 	u8 len;
 
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    *pos, end - *pos);
 	if (!sae_is_password_id_elem(*pos, end)) {
 		if (sae->tmp->pw_id) {
 			wpa_printf(MSG_DEBUG,
@@ -2060,8 +2065,8 @@
 				   sae->tmp->pw_id);
 			return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 		}
-		os_free(sae->tmp->pw_id);
-		sae->tmp->pw_id = NULL;
+		os_free(sae->tmp->parsed_pw_id);
+		sae->tmp->parsed_pw_id = NULL;
 		return WLAN_STATUS_SUCCESS; /* No Password Identifier */
 	}
 
@@ -2073,6 +2078,18 @@
 	epos++; /* skip ext ID */
 	len--;
 
+	if (!h2e) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Password Identifier included, but H2E is not used");
+		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+	}
+
+	if (sae->no_pw_id) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Password Identifier included, but none has been enabled");
+		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+	}
+
 	if (sae->tmp->pw_id &&
 	    (len != os_strlen(sae->tmp->pw_id) ||
 	     os_memcmp(sae->tmp->pw_id, epos, len) != 0)) {
@@ -2082,14 +2099,14 @@
 		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 	}
 
-	os_free(sae->tmp->pw_id);
-	sae->tmp->pw_id = os_malloc(len + 1);
-	if (!sae->tmp->pw_id)
+	os_free(sae->tmp->parsed_pw_id);
+	sae->tmp->parsed_pw_id = os_malloc(len + 1);
+	if (!sae->tmp->parsed_pw_id)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	os_memcpy(sae->tmp->pw_id, epos, len);
-	sae->tmp->pw_id[len] = '\0';
+	os_memcpy(sae->tmp->parsed_pw_id, epos, len);
+	sae->tmp->parsed_pw_id[len] = '\0';
 	wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
-			  sae->tmp->pw_id, len);
+			  sae->tmp->parsed_pw_id, len);
 	*pos = epos + len;
 	return WLAN_STATUS_SUCCESS;
 }
@@ -2101,8 +2118,6 @@
 	const u8 *epos;
 	u8 len;
 
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    *pos, end - *pos);
 	if (!sae_is_rejected_groups_elem(*pos, end)) {
 		wpabuf_free(sae->tmp->peer_rejected_groups);
 		sae->tmp->peer_rejected_groups = NULL;
@@ -2141,8 +2156,6 @@
 	const u8 *epos;
 	u8 len;
 
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    *pos, end - *pos);
 	if (!sae_is_akm_suite_selector_elem(*pos, end))
 		return WLAN_STATUS_SUCCESS;
 
@@ -2195,8 +2208,13 @@
 	if (ie_offset)
 		*ie_offset = pos - data;
 
+	if (end > pos)
+		wpa_hexdump(MSG_DEBUG,
+			    "SAE: Possible elements at the end of the frame",
+			    pos, end - pos);
+
 	/* Optional Password Identifier element */
-	res = sae_parse_password_identifier(sae, &pos, end);
+	res = sae_parse_password_identifier(sae, h2e, &pos, end);
 	if (res != WLAN_STATUS_SUCCESS)
 		return res;
 
diff --git a/src/common/sae.h b/src/common/sae.h
index a353aa8..0d94e1f 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -59,11 +59,13 @@
 	struct crypto_bignum *order_buf;
 	struct wpabuf *anti_clogging_token;
 	char *pw_id;
+	char *parsed_pw_id;
 	int vlan_id;
 	u8 bssid[ETH_ALEN];
 	struct wpabuf *own_rejected_groups;
 	struct wpabuf *peer_rejected_groups;
 	unsigned int own_addr_higher:1;
+	unsigned int try_other_password:1;
 
 #ifdef CONFIG_SAE_PK
 	u8 kek[SAE_MAX_HASH_LEN];
@@ -84,6 +86,8 @@
 #endif /* CONFIG_SAE_PK */
 
 	struct os_reltime disabled_until;
+
+	const void *used_pw;
 };
 
 struct sae_pt {
@@ -120,6 +124,7 @@
 	u16 rc; /* protocol instance variable: Rc (received send-confirm) */
 	unsigned int h2e:1;
 	unsigned int pk:1;
+	unsigned int no_pw_id:1;
 	struct sae_temporary_data *tmp;
 };
 
@@ -146,7 +151,8 @@
 const char * sae_state_txt(enum sae_state state);
 size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
 size_t sae_ffc_prime_len_2_hash_len(size_t prime_len);
-struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
+struct sae_pt * sae_derive_pt(const int *groups,
+			      const u8 *ssid, size_t ssid_len,
 			      const u8 *password, size_t password_len,
 			      const char *identifier);
 struct crypto_ec_point *
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index a8c7c41..613ea7f 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -134,8 +134,7 @@
  */
 int wpa_use_akm_defined(int akmp)
 {
-	return akmp == WPA_KEY_MGMT_OSEN ||
-		akmp == WPA_KEY_MGMT_OWE ||
+	return akmp == WPA_KEY_MGMT_OWE ||
 		akmp == WPA_KEY_MGMT_DPP ||
 		akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
 		akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 ||
@@ -152,8 +151,7 @@
  */
 int wpa_use_cmac(int akmp)
 {
-	return akmp == WPA_KEY_MGMT_OSEN ||
-		akmp == WPA_KEY_MGMT_OWE ||
+	return akmp == WPA_KEY_MGMT_OWE ||
 		akmp == WPA_KEY_MGMT_DPP ||
 		wpa_key_mgmt_ft(akmp) ||
 		wpa_key_mgmt_sha256(akmp) ||
@@ -174,8 +172,7 @@
  */
 int wpa_use_aes_key_wrap(int akmp)
 {
-	return akmp == WPA_KEY_MGMT_OSEN ||
-		akmp == WPA_KEY_MGMT_OWE ||
+	return akmp == WPA_KEY_MGMT_OWE ||
 		akmp == WPA_KEY_MGMT_DPP ||
 		akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 ||
 		wpa_key_mgmt_ft(akmp) ||
@@ -266,12 +263,6 @@
 			os_memcpy(mic, hash, key_len);
 			break;
 #endif /* CONFIG_SAE */
-#ifdef CONFIG_HS20
-		case WPA_KEY_MGMT_OSEN:
-			wpa_printf(MSG_DEBUG,
-				   "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
-			return omac1_aes_128(key, buf, len, mic);
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_SUITEB
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
 			wpa_printf(MSG_DEBUG,
@@ -582,6 +573,7 @@
 	ptk->kek2_len = 0;
 	ptk->kck2_len = 0;
 
+	ptk->ptk_len = ptk_len;
 	os_memset(tmp, 0, sizeof(tmp));
 	os_memset(data, 0, data_len);
 	return 0;
@@ -1560,6 +1552,7 @@
 				ptk->kdk, ptk->kdk_len);
 	}
 
+	ptk->ptk_len = ptk_len;
 	forced_memzero(tmp, sizeof(tmp));
 	ret = 0;
 err:
@@ -1829,8 +1822,6 @@
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP)
 		return WPA_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
-	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
-		return WPA_KEY_MGMT_OSEN;
 #ifdef CONFIG_PASN
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PASN)
 		return WPA_KEY_MGMT_PASN;
@@ -1891,17 +1882,7 @@
 		return -1;
 	}
 
-	if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 &&
-	    rsn_ie[1] == rsn_ie_len - 2 &&
-	    WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) {
-		pos = rsn_ie + 6;
-		left = rsn_ie_len - 6;
-
-		data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
-		data->has_group = 1;
-		data->key_mgmt = WPA_KEY_MGMT_OSEN;
-		data->proto = WPA_PROTO_OSEN;
-	} else if (rsn_ie_len >= 2 + 4 + 2 && rsn_ie[1] >= 4 + 2 &&
+	if (rsn_ie_len >= 2 + 4 + 2 && rsn_ie[1] >= 4 + 2 &&
 		   rsn_ie[1] == rsn_ie_len - 2 &&
 		   (WPA_GET_BE32(&rsn_ie[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE ||
 		    WPA_GET_BE32(&rsn_ie[2]) ==
@@ -2799,8 +2780,6 @@
 		return "FT-SAE";
 	case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
 		return "FT-SAE-EXT-KEY";
-	case WPA_KEY_MGMT_OSEN:
-		return "OSEN";
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
 		return "WPA2-EAP-SUITE-B";
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
@@ -2847,8 +2826,6 @@
 		return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 	if (akm & WPA_KEY_MGMT_CCKM)
 		return RSN_AUTH_KEY_MGMT_CCKM;
-	if (akm & WPA_KEY_MGMT_OSEN)
-		return RSN_AUTH_KEY_MGMT_OSEN;
 	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
 		return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
 	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
@@ -3481,12 +3458,6 @@
 		return 0;
 	}
 
-	if (selector == OSEN_IE_VENDOR_TYPE) {
-		ie->osen = pos;
-		ie->osen_len = dlen;
-		return 0;
-	}
-
 	if (left >= PMKID_LEN && selector == RSN_KEY_DATA_PMKID) {
 		ie->pmkid = p;
 		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", pos, dlen);
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index e608d3c..d2c326c 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -94,7 +94,6 @@
 #define RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 25)
 
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
-#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
 #define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02)
 
 #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
@@ -268,8 +267,11 @@
 	size_t kck2_len;
 	size_t kek2_len;
 	size_t kdk_len;
+	size_t ptk_len;
 	size_t ltf_keyseed_len;
 	int installed; /* 1 if key has already been installed to driver */
+	bool installed_rx; /* whether TK has been installed as the next TK
+			    * for temporary RX-only use in the driver */
 };
 
 struct wpa_gtk {
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 2ea8ab3..90c6749 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -343,7 +343,6 @@
 /* parameters: <Venue Number> <Venue URL> */
 #define RX_VENUE_URL "RX-VENUE-URL "
 
-#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
 #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
 #define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
 
@@ -456,6 +455,11 @@
 /* Event triggered for received management frame */
 #define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED "
 
+/* Event triggerred on AP receiving Wi-Fi Alliance Generational Capabilities
+ * indication.
+ * Parameters: <STA addr> <Generational Capabilities Indication body> */
+#define WFA_GEN_CAPAB_RX "WFA-GEN-CAPAB "
+
 #ifndef BIT
 #define BIT(x) (1U << (x))
 #endif
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 2d8ff60..c84ccb4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -186,8 +186,32 @@
 #endif /* OpenSSL version < 1.1.1 */
 
 
+static void openssl_disable_fips(void)
+{
+#ifndef CONFIG_FIPS
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	static bool done = false;
+
+	if (done)
+		return;
+	done = true;
+
+	if (!EVP_default_properties_is_fips_enabled(NULL))
+		return; /* FIPS mode is not enabled */
+
+	if (!EVP_default_properties_enable_fips(NULL, 0))
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to disable FIPS mode");
+	else
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Disabled FIPS mode to enable non-FIPS-compliant algorithms and parameters");
+#endif /* OpenSSL version >= 3.0 */
+#endif /* !CONFIG_FIPS */
+}
+
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
 static OSSL_PROVIDER *openssl_legacy_provider = NULL;
+static OSSL_PROVIDER *openssl_default_provider = NULL;
 #endif /* OpenSSL version >= 3.0 */
 
 void openssl_load_legacy_provider(void)
@@ -212,6 +236,36 @@
 }
 
 
+static void openssl_load_default_provider_if_fips(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (openssl_default_provider)
+		return;
+
+	if (!OSSL_PROVIDER_available(NULL, "fips"))
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Load default provider to replace fips provider when needed");
+	openssl_default_provider = OSSL_PROVIDER_try_load(NULL, "default", 1);
+	if (!openssl_default_provider)
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Failed to load default provider");
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static void openssl_unload_default_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	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)
@@ -319,8 +373,16 @@
 
 #ifndef CONFIG_FIPS
 
+static void openssl_need_md5(void)
+{
+	openssl_disable_fips();
+	openssl_load_default_provider_if_fips();
+}
+
+
 int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
+	openssl_disable_fips();
 	openssl_load_legacy_provider();
 	return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
 }
@@ -404,6 +466,7 @@
 
 int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
+	openssl_need_md5();
 	return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
 }
 
@@ -1023,16 +1086,23 @@
 	struct wpabuf *pubkey = NULL, *privkey = NULL;
 	BIGNUM *priv_bn = NULL;
 	EVP_PKEY_CTX *gctx;
+	const char *propquery = NULL;
 
 	*priv = NULL;
 	wpabuf_free(*publ);
 	*publ = NULL;
 
+	if (OSSL_PROVIDER_available(NULL, "fips")) {
+		openssl_disable_fips();
+		openssl_load_default_provider_if_fips();
+		propquery = "provider!=fips";
+	}
+
 	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);
+	gctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", propquery);
 	if (!gctx ||
 	    EVP_PKEY_keygen_init(gctx) != 1 ||
 	    EVP_PKEY_CTX_set_params(gctx, params) != 1 ||
@@ -1371,6 +1441,9 @@
 	}
 
 	if (EVP_MAC_init(ctx->ctx, key, key_len, params) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: EVP_MAC_init(hmac,digest=%s) failed: %s",
+			   a, ERR_error_string(ERR_get_error(), NULL));
 		EVP_MAC_CTX_free(ctx->ctx);
 		bin_clear_free(ctx, sizeof(*ctx));
 		ctx = NULL;
@@ -1527,13 +1600,30 @@
 	EVP_MAC_CTX *ctx;
 	size_t i, mlen;
 	int res;
+	const char *property_query = NULL;
 
 	if (TEST_FAIL())
 		return -1;
 
-	hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
-	if (!hmac)
+#ifndef CONFIG_FIPS
+	if (os_strcmp(digest, "MD5") == 0) {
+		openssl_need_md5();
+		property_query = "provider!=fips";
+	} else if (key_len < 14 && OSSL_PROVIDER_available(NULL, "fips")) {
+		/* Need to use non-FIPS provider in OpenSSL to handle cases
+		 * where HMAC is used with salt that is less than 112 bits
+		 * instead of the HMAC uses with an actual key. */
+		openssl_disable_fips();
+		openssl_load_default_provider_if_fips();
+		property_query = "provider!=fips";
+	}
+#endif /* CONFIG_FIPS */
+	hmac = EVP_MAC_fetch(NULL, "HMAC", property_query);
+	if (!hmac) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_MAC_fetch(HMAC) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
 		return -1;
+	}
 
 	params[0] = OSSL_PARAM_construct_utf8_string("digest", digest, 0);
 	params[1] = OSSL_PARAM_construct_end();
@@ -1543,8 +1633,13 @@
 	if (!ctx)
 		return -1;
 
-	if (EVP_MAC_init(ctx, key, key_len, params) != 1)
+	if (EVP_MAC_init(ctx, key, key_len, params) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: EVP_MAC_init(hmac,digest=%s,key_len=%zu) failed: %s",
+			   digest, key_len,
+			   ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
+	}
 
 	for (i = 0; i < num_elem; i++) {
 		if (EVP_MAC_update(ctx, addr[i], len[i]) != 1)
@@ -1822,8 +1917,12 @@
 
 	if (!emac || !cipher ||
 	    !(ctx = EVP_MAC_CTX_new(emac)) ||
-	    EVP_MAC_init(ctx, key, key_len, params) != 1)
+	    EVP_MAC_init(ctx, key, key_len, params) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: EVP_MAC_init(cmac,cipher=%s) failed: %s",
+			   cipher, ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
+	}
 
 	for (i = 0; i < num_elem; i++) {
 		if (!EVP_MAC_update(ctx, addr[i], len[i]))
@@ -2650,8 +2749,12 @@
 		goto fail;
 
 	ecdh->pkey = EVP_EC_gen(name);
-	if (!ecdh->pkey)
+	if (!ecdh->pkey) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: EVP_EC_gen(group=%d) failed: %s",
+			   group, ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
+	}
 
 done:
 	return ecdh;
@@ -3416,8 +3519,8 @@
 	    EVP_PKEY_CTX_set_params(ctx, params) != 1 ||
 	    EVP_PKEY_generate(ctx, &pkey) != 1) {
 		wpa_printf(MSG_INFO,
-			   "OpenSSL: failed to generate EC keypair: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+			   "OpenSSL: Failed to generate EC keypair (group=%d): %s",
+			   group, ERR_error_string(ERR_get_error(), NULL));
 		pkey = NULL;
 	}
 
@@ -3680,6 +3783,8 @@
 	ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER",
 					    "type-specific", NULL);
 	if (!ctx || OSSL_ENCODER_to_data(ctx, &pdata, &pdata_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: OSSL_ENCODER failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
 		OSSL_ENCODER_CTX_free(ctx);
 		EVP_PKEY_free(copy);
 		return NULL;
@@ -4248,7 +4353,7 @@
 }
 
 
-struct crypto_csr * crypto_csr_init()
+struct crypto_csr * crypto_csr_init(void)
 {
 	return (struct crypto_csr *)X509_REQ_new();
 }
@@ -4793,8 +4898,12 @@
 	if (!hctx)
 		return -1;
 
-	if (EVP_MAC_init(hctx, salt, salt_len, params) != 1)
+	if (EVP_MAC_init(hctx, salt, salt_len, params) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: EVP_MAC_init(hmac,digest/HPKE) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
+	}
 
 	if (EVP_MAC_update(hctx, (const unsigned char *) "HPKE-v1", 7) != 1 ||
 	    EVP_MAC_update(hctx, suite_id, suite_id_len) != 1 ||
@@ -4902,8 +5011,12 @@
 		if (!hctx)
 			goto fail;
 
-		if (EVP_MAC_init(hctx, prk, mdlen, params) != 1)
+		if (EVP_MAC_init(hctx, prk, mdlen, params) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: EVP_MAC_init(hmac,digest/HPKE) failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
 			goto fail;
+		}
 
 		if (iter > 0 && EVP_MAC_update(hctx, hash, mdlen) != 1)
 			goto fail;
@@ -5581,4 +5694,5 @@
 void crypto_unload(void)
 {
 	openssl_unload_legacy_provider();
+	openssl_unload_default_provider();
 }
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index 2691743..7a91202 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -18,6 +18,7 @@
 #include <wolfssl/openssl/bn.h>
 #include <wolfssl/wolfcrypt/aes.h>
 #include <wolfssl/wolfcrypt/arc4.h>
+#include <wolfssl/wolfcrypt/asn.h>
 #include <wolfssl/wolfcrypt/asn_public.h>
 #include <wolfssl/wolfcrypt/cmac.h>
 #include <wolfssl/wolfcrypt/des3.h>
@@ -29,6 +30,7 @@
 #include <wolfssl/wolfcrypt/md5.h>
 #include <wolfssl/wolfcrypt/pkcs7.h>
 #include <wolfssl/wolfcrypt/pwdbased.h>
+#include <wolfssl/wolfcrypt/rsa.h>
 #include <wolfssl/wolfcrypt/sha.h>
 #include <wolfssl/wolfcrypt/sha256.h>
 #include <wolfssl/wolfcrypt/sha512.h>
@@ -514,8 +516,10 @@
 {
 	int ret;
 
+	PRIVATE_KEY_UNLOCK();
 	ret = wc_PBKDF2(buf, (const byte *) passphrase, os_strlen(passphrase),
 			ssid, ssid_len, iterations, buflen, WC_SHA);
+	PRIVATE_KEY_LOCK();
 	if (ret != 0) {
 		if (ret == HMAC_MIN_KEYLEN_E) {
 			LOG_WOLF_ERROR_VA("wolfSSL: Password is too short. Make sure your password is at least %d characters long. This is a requirement for FIPS builds.",
@@ -3412,7 +3416,7 @@
 {
 	if (!csr || !len || !type) {
 		LOG_INVALID_PARAMETERS();
-		return NULL;;
+		return NULL;
 	}
 
 	switch (attr) {
@@ -3555,6 +3559,284 @@
 #endif /* CONFIG_DPP */
 
 
+struct crypto_rsa_key {
+	RsaKey key;
+	WC_RNG *rng;
+};
+
+static struct crypto_rsa_key * crypto_rsa_key_init(void)
+{
+	struct crypto_rsa_key *ret;
+	int err;
+
+	ret = os_zalloc(sizeof(*ret));
+	if (!ret)
+		return NULL;
+
+	err = wc_InitRsaKey(&ret->key, NULL);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_InitRsaKey, err);
+		goto fail;
+	}
+
+	ret->rng = wc_rng_init();
+	if (!ret->rng) {
+		LOG_WOLF_ERROR_FUNC_NULL(wc_rng_init);
+		goto fail;
+	}
+
+	err = wc_RsaSetRNG(&ret->key, ret->rng);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_RsaSetRNG, err);
+		goto fail;
+	}
+
+	return ret;
+fail:
+	crypto_rsa_key_free(ret);
+	return NULL;
+}
+
+
+void crypto_rsa_key_free(struct crypto_rsa_key *key)
+{
+	if (key) {
+		int err;
+
+		err = wc_FreeRsaKey(&key->key);
+		if (err != 0)
+			LOG_WOLF_ERROR_FUNC(wc_FreeRsaKey, err);
+		wc_rng_free(key->rng);
+		os_free(key);
+	}
+}
+
+
+static void read_rsa_key_from_x509(unsigned char *key_pem, size_t key_pem_len,
+				   DerBuffer **key_der)
+{
+	struct DecodedCert cert;
+	DerBuffer *cert_der = NULL;
+	word32 der_key_sz = 0;
+	int err;
+
+	err = wc_PemToDer(key_pem, (long) key_pem_len, CERT_TYPE, &cert_der,
+			NULL, NULL, NULL);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_PemToDer, err);
+		goto fail;
+	}
+
+	wc_InitDecodedCert(&cert, cert_der->buffer, cert_der->length, NULL);
+	err = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_PemToDer, err);
+		goto fail;
+	}
+
+	err = wc_GetPubKeyDerFromCert(&cert, NULL, &der_key_sz);
+	if (err != LENGTH_ONLY_E) {
+		LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err);
+		goto fail;
+	}
+
+	if (*key_der)
+		wc_FreeDer(key_der);
+	*key_der = NULL;
+
+	err = wc_AllocDer(key_der, der_key_sz, PUBLICKEY_TYPE, NULL);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_AllocDer, err);
+		goto fail;
+	}
+
+	err = wc_GetPubKeyDerFromCert(&cert, (*key_der)->buffer,
+				      &(*key_der)->length);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err);
+		goto fail;
+	}
+
+fail:
+	if (cert_der) {
+		wc_FreeDecodedCert(&cert);
+		wc_FreeDer(&cert_der);
+	}
+
+	/* caller is responsible for free'ing key_der */
+}
+
+
+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
+{
+	struct crypto_rsa_key *ret = NULL;
+	unsigned char *key_pem = NULL;
+	size_t key_pem_len = 0;
+	DerBuffer *key_der = NULL;
+	int key_format = 0;
+	int err;
+	int success = 0;
+	word32 idx = 0;
+
+	key_pem = (unsigned char *) os_readfile(file, &key_pem_len);
+	if (!key_pem) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_readfile);
+		goto fail;
+	}
+
+	err = wc_PemToDer(key_pem, (long) key_pem_len,
+			  private_key ? PRIVATEKEY_TYPE : PUBLICKEY_TYPE,
+			  &key_der, NULL, NULL, &key_format);
+	if (err != 0) {
+		if (private_key) {
+			LOG_WOLF_ERROR_FUNC(wc_PemToDer, err);
+			goto fail;
+		}
+
+		/* Input file might be public key or x509 cert we want to
+		 *extract the key from */
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Trying to extract key from x509 cert");
+		read_rsa_key_from_x509(key_pem, key_pem_len, &key_der);
+		if (!key_der) {
+			LOG_WOLF_ERROR_FUNC(wc_PemToDer, err);
+			LOG_WOLF_ERROR_FUNC_NULL(read_rsa_key_from_x509);
+			goto fail;
+		}
+	}
+
+	if (private_key && key_format != RSAk) {
+		LOG_WOLF_ERROR("Private key is not RSA key");
+		goto fail;
+	}
+
+	/* No longer needed so let's free the memory early */
+	os_free(key_pem);
+	key_pem = NULL;
+
+	ret = crypto_rsa_key_init();
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_rsa_key_init);
+		goto fail;
+	}
+
+	if (private_key)
+		err = wc_RsaPrivateKeyDecode(key_der->buffer, &idx, &ret->key,
+					     key_der->length);
+	else
+		err = wc_RsaPublicKeyDecode(key_der->buffer, &idx, &ret->key,
+					    key_der->length);
+
+	if (err != 0) {
+		if (private_key)
+			LOG_WOLF_ERROR_FUNC(wc_RsaPrivateKeyDecode, err);
+		else
+			LOG_WOLF_ERROR_FUNC(wc_RsaPublicKeyDecode, err);
+		goto fail;
+	}
+
+	success = 1;
+fail:
+	os_free(key_pem);
+	if (key_der)
+		wc_FreeDer(&key_der);
+	if (!success && ret) {
+		crypto_rsa_key_free(ret);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+
+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+	int err;
+	int success = 0;
+	struct wpabuf *ret = NULL;
+
+	if (!key || !in) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	ret = wpabuf_alloc(wc_RsaEncryptSize(&key->key));
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: crypto_rsa_oaep_sha256_encrypt: wpabuf_len(in) %ld  wc_RsaEncryptSize(key->key) %d",
+		   wpabuf_len(in), wc_RsaEncryptSize(&key->key));
+
+	err = wc_RsaPublicEncrypt_ex(wpabuf_head_u8(in), wpabuf_len(in),
+				     wpabuf_mhead_u8(ret), wpabuf_size(ret),
+				     &key->key, key->rng,
+				     WC_RSA_OAEP_PAD, WC_HASH_TYPE_SHA256,
+				     WC_MGF1SHA256, NULL, 0);
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_RsaPublicEncrypt_ex, err);
+		goto fail;
+	}
+	wpabuf_put(ret, err);
+
+	success = 1;
+fail:
+	if (!success && ret) {
+		wpabuf_free(ret);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+
+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+	int err;
+	int success = 0;
+	struct wpabuf *ret = NULL;
+
+	if (!key || !in) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	ret = wpabuf_alloc(wc_RsaEncryptSize(&key->key));
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: crypto_rsa_oaep_sha256_decrypt: wpabuf_len(in) %ld wc_RsaEncryptSize(key->key) %d",
+		   wpabuf_len(in), wc_RsaEncryptSize(&key->key));
+
+	err = wc_RsaPrivateDecrypt_ex(wpabuf_head_u8(in), wpabuf_len(in),
+				      wpabuf_mhead_u8(ret), wpabuf_size(ret),
+				      &key->key, WC_RSA_OAEP_PAD,
+				      WC_HASH_TYPE_SHA256, WC_MGF1SHA256,
+				      NULL, 0);
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_RsaPublicEncrypt_ex, err);
+		goto fail;
+	}
+	wpabuf_put(ret, err);
+
+	success = 1;
+fail:
+	if (!success && ret) {
+		wpabuf_free(ret);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+
 void crypto_unload(void)
 {
 }
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index d665a99..de7394a 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -97,7 +97,7 @@
 	 * Mask out unused bits in the last octet if it does not use all the
 	 * bits.
 	 */
-	if (buf_len_bits % 8) {
+	if (pos > 0 && (buf_len_bits % 8)) {
 		u8 mask = 0xff << (8 - buf_len_bits % 8);
 		buf[pos - 1] &= mask;
 	}
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
index 420e78c..fdf3316 100644
--- a/src/crypto/sha384-prf.c
+++ b/src/crypto/sha384-prf.c
@@ -97,7 +97,7 @@
 	 * Mask out unused bits in the last octet if it does not use all the
 	 * bits.
 	 */
-	if (buf_len_bits % 8) {
+	if (pos > 0 && (buf_len_bits % 8)) {
 		u8 mask = 0xff << (8 - buf_len_bits % 8);
 		buf[pos - 1] &= mask;
 	}
diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c
index e48cf5f..be45814 100644
--- a/src/crypto/sha512-prf.c
+++ b/src/crypto/sha512-prf.c
@@ -97,7 +97,7 @@
 	 * Mask out unused bits in the last octet if it does not use all the
 	 * bits.
 	 */
-	if (buf_len_bits % 8) {
+	if (pos > 0 && (buf_len_bits % 8)) {
 		u8 mask = 0xff << (8 - buf_len_bits % 8);
 		buf[pos - 1] &= mask;
 	}
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index e3f5b5a..8ce9390 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -62,6 +62,8 @@
 	char *suffix_match;
 	char *domain_match;
 	unsigned int flags;
+
+	char *prio_str;
 };
 
 
@@ -213,7 +215,9 @@
 	if (ret < 0)
 		goto fail;
 
-	ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+	ret = gnutls_priority_set_direct(conn->session,
+					 conn->prio_str ? conn->prio_str :
+					 "NORMAL:-VERS-SSL3.0",
 					 &err);
 	if (ret < 0) {
 		wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
@@ -285,6 +289,7 @@
 	wpabuf_free(conn->pull_buf);
 	os_free(conn->suffix_match);
 	os_free(conn->domain_match);
+	os_free(conn->prio_str);
 	os_free(conn);
 }
 
@@ -410,15 +415,18 @@
 
 	if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 |
 			     TLS_CONN_DISABLE_TLSv1_1 |
-			     TLS_CONN_DISABLE_TLSv1_2)) {
+			     TLS_CONN_DISABLE_TLSv1_2 |
+			     TLS_CONN_DISABLE_TLSv1_3)) {
 		os_snprintf(prio_buf, sizeof(prio_buf),
-			    "NORMAL:-VERS-SSL3.0%s%s%s",
+			    "NORMAL:-VERS-SSL3.0%s%s%s%s",
 			    params->flags & TLS_CONN_DISABLE_TLSv1_0 ?
 			    ":-VERS-TLS1.0" : "",
 			    params->flags & TLS_CONN_DISABLE_TLSv1_1 ?
 			    ":-VERS-TLS1.1" : "",
 			    params->flags & TLS_CONN_DISABLE_TLSv1_2 ?
-			    ":-VERS-TLS1.2" : "");
+			    ":-VERS-TLS1.2" : "",
+			    params->flags & TLS_CONN_DISABLE_TLSv1_3 ?
+			    ":-VERS-TLS1.3" : "");
 		prio = prio_buf;
 	}
 
@@ -459,6 +467,8 @@
 				   err);
 			return -1;
 		}
+		os_free(conn->prio_str);
+		conn->prio_str = os_strdup(prio);
 	}
 
 	if (params->openssl_ecdh_curves) {
@@ -1513,7 +1523,7 @@
 				conn->global->event_cb(conn->global->cb_ctx,
 						       TLS_ALERT, &ev);
 			}
-			/* continue */
+			/* fallthrough */
 		default:
 			wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
 				   "-> %s", __func__, gnutls_strerror(ret));
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index d849933..1eb3b91 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -33,6 +33,8 @@
 #include <openssl/core_names.h>
 #include <openssl/decoder.h>
 #include <openssl/param_build.h>
+#include <openssl/store.h>
+#include <openssl/provider.h>
 #else /* OpenSSL version >= 3.0 */
 #ifndef OPENSSL_NO_DSA
 #include <openssl/dsa.h>
@@ -165,8 +167,8 @@
 	BIO *ssl_in, *ssl_out;
 #if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
 	ENGINE *engine;        /* functional reference to the engine */
-	EVP_PKEY *private_key; /* the private key if using engine */
 #endif /* OPENSSL_NO_ENGINE */
+	EVP_PKEY *private_key; /* the private key if using engine/provider */
 	char *subject_match, *altsubject_match, *suffix_match, *domain_match;
 	char *check_cert_subject;
 	int read_alerts, write_alerts, failed;
@@ -394,6 +396,151 @@
 }
 
 
+#ifndef ANDROID
+#ifdef OPENSSL_NO_ENGINE
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static OSSL_PROVIDER *openssl_pkcs11_provider = NULL;
+#endif /* OpenSSL version >= 3.0 */
+
+static void openssl_load_pkcs11_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (openssl_pkcs11_provider)
+		return;
+
+	openssl_pkcs11_provider = OSSL_PROVIDER_try_load(NULL, "pkcs11", 1);
+	if (!openssl_pkcs11_provider)
+		wpa_printf(MSG_WARNING, "PKCS11 provider not present");
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static void openssl_unload_pkcs11_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (openssl_pkcs11_provider) {
+		OSSL_PROVIDER_unload(openssl_pkcs11_provider);
+		openssl_pkcs11_provider = NULL;
+	}
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static bool openssl_can_use_provider(const char *engine_id, const char *req)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (!os_strcmp(engine_id, "pkcs11") && openssl_pkcs11_provider)
+		return true;
+
+	wpa_printf(MSG_ERROR,
+		   "Cannot find OpenSSL provider for '%s' (missing '%s')",
+		   req, engine_id);
+#endif /* OpenSSL version >= 3.0 */
+	return false;
+}
+
+
+static EVP_PKEY * provider_load_key(const char *uri)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	OSSL_STORE_CTX *store;
+	OSSL_STORE_INFO *info;
+	EVP_PKEY *key = NULL;
+
+	if (!uri) {
+		tls_show_errors(MSG_ERROR, __func__,
+				"Invalid NULL uri for key");
+		goto err_key;
+	}
+
+	store = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL);
+	if (!store) {
+		wpa_printf(MSG_DEBUG, "Bad uri for private key:%s", uri);
+
+		tls_show_errors(MSG_ERROR, __func__,
+				"Failed to open key store");
+		goto err_key;
+	}
+
+	if (os_strncmp(uri, "pkcs11:", 7) &&
+	    os_strstr(uri, "type=private") == NULL) {
+		/* This is a workaround for OpenSSL < 3.2.0 where the code fails
+		 * to correctly source public keys unless explicitly requested
+		 * via an expect hint. */
+		if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PUBKEY) != 1) {
+			tls_show_errors(MSG_ERROR, __func__,
+					"Failed to expect Public Key File");
+			goto err_store;
+		}
+	}
+
+	while (!OSSL_STORE_eof(store)) {
+		info = OSSL_STORE_load(store);
+		if ((OSSL_STORE_INFO_get_type(info)) == OSSL_STORE_INFO_PKEY)
+			key = OSSL_STORE_INFO_get1_PKEY(info);
+
+		OSSL_STORE_INFO_free(info);
+		if (key)
+			break;
+	}
+
+err_store:
+	OSSL_STORE_close(store);
+err_key:
+	if (!key)
+		wpa_printf(MSG_ERROR, "OpenSSL: Failed to load key from URI");
+
+	return key;
+#else /* OpenSSL version >= 3.0 */
+	return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static X509 * provider_load_cert(const char *cert_id)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	OSSL_STORE_CTX *store;
+	OSSL_STORE_INFO *info;
+	X509 *cert = NULL;
+
+	if (!cert_id) {
+		tls_show_errors(MSG_ERROR, __func__, "Invalid NULL uri");
+		goto err_cert;
+	}
+
+	store = OSSL_STORE_open(cert_id, NULL, NULL, NULL, NULL);
+	if (!store) {
+		tls_show_errors(MSG_ERROR, __func__, "Failed to open store");
+		goto err_cert;
+	}
+
+	while (!OSSL_STORE_eof(store)) {
+		info = OSSL_STORE_load(store);
+		if ((OSSL_STORE_INFO_get_type(info)) == OSSL_STORE_INFO_CERT)
+			cert = OSSL_STORE_INFO_get1_CERT(info);
+
+		OSSL_STORE_INFO_free(info);
+		if (cert)
+			break;
+	}
+	OSSL_STORE_close(store);
+
+err_cert:
+	if (!cert)
+		tls_show_errors(MSG_ERROR, __func__,
+				"Failed to load cert from URI");
+	return cert;
+#else /* OpenSSL version >= 3.0 */
+	return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+#endif /* OPENSSL_NO_ENGINE */
+#endif /* !ANDROID */
+
+
 #ifdef CONFIG_NATIVE_WINDOWS
 
 /* Windows CryptoAPI and access to certificate stores */
@@ -1057,6 +1204,9 @@
 		void openssl_load_legacy_provider(void);
 
 		openssl_load_legacy_provider();
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+		openssl_load_pkcs11_provider();
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
 
 		tls_global = context = tls_context_new(conf);
 		if (context == NULL)
@@ -1125,6 +1275,7 @@
 	else
 		ssl = NULL;
 	if (ssl == NULL) {
+		tls_show_errors(MSG_INFO, "SSL_CTX_new", "init");
 		tls_openssl_ref_count--;
 		if (context != tls_global)
 			os_free(context);
@@ -1248,6 +1399,9 @@
 
 	tls_openssl_ref_count--;
 	if (tls_openssl_ref_count == 0) {
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+		openssl_unload_pkcs11_provider();
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
 #ifndef OPENSSL_NO_ENGINE
 		ENGINE_cleanup();
@@ -1408,6 +1562,11 @@
 
 	return ret;
 #else /* OPENSSL_NO_ENGINE */
+#ifndef ANDROID
+	conn->private_key = provider_load_key(key_id);
+	if (!conn->private_key)
+		return -1;
+#endif /* !ANDROID */
 	return 0;
 #endif /* OPENSSL_NO_ENGINE */
 }
@@ -1415,12 +1574,12 @@
 
 static void tls_engine_deinit(struct tls_connection *conn)
 {
-#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
-	wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
 	if (conn->private_key) {
 		EVP_PKEY_free(conn->private_key);
 		conn->private_key = NULL;
 	}
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+	wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
 	if (conn->engine) {
 #if !defined(OPENSSL_IS_BORINGSSL)
 		ENGINE_finish(conn->engine);
@@ -1940,6 +2099,8 @@
 			len = os_strlen(pos);
 		if (tls_match_altsubject_component(cert, type, pos, len) > 0)
 			return 1;
+		if (!end)
+			break;
 		pos = end + 1;
 	} while (end);
 
@@ -3853,11 +4014,17 @@
 static int tls_connection_engine_client_cert(struct tls_connection *conn,
 					     const char *cert_id)
 {
-#ifndef OPENSSL_NO_ENGINE
+#ifndef ANDROID
 	X509 *cert;
 
+#ifndef OPENSSL_NO_ENGINE
 	if (tls_engine_get_cert(conn, cert_id, &cert))
 		return -1;
+#else /* OPENSSL_NO_ENGINE */
+	cert = provider_load_cert(cert_id);
+	if (!cert)
+		return -1;
+#endif /* OPENSSL_NO_ENGINE */
 
 	if (!SSL_use_certificate(conn->ssl, cert)) {
 		tls_show_errors(MSG_ERROR, __func__,
@@ -3866,13 +4033,12 @@
 		return -1;
 	}
 	X509_free(cert);
-	wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
+	wpa_printf(MSG_DEBUG, "ENGINE/provider: SSL_use_certificate --> "
 		   "OK");
 	return 0;
-
-#else /* OPENSSL_NO_ENGINE */
+#else /* ANDROID */
 	return -1;
-#endif /* OPENSSL_NO_ENGINE */
+#endif /* ANDROID */
 }
 
 
@@ -3880,13 +4046,19 @@
 					 struct tls_connection *conn,
 					 const char *ca_cert_id)
 {
-#ifndef OPENSSL_NO_ENGINE
+#ifndef ANDROID
 	X509 *cert;
 	SSL_CTX *ssl_ctx = data->ssl;
 	X509_STORE *store;
 
+#ifndef OPENSSL_NO_ENGINE
 	if (tls_engine_get_cert(conn, ca_cert_id, &cert))
 		return -1;
+#else /* OPENSSL_NO_ENGINE */
+	cert = provider_load_cert(ca_cert_id);
+	if (!cert)
+		return -1;
+#endif /* OPENSSL_NO_ENGINE */
 
 	/* start off the same as tls_connection_ca_cert */
 	store = X509_STORE_new();
@@ -3900,7 +4072,7 @@
 	if (!X509_STORE_add_cert(store, cert)) {
 		unsigned long err = ERR_peek_error();
 		tls_show_errors(MSG_WARNING, __func__,
-				"Failed to add CA certificate from engine "
+				"Failed to add CA certificate from engine/provider "
 				"to certificate store");
 		if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
 		    ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
@@ -3913,25 +4085,24 @@
 		}
 	}
 	X509_free(cert);
-	wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
-		   "to certificate store", __func__);
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: %s - added CA certificate from engine/provider to certificate store",
+		   __func__);
 	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 	conn->ca_cert_verify = 1;
 
 	return 0;
-
-#else /* OPENSSL_NO_ENGINE */
+#else /* ANDROID */
 	return -1;
-#endif /* OPENSSL_NO_ENGINE */
+#endif /* ANDROID */
 }
 
 
 static int tls_connection_engine_private_key(struct tls_connection *conn)
 {
-#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
 	if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
 		tls_show_errors(MSG_ERROR, __func__,
-				"ENGINE: cannot use private key for TLS");
+				"ENGINE/provider: cannot use private key for TLS");
 		return -1;
 	}
 	if (!SSL_check_private_key(conn->ssl)) {
@@ -3940,11 +4111,6 @@
 		return -1;
 	}
 	return 0;
-#else /* OPENSSL_NO_ENGINE */
-	wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
-		   "engine support was not compiled in");
-	return -1;
-#endif /* OPENSSL_NO_ENGINE */
 }
 
 
@@ -5495,6 +5661,10 @@
 	}
 
 	if (engine_id && ca_cert_id) {
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+		if (!openssl_can_use_provider(engine_id, ca_cert_id))
+			return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
 		if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
 	} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
@@ -5506,6 +5676,10 @@
 	}
 
 	if (engine_id && cert_id) {
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+		if (!openssl_can_use_provider(engine_id, cert_id))
+			return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
 		if (tls_connection_engine_client_cert(conn, cert_id))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
 	} else if (tls_connection_client_cert(conn, params->client_cert,
@@ -5516,7 +5690,12 @@
 	}
 
 	if (engine_id && key_id) {
-		wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+		if (!openssl_can_use_provider(engine_id, key_id))
+			return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
+		wpa_printf(MSG_DEBUG,
+			   "TLS: Using private key from engine/provider");
 		if (tls_connection_engine_private_key(conn))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
 	} else if (tls_connection_private_key(data, conn,
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index 0b2947d..3bf52d6 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -42,7 +42,9 @@
 
 static int tls_ref_count = 0;
 
-static int tls_ex_idx_session = 0;
+#define TLS_SESSION_EX_IDX (0)
+#define TLS_SSL_CTX_CTX_EX_IDX (0)
+#define TLS_SSL_CON_EX_IDX (0)
 
 
 /* tls input data for wolfSSL Read Callback */
@@ -63,13 +65,15 @@
 	int cert_in_cb;
 	char *ocsp_stapling_response;
 	unsigned int tls_session_lifetime;
+	/* This is alloc'ed and needs to be free'd */
+	char *check_cert_subject;
 };
 
 static struct tls_context *tls_global = NULL;
 
 /* wolfssl tls_connection */
 struct tls_connection {
-	struct tls_context *context;
+	const struct tls_context *context;
 	WOLFSSL *ssl;
 	int read_alerts;
 	int write_alerts;
@@ -80,6 +84,7 @@
 	char *alt_subject_match;
 	char *suffix_match;
 	char *domain_match;
+	char *check_cert_subject;
 
 	u8 srv_cert_hash[32];
 
@@ -120,6 +125,22 @@
 }
 
 
+static void tls_context_free(struct tls_context *context)
+{
+	if (context) {
+		os_free(context->check_cert_subject);
+		os_free(context);
+	}
+}
+
+
+/* Helper to make sure the context stays const */
+static const struct tls_context * ssl_ctx_get_tls_context(void *ssl_ctx)
+{
+	return wolfSSL_CTX_get_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX);
+}
+
+
 static void wolfssl_reset_in_data(struct tls_in_data *in,
 				  const struct wpabuf *buf)
 {
@@ -184,7 +205,7 @@
 {
 	struct wpabuf *buf;
 
-	buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	buf = wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX);
 	if (!buf)
 		return;
 	wpa_printf(MSG_DEBUG,
@@ -192,7 +213,7 @@
 		   buf, sess);
 	wpabuf_free(buf);
 
-	wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+	wolfSSL_SESSION_set_ex_data(sess, TLS_SESSION_EX_IDX, NULL);
 }
 
 
@@ -223,11 +244,158 @@
 #endif /* DEBUG_WOLFSSL */
 
 
+#define SUITEB_OLDTLS_192_CIPHERS "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"
+#define SUITEB_TLS13_192_CIPHERS "TLS13-AES256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256"
+#define SUITEB_TLS_192_CIPHERS SUITEB_TLS13_192_CIPHERS ":" SUITEB_OLDTLS_192_CIPHERS
+
+#define SUITEB_OLDTLS_128_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:" SUITEB_OLDTLS_192_CIPHERS
+#define SUITEB_TLS13_128_CIPHERS "TLS13-AES128-GCM-SHA256:" SUITEB_TLS13_192_CIPHERS
+#define SUITEB_TLS_128_CIPHERS SUITEB_TLS13_128_CIPHERS ":" SUITEB_OLDTLS_128_CIPHERS
+
+#define SUITEB_TLS_192_SIGALGS "ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384"
+#define SUITEB_TLS_128_SIGALGS "ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:" SUITEB_TLS_192_SIGALGS
+
+#define SUITEB_TLS_192_CURVES "P-384:P-521"
+#define SUITEB_TLS_128_CURVES "P-256:" SUITEB_TLS_192_CURVES
+
+#define SUITEB_TLS_128_RSA_KEY_SZ 2048
+#define SUITEB_TLS_192_RSA_KEY_SZ 3072
+
+#define SUITEB_TLS_128_ECC_KEY_SZ 256
+#define SUITEB_TLS_192_ECC_KEY_SZ 384
+
+static int handle_ciphersuites(WOLFSSL_CTX *ssl_ctx, WOLFSSL *ssl,
+			       const char *openssl_ciphers, unsigned int flags)
+{
+	const char *ciphers = "DEFAULT:!aNULL";
+	const char *sigalgs = NULL;
+	const char *curves = NULL;
+	bool tls13 = !(flags & TLS_CONN_DISABLE_TLSv1_3);
+	unsigned int tls13_only_mask = TLS_CONN_DISABLE_TLSv1_2 |
+		TLS_CONN_DISABLE_TLSv1_1 | TLS_CONN_DISABLE_TLSv1_0;
+	bool old_tls_only = ((flags & tls13_only_mask) != tls13_only_mask) &&
+		!tls13;
+	bool tls13only = ((flags & tls13_only_mask) == tls13_only_mask) &&
+		!(flags & TLS_CONN_DISABLE_TLSv1_3);
+	short key_sz = 0;
+	short ecc_key_sz = 0;
+
+	if (openssl_ciphers) {
+		if (os_strcmp(openssl_ciphers, "SUITEB128") == 0) {
+			if (tls13only)
+				ciphers = SUITEB_TLS13_128_CIPHERS;
+			else if (old_tls_only)
+				ciphers = SUITEB_OLDTLS_128_CIPHERS;
+			else
+				ciphers = SUITEB_TLS_128_CIPHERS;
+			sigalgs = SUITEB_TLS_128_SIGALGS;
+			key_sz = SUITEB_TLS_128_RSA_KEY_SZ;
+			ecc_key_sz = SUITEB_TLS_128_ECC_KEY_SZ;
+			curves = SUITEB_TLS_128_CURVES;
+		} else if (os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
+			if (tls13only)
+				ciphers = SUITEB_TLS13_192_CIPHERS;
+			else if (old_tls_only)
+				ciphers = SUITEB_OLDTLS_192_CIPHERS;
+			else
+				ciphers = SUITEB_TLS_192_CIPHERS;
+			sigalgs = SUITEB_TLS_192_SIGALGS;
+			key_sz = SUITEB_TLS_192_RSA_KEY_SZ;
+			ecc_key_sz = SUITEB_TLS_192_ECC_KEY_SZ;
+			curves = SUITEB_TLS_192_CURVES;
+		} else {
+			ciphers = openssl_ciphers;
+		}
+	} else if (flags & TLS_CONN_SUITEB) {
+		if (tls13only)
+			ciphers = SUITEB_TLS13_192_CIPHERS;
+		else if (old_tls_only)
+			ciphers = SUITEB_OLDTLS_192_CIPHERS;
+		else
+			ciphers = SUITEB_TLS_192_CIPHERS;
+		sigalgs = SUITEB_TLS_192_SIGALGS;
+		key_sz = SUITEB_TLS_192_RSA_KEY_SZ;
+		ecc_key_sz = SUITEB_TLS_192_ECC_KEY_SZ;
+		curves = SUITEB_TLS_192_CURVES;
+	}
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites for %s",
+		   ssl_ctx ? "ctx" : "ssl");
+	wpa_printf(MSG_DEBUG, "wolfSSL: openssl_ciphers: %s",
+		   openssl_ciphers ? openssl_ciphers : "N/A");
+	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s",
+		   ciphers ? ciphers : "N/A");
+	wpa_printf(MSG_DEBUG, "wolfSSL: sigalgs: %s",
+		   sigalgs ? sigalgs : "N/A");
+	wpa_printf(MSG_DEBUG, "wolfSSL: key size: %d", key_sz);
+
+	if (ciphers) {
+		if ((ssl_ctx &&
+		     wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) ||
+		    (ssl && wolfSSL_set_cipher_list(ssl, ciphers) != 1)) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: Failed to set cipher string '%s'",
+				   ciphers);
+			return -1;
+		}
+	}
+
+	if (sigalgs) {
+		if ((ssl_ctx &&
+		     wolfSSL_CTX_set1_sigalgs_list(ssl_ctx, sigalgs) != 1) ||
+		    (ssl && wolfSSL_set1_sigalgs_list(ssl, sigalgs) != 1)) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: Failed to set sigalgs '%s'",
+				   sigalgs);
+			return -1;
+		}
+	}
+
+	if (key_sz) {
+		if ((ssl_ctx &&
+		     wolfSSL_CTX_SetMinRsaKey_Sz(ssl_ctx, key_sz) != 1) ||
+		    (ssl && wolfSSL_SetMinRsaKey_Sz(ssl, key_sz) != 1) ||
+		    (ssl_ctx &&
+		     wolfSSL_CTX_SetMinDhKey_Sz(ssl_ctx, key_sz) != 1) ||
+		    (ssl && wolfSSL_SetMinDhKey_Sz(ssl, key_sz) != 1)) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: Failed to set min key size");
+			return -1;
+		}
+	}
+
+	if (ecc_key_sz) {
+		if ((ssl_ctx &&
+		     wolfSSL_CTX_SetMinEccKey_Sz(ssl_ctx, ecc_key_sz) != 1) ||
+		    (ssl && wolfSSL_SetMinEccKey_Sz(ssl, ecc_key_sz) != 1) ||
+		    (ssl_ctx &&
+		     wolfSSL_CTX_SetTmpEC_DHE_Sz(ssl_ctx,
+						 ecc_key_sz / 8) != 1) ||
+		    (ssl &&
+		     wolfSSL_SetTmpEC_DHE_Sz(ssl, ecc_key_sz / 8) != 1)) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: Failed to set min ecc key size");
+			return -1;
+		}
+	}
+
+	if (curves) {
+		if ((ssl_ctx &&
+		     wolfSSL_CTX_set1_curves_list(ssl_ctx, curves) != 1) ||
+		    (ssl && wolfSSL_set1_curves_list(ssl, curves) != 1)) {
+			wpa_printf(MSG_ERROR, "wolfSSL: Failed to set curves");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
 void * tls_init(const struct tls_config *conf)
 {
 	WOLFSSL_CTX *ssl_ctx;
 	struct tls_context *context;
-	const char *ciphers;
 
 #ifdef DEBUG_WOLFSSL
 	wolfSSL_SetLoggingCb(wolfSSL_logging_cb);
@@ -255,16 +423,17 @@
 	if (!ssl_ctx) {
 		tls_ref_count--;
 		if (context != tls_global)
-			os_free(context);
+			tls_context_free(context);
 		if (tls_ref_count == 0) {
-			os_free(tls_global);
+			tls_context_free(tls_global);
 			tls_global = NULL;
 		}
+		return NULL;
 	}
 	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);
+	wolfSSL_CTX_set_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX, context);
 
 	if (conf->tls_session_lifetime > 0) {
 		wolfSSL_CTX_set_session_id_context(ssl_ctx,
@@ -280,36 +449,33 @@
 						   WOLFSSL_SESS_CACHE_OFF);
 	}
 
-	if (conf && conf->openssl_ciphers)
-		ciphers = conf->openssl_ciphers;
-	else
-		ciphers = "ALL";
-	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", ciphers);
-	if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "wolfSSL: Failed to set cipher string '%s'",
-			   ciphers);
+	if (handle_ciphersuites(ssl_ctx, NULL, conf->openssl_ciphers,
+				conf ? conf->tls_flags : 0) != 0) {
+		wpa_printf(MSG_INFO, "wolfssl: Error setting ciphersuites");
 		tls_deinit(ssl_ctx);
 		return NULL;
 	}
 
+
 	return ssl_ctx;
 }
 
 
 void tls_deinit(void *ssl_ctx)
 {
-	struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+	struct tls_context *context;
 
+	/* Need to cast the const away to be able to free this */
+	context = (struct tls_context *) ssl_ctx_get_tls_context(ssl_ctx);
 	if (context != tls_global)
-		os_free(context);
+		tls_context_free(context);
 
 	wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx);
 
 	tls_ref_count--;
 	if (tls_ref_count == 0) {
 		wolfSSL_Cleanup();
-		os_free(tls_global);
+		tls_context_free(tls_global);
 		tls_global = NULL;
 	}
 }
@@ -351,8 +517,8 @@
 
 	wolfSSL_SetIOReadCtx(conn->ssl,  &conn->input);
 	wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output);
-	wolfSSL_set_ex_data(conn->ssl, 0, conn);
-	conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+	wolfSSL_set_ex_data(conn->ssl, TLS_SSL_CON_EX_IDX, conn);
+	conn->context = ssl_ctx_get_tls_context(ssl_ctx);
 
 	/* Need randoms post-hanshake for EAP-FAST, export key and deriving
 	 * session ID in EAP methods. */
@@ -378,6 +544,7 @@
 	os_free(conn->suffix_match);
 	os_free(conn->domain_match);
 	os_free(conn->peer_subject);
+	os_free(conn->check_cert_subject);
 
 	/* self */
 	os_free(conn);
@@ -427,7 +594,8 @@
 					    const char *subject_match,
 					    const char *alt_subject_match,
 					    const char *suffix_match,
-					    const char *domain_match)
+					    const char *domain_match,
+					    const char *check_cert_subject)
 {
 	os_free(conn->subject_match);
 	conn->subject_match = NULL;
@@ -461,6 +629,14 @@
 			return -1;
 	}
 
+	os_free(conn->check_cert_subject);
+	conn->check_cert_subject = NULL;
+	if (check_cert_subject) {
+		conn->check_cert_subject = os_strdup(check_cert_subject);
+		if (!conn->check_cert_subject)
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -819,6 +995,8 @@
 	case X509_V_ERR_CERT_UNTRUSTED:
 	case X509_V_ERR_CERT_REJECTED:
 		return TLS_FAIL_BAD_CERTIFICATE;
+	case RSA_KEY_SIZE_E:
+		return TLS_FAIL_INSUFFICIENT_KEY_LEN;
 	default:
 		return TLS_FAIL_UNSPECIFIED;
 	}
@@ -838,6 +1016,148 @@
 }
 
 
+/**
+ * match_dn_field - Match configuration DN field against Certificate DN field
+ * @cert: Certificate
+ * @nid: NID of DN field
+ * @field: Field name
+ * @value DN field value which is passed from configuration
+ *	e.g., if configuration have C=US and this argument will point to US.
+ * Returns: 1 on success and 0 on failure
+ */
+static int match_dn_field(WOLFSSL_X509 *cert, int nid, const char *field,
+			  const char *value)
+{
+	int ret = 0;
+	int len = os_strlen(value);
+	char buf[256];
+	/* Fetch value based on NID */
+	int buf_len = wolfSSL_X509_NAME_get_text_by_NID(
+		wolfSSL_X509_get_subject_name((WOLFSSL_X509 *) cert), nid,
+		buf, sizeof(buf));
+
+	if (buf_len >= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Matching fields: '%s' '%s' '%s'", field,
+			   value, buf);
+
+		/* Check wildcard at the right end side */
+		/* E.g., if OU=develop* mentioned in configuration, allow 'OU'
+		 * of the subject in the client certificate to start with
+		 * 'develop' */
+		if (len > 0 && value[len - 1] == '*') {
+			ret = buf_len >= len &&
+				os_memcmp(buf, value, len - 1) == 0;
+		} else {
+			ret = os_strcmp(buf, value) == 0;
+		}
+	} else {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: cert does not contain entry for '%s'",
+			   field);
+	}
+
+	return ret;
+}
+
+
+#define DN_FIELD_LEN 20
+
+/**
+ * get_value_from_field - Get value from DN field
+ * @cert: Certificate
+ * @field_str: DN field string which is passed from configuration file (e.g.,
+ *	 C=US)
+ * @processed_nids: List of NIDs already processed
+ * Returns: 1 on success and 0 on failure
+ */
+static int get_value_from_field(WOLFSSL_X509 *cert, char *field_str,
+				int *processed_nids)
+{
+	int nid, i;
+	char *context = NULL, *name, *value;
+
+	if (os_strcmp(field_str, "*") == 0)
+		return 1; /* wildcard matches everything */
+
+	name = str_token(field_str, "=", &context);
+	if (!name)
+		return 0;
+
+	nid = wolfSSL_OBJ_txt2nid(name);
+	if (nid == NID_undef) {
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: Unknown field '%s' in check_cert_subject",
+			   name);
+		return 0;
+	}
+
+	/* Check for duplicates */
+	for (i = 0; processed_nids[i] != NID_undef && i < DN_FIELD_LEN; i++) {
+		if (processed_nids[i] == nid) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: No support for multiple DN's in check_cert_subject");
+			return 0;
+		}
+	}
+	if (i == DN_FIELD_LEN) {
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: Only %d DN's are supported in check_cert_subject",
+			   DN_FIELD_LEN);
+		return 0;
+	}
+	processed_nids[i] = nid;
+
+	value = str_token(field_str, "=", &context);
+	if (!value) {
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: Distinguished Name field '%s' value is not defined in check_cert_subject",
+			   name);
+		return 0;
+	}
+
+	return match_dn_field(cert, nid, name, value);
+}
+
+
+/**
+ * tls_match_dn_field - Match subject DN field with check_cert_subject
+ * @cert: Certificate
+ * @match: check_cert_subject string
+ * Returns: Return 1 on success and 0 on failure
+*/
+static int tls_match_dn_field(WOLFSSL_X509 *cert, const char *match)
+{
+	const char *token, *last = NULL;
+	/* Maximum length of each DN field is 255 characters */
+	char field[256];
+	int processed_nids[DN_FIELD_LEN], i;
+
+	for (i = 0; i < DN_FIELD_LEN; i++)
+		processed_nids[i] = NID_undef;
+
+	/* Process each '/' delimited field */
+	while ((token = cstr_token(match, "/", &last))) {
+		if (last - token >= (int) sizeof(field)) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: Too long DN matching field value in '%s'",
+				   match);
+			return 0;
+		}
+		os_memcpy(field, token, last - token);
+		field[last - token] = '\0';
+
+		if (!get_value_from_field(cert, field, processed_nids)) {
+			wpa_printf(MSG_INFO, "wolfSSL: No match for DN '%s'",
+				   field);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
 static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
 {
 	struct wpabuf *buf = NULL;
@@ -845,7 +1165,7 @@
 	int cert_len;
 
 	data = wolfSSL_X509_get_der(cert, &cert_len);
-	if (!data)
+	if (data)
 		buf = wpabuf_alloc_copy(data, cert_len);
 
 	return buf;
@@ -859,7 +1179,7 @@
 {
 	union tls_event_data ev;
 	struct wpabuf *cert = NULL;
-	struct tls_context *context = conn->context;
+	const struct tls_context *context = conn->context;
 
 	if (!context->event_cb)
 		return;
@@ -877,13 +1197,44 @@
 }
 
 
+static int wolfssl_cert_tod(X509 *cert)
+{
+	WOLFSSL_STACK *ext;
+	int i;
+	char *buf;
+	int tod = 0;
+
+	ext = wolfSSL_X509_get_ext_d2i(cert, CERT_POLICY_OID, NULL, NULL);
+	if (!ext)
+		return 0;
+
+	for (i = 0; i < wolfSSL_sk_num(ext); i++) {
+		WOLFSSL_ASN1_OBJECT *policy;
+
+		policy = wolfSSL_sk_value(ext, i);
+		if (!policy)
+			continue;
+
+		buf = (char*)policy->obj;
+		wpa_printf(MSG_DEBUG, "wolfSSL: Certificate Policy %s", buf);
+		if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.1") == 0)
+			tod = 1; /* TOD-STRICT */
+		else if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.2") == 0 && !tod)
+			tod = 2; /* TOD-TOFU */
+	}
+	wolfSSL_sk_pop_free(ext, NULL);
+
+	return tod;
+}
+
+
 static void wolfssl_tls_cert_event(struct tls_connection *conn,
 				   WOLFSSL_X509 *err_cert, int depth,
 				   const char *subject)
 {
 	struct wpabuf *cert = NULL;
 	union tls_event_data ev;
-	struct tls_context *context = conn->context;
+	const struct tls_context *context = conn->context;
 	char *alt_subject[TLS_MAX_ALT_SUBJECT];
 	int alt, num_alt_subject = 0;
 	WOLFSSL_GENERAL_NAME *gen;
@@ -964,6 +1315,7 @@
 	for (alt = 0; alt < num_alt_subject; alt++)
 		ev.peer_cert.altsubject[alt] = alt_subject[alt];
 	ev.peer_cert.num_altsubject = num_alt_subject;
+	ev.peer_cert.tod = wolfssl_cert_tod(err_cert);
 
 	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
 	wpabuf_free(cert);
@@ -979,8 +1331,9 @@
 	int err, depth;
 	WOLFSSL *ssl;
 	struct tls_connection *conn;
-	struct tls_context *context;
+	const struct tls_context *context;
 	char *match, *altmatch, *suffix_match, *domain_match;
+	const char *check_cert_subject;
 	const char *err_str;
 
 	err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
@@ -996,7 +1349,7 @@
 	wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf,
 				  sizeof(buf));
 
-	conn = wolfSSL_get_ex_data(ssl, 0);
+	conn = wolfSSL_get_ex_data(ssl, TLS_SSL_CON_EX_IDX);
 	if (!conn) {
 		wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data");
 		return 0;
@@ -1069,6 +1422,8 @@
 	}
 #endif /* CONFIG_SHA256 */
 
+	wolfssl_tls_cert_event(conn, err_cert, depth, buf);
+
 	if (!preverify_ok) {
 		wpa_printf(MSG_WARNING,
 			   "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'",
@@ -1082,7 +1437,19 @@
 		   "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
 		   __func__, preverify_ok, err, err_str,
 		   conn->ca_cert_verify, depth, buf);
-	if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+	check_cert_subject = conn->check_cert_subject;
+	if (!check_cert_subject)
+		check_cert_subject = conn->context->check_cert_subject;
+	if (check_cert_subject && depth == 0 &&
+	    !tls_match_dn_field(err_cert, check_cert_subject)) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: Subject '%s' did not match with '%s'",
+			   buf, check_cert_subject);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Distinguished Name",
+				       TLS_FAIL_DN_MISMATCH);
+	} else if (depth == 0 && match && os_strstr(buf, match) == NULL) {
 		wpa_printf(MSG_WARNING,
 			   "TLS: Subject '%s' did not match with '%s'",
 			   buf, match);
@@ -1116,8 +1483,6 @@
 		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
 				       "Domain mismatch",
 				       TLS_FAIL_DOMAIN_MISMATCH);
-	} else {
-		wolfssl_tls_cert_event(conn, err_cert, depth, buf);
 	}
 
 	if (conn->cert_probe && preverify_ok && depth == 0) {
@@ -1129,31 +1494,6 @@
 				       TLS_FAIL_SERVER_CHAIN_PROBE);
 	}
 
-#ifdef HAVE_OCSP_WOLFSSL
-	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
-	    preverify_ok) {
-		enum ocsp_result res;
-
-		res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
-				      conn->peer_issuer,
-				      conn->peer_issuer_issuer);
-		if (res == OCSP_REVOKED) {
-			preverify_ok = 0;
-			wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
-					       "certificate revoked",
-					       TLS_FAIL_REVOKED);
-			if (err == X509_V_OK)
-				X509_STORE_CTX_set_error(
-					x509_ctx, X509_V_ERR_CERT_REVOKED);
-		} else if (res != OCSP_GOOD &&
-			   (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
-			preverify_ok = 0;
-			wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
-					       "bad certificate status response",
-					       TLS_FAIL_UNSPECIFIED);
-		}
-	}
-#endif /* HAVE_OCSP_WOLFSSL */
 	if (depth == 0 && preverify_ok && context->event_cb != NULL)
 		context->event_cb(context->cb_ctx,
 				  TLS_CERT_CHAIN_SUCCESS, NULL);
@@ -1237,15 +1577,9 @@
 	}
 
 	if (ca_cert || ca_path) {
-		WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new();
-
-		if (!cm) {
-			wpa_printf(MSG_INFO,
-				   "SSL: failed to create certificate store");
-			return -1;
-		}
-		wolfSSL_CTX_set_cert_store(ctx, cm);
-
+		wpa_printf(MSG_DEBUG, "SSL: Loading CA's from '%s' and '%s'",
+			   ca_cert ? ca_cert : "N/A",
+			   ca_path ? ca_path : "N/A");
 		if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) !=
 		    SSL_SUCCESS) {
 			wpa_printf(MSG_INFO,
@@ -1262,6 +1596,7 @@
 				return -1;
 			}
 		}
+		wpa_printf(MSG_DEBUG, "SSL: Loaded ca_cert or ca_path");
 		return 0;
 	}
 
@@ -1272,19 +1607,24 @@
 
 static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags)
 {
+	long op = 0;
+
 #ifdef HAVE_SESSION_TICKET
 	if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET))
 		wolfSSL_UseSessionTicket(ssl);
 #endif /* HAVE_SESSION_TICKET */
 
+	wpa_printf(MSG_DEBUG, "SSL: conn_flags: %d", flags);
+
 	if (flags & TLS_CONN_DISABLE_TLSv1_0)
-		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1);
+		op |= WOLFSSL_OP_NO_TLSv1;
 	if (flags & TLS_CONN_DISABLE_TLSv1_1)
-		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+		op |= WOLFSSL_OP_NO_TLSv1_1;
 	if (flags & TLS_CONN_DISABLE_TLSv1_2)
-		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+		op |= WOLFSSL_OP_NO_TLSv1_2;
 	if (flags & TLS_CONN_DISABLE_TLSv1_3)
-		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
+		op |= WOLFSSL_OP_NO_TLSv1_3;
+	wolfSSL_set_options(ssl, op);
 }
 
 
@@ -1296,7 +1636,8 @@
 	if (tls_connection_set_subject_match(conn, params->subject_match,
 					     params->altsubject_match,
 					     params->suffix_match,
-					     params->domain_match) < 0) {
+					     params->domain_match,
+					     params->check_cert_subject) < 0) {
 		wpa_printf(MSG_INFO, "Error setting subject match");
 		return -1;
 	}
@@ -1324,13 +1665,17 @@
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s",
-		   params->openssl_ciphers ? params->openssl_ciphers : "N/A");
-	if (params->openssl_ciphers &&
-	    wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
-		wpa_printf(MSG_INFO,
-			   "wolfSSL: Failed to set cipher string '%s'",
-			   params->openssl_ciphers);
+	if (handle_ciphersuites(NULL, conn->ssl, params->openssl_ciphers,
+				params->flags) != 0) {
+		wpa_printf(MSG_INFO, "wolfssl: Error setting ciphersuites");
+		return -1;
+	}
+
+	if (params->openssl_ecdh_curves &&
+	    wolfSSL_set1_curves_list(conn->ssl, params->openssl_ecdh_curves) !=
+	    1) {
+		wpa_printf(MSG_INFO, "wolfSSL: Failed to set ECDH curves '%s'",
+			   params->openssl_ecdh_curves);
 		return -1;
 	}
 
@@ -1524,10 +1869,25 @@
 int tls_global_set_params(void *tls_ctx,
 			  const struct tls_connection_params *params)
 {
+	/* Need to cast away const as this is one of the only places
+	 * where we should modify it */
+	struct tls_context *context =
+		(struct tls_context *) ssl_ctx_get_tls_context(tls_ctx);
+
 	wpa_printf(MSG_DEBUG, "SSL: global set params");
 
-	if (params->check_cert_subject)
-		return -1; /* not yet supported */
+	os_free(context->check_cert_subject);
+	context->check_cert_subject = NULL;
+	if (params->check_cert_subject) {
+		context->check_cert_subject =
+			os_strdup(params->check_cert_subject);
+		if (!context->check_cert_subject) {
+			wpa_printf(MSG_ERROR,
+				   "SSL: Failed to copy check_cert_subject '%s'",
+				   params->check_cert_subject);
+			return -1;
+		}
+	}
 
 	if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
 		wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
@@ -1556,35 +1916,74 @@
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s",
-		   params->openssl_ciphers ? params->openssl_ciphers : "N/A");
-	if (params->openssl_ciphers &&
-	    wolfSSL_CTX_set_cipher_list(tls_ctx,
-					params->openssl_ciphers) != 1) {
-		wpa_printf(MSG_INFO,
-			   "wolfSSL: Failed to set cipher string '%s'",
-			   params->openssl_ciphers);
+	if (handle_ciphersuites(tls_ctx, NULL, params->openssl_ciphers,
+				params->flags) != 0) {
+		wpa_printf(MSG_INFO, "wolfssl: Error setting ciphersuites");
 		return -1;
 	}
 
-	if (params->openssl_ecdh_curves) {
-		wpa_printf(MSG_INFO,
-			   "wolfSSL: openssl_ecdh_curves not supported");
+	if (params->openssl_ecdh_curves &&
+	    wolfSSL_CTX_set1_curves_list((WOLFSSL_CTX *) tls_ctx,
+					 params->openssl_ecdh_curves) != 1) {
+		wpa_printf(MSG_INFO, "wolfSSL: Failed to set ECDH curves '%s'",
+			   params->openssl_ecdh_curves);
 		return -1;
 	}
 
 #ifdef HAVE_SESSION_TICKET
 	/* Session ticket is off by default - can't disable once on. */
-	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
-		wolfSSL_CTX_UseSessionTicket(tls_ctx);
+	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET) &&
+	    wolfSSL_CTX_UseSessionTicket(tls_ctx) != WOLFSSL_SUCCESS) {
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: wolfSSL_CTX_UseSessionTicket failed");
+		return -1;
+	}
 #endif /* HAVE_SESSION_TICKET */
 
 #ifdef HAVE_OCSP
 	if (params->ocsp_stapling_response) {
-		wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx,
-						params->ocsp_stapling_response);
-		wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb,
-				       ocsp_resp_free_cb, NULL);
+		if (wolfSSL_CTX_EnableOCSP(tls_ctx,
+					   WOLFSSL_OCSP_URL_OVERRIDE) !=
+		    WOLFSSL_SUCCESS ||
+		    /* Workaround to force using the override URL without
+		     * enabling OCSP */
+		    wolfSSL_CTX_DisableOCSP(tls_ctx) != WOLFSSL_SUCCESS) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: wolfSSL_CTX_UseOCSPStapling() failed");
+			return -1;
+		}
+
+		if (wolfSSL_CTX_UseOCSPStapling(tls_ctx, WOLFSSL_CSR_OCSP,
+						WOLFSSL_CSR_OCSP_USE_NONCE) !=
+		    WOLFSSL_SUCCESS) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: wolfSSL_CTX_UseOCSPStapling() failed");
+			return -1;
+		}
+
+		if (wolfSSL_CTX_EnableOCSPStapling(tls_ctx) !=
+		    WOLFSSL_SUCCESS) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: wolfSSL_EnableOCSPStapling() failed");
+			return -1;
+		}
+
+		if (wolfSSL_CTX_SetOCSP_OverrideURL(
+			    tls_ctx,
+			    params->ocsp_stapling_response) !=
+		    WOLFSSL_SUCCESS) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: wolfSSL_CTX_SetOCSP_OverrideURL() failed");
+			return -1;
+		}
+
+		if (wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb,
+					   ocsp_resp_free_cb, NULL) !=
+		    WOLFSSL_SUCCESS) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: wolfSSL_CTX_SetOCSP_Cb() failed");
+			return -1;
+		}
 	}
 #endif /* HAVE_OCSP */
 
@@ -1610,12 +2009,13 @@
 			      const u8 *session_ctx, size_t session_ctx_len)
 {
 	static int counter = 0;
-	struct tls_context *context;
+	const struct tls_context *context;
 
 	if (!conn)
 		return -1;
 
 	wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer);
+	wpa_printf(MSG_DEBUG, "SSL: flags: %d", flags);
 
 	if (verify_peer) {
 		conn->ca_cert_verify = 1;
@@ -1629,7 +2029,7 @@
 
 	wolfSSL_set_accept_state(conn->ssl);
 
-	context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx, 0);
+	context = ssl_ctx_get_tls_context(ssl_ctx);
 	if (context && context->tls_session_lifetime == 0) {
 		/*
 		 * Set session id context to a unique value to make sure
@@ -1645,7 +2045,7 @@
 					       session_ctx_len);
 	}
 
-	/* TODO: do we need to fake a session like OpenSSL does here? */
+	tls_set_conn_flags(conn->ssl, flags);
 
 	return 0;
 }
@@ -1692,12 +2092,28 @@
 			char msg[80];
 
 			wpa_printf(MSG_DEBUG,
-				   "SSL: %s - failed %s",
+				   "SSL: %s - failed (%d) %s",
 				   server ? "wolfSSL_accept" :
-				   "wolfSSL_connect",
+				   "wolfSSL_connect", err,
 				   wolfSSL_ERR_error_string(err, msg));
 			conn->failed++;
 		}
+
+		/* Generate extra events */
+		if (err == OCSP_CERT_REVOKED ||
+		    err == BAD_CERTIFICATE_STATUS_ERROR ||
+		    err == OCSP_CERT_REVOKED) {
+			char buf[256];
+			WOLFSSL_X509 *err_cert;
+
+			err_cert = wolfSSL_get_peer_certificate(conn->ssl);
+			wolfSSL_X509_NAME_oneline(
+				wolfSSL_X509_get_subject_name(err_cert),
+				buf, sizeof(buf));
+			wolfssl_tls_fail_event(conn, err_cert, err, 0, buf,
+					       "bad certificate status response",
+					       TLS_FAIL_UNSPECIFIED);
+		}
 	}
 
 	return conn->output.out_data;
@@ -1866,11 +2282,12 @@
 	char buf[128], *pos, *end;
 	u8 *c;
 	int ret;
+	bool set_sig_algs = false;
 
 	if (!conn || !conn->ssl || !ciphers)
 		return -1;
 
-	buf[0] = '\0';
+	buf[0] = buf[1] = '\0';
 	pos = buf;
 	end = pos + sizeof(buf);
 
@@ -1890,6 +2307,7 @@
 			break;
 		case TLS_CIPHER_ANON_DH_AES128_SHA:
 			suite = "ADH-AES128-SHA";
+			set_sig_algs = true;
 			break;
 		case TLS_CIPHER_RSA_DHE_AES256_SHA:
 			suite = "DHE-RSA-AES256-SHA";
@@ -1910,10 +2328,16 @@
 		c++;
 	}
 
-	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1);
+	/* +1 to skip the ":" */
+	if (handle_ciphersuites(NULL, conn->ssl, buf + 1, conn->flags) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfssl: Cipher suite configuration failed");
+		return -1;
+	}
 
-	if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
-		wpa_printf(MSG_DEBUG, "Cipher suite configuration failed");
+	if (set_sig_algs &&
+	    wolfSSL_set1_sigalgs_list(conn->ssl, SUITEB_TLS_128_SIGALGS) != 1) {
+		wpa_printf(MSG_DEBUG, "wolfssl: Sigalg configuration failed");
 		return -1;
 	}
 
@@ -1924,34 +2348,19 @@
 int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
 		   char *buf, size_t buflen)
 {
-	WOLFSSL_CIPHER *cipher;
 	const char *name;
 
 	if (!conn || !conn->ssl)
 		return -1;
 
-	cipher = wolfSSL_get_current_cipher(conn->ssl);
-	if (!cipher)
-		return -1;
-
-	name = wolfSSL_CIPHER_get_name(cipher);
+	if (wolfSSL_version(conn->ssl) == TLS1_3_VERSION)
+		name = wolfSSL_get_cipher(conn->ssl);
+	else
+		name = wolfSSL_get_cipher_name(conn->ssl);
 	if (!name)
 		return -1;
 
-	if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0)
-		os_strlcpy(buf, "RC4-SHA", buflen);
-	else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0)
-		os_strlcpy(buf, "AES128-SHA", buflen);
-	else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0)
-		os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen);
-	else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0)
-		os_strlcpy(buf, "ADH-AES128-SHA", buflen);
-	else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0)
-		os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen);
-	else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0)
-		os_strlcpy(buf, "AES256-SHA", buflen);
-	else
-		os_strlcpy(buf, name, buflen);
+	os_strlcpy(buf, name, buflen);
 
 	return 0;
 }
@@ -2273,13 +2682,13 @@
 		goto fail;
 	}
 
-	old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	old = wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX);
 	if (old) {
 		wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p",
 			   old);
 		wpabuf_free(old);
 	}
-	if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+	if (wolfSSL_SESSION_set_ex_data(sess, TLS_SESSION_EX_IDX, data) != 1)
 		goto fail;
 
 	wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data);
@@ -2302,7 +2711,7 @@
 	sess = wolfSSL_get_session(conn->ssl);
 	if (!sess)
 		return NULL;
-	return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	return wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX);
 }
 
 
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 765ea59..8a7e673 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -892,6 +892,14 @@
 	bool eht_enabled;
 
 	/**
+	 * punct_bitmap - Preamble puncturing bitmap
+	 * Each bit corresponds to a 20 MHz subchannel, the lowest bit for the
+	 * channel with the lowest frequency. A bit set to 1 indicates that the
+	 * subchannel is punctured, otherwise active.
+	 */
+	u16 punct_bitmap;
+
+	/**
 	 * link_id: If >=0 indicates the link of the AP MLD to configure
 	 */
 	int link_id;
@@ -1396,11 +1404,15 @@
 	 */
 	struct wpa_driver_mld_params mld_params;
 
-
 	/**
 	 * rsn_overriding - wpa_supplicant RSN overriding support
 	 */
 	bool rsn_overriding;
+
+	/**
+	 * spp_amsdu - SPP A-MSDU used on this connection
+	 */
+	bool spp_amsdu;
 };
 
 enum hide_ssid {
@@ -1645,11 +1657,6 @@
 	int disable_dgaf;
 
 	/**
-	 * osen - Whether OSEN security is enabled
-	 */
-	int osen;
-
-	/**
 	 * freq - Channel parameters for dynamic bandwidth changes
 	 */
 	struct hostapd_freq_params *freq;
@@ -2026,10 +2033,17 @@
 	 * %KEY_FLAG_GROUP_TX_DEFAULT
 	 *  GTK key valid for TX only, immediately taking over TX.
 	 * %KEY_FLAG_PAIRWISE_RX_TX
-	 *  Pairwise key immediately becoming the active pairwise key.
+	 *  Pairwise key immediately becoming the active pairwise key. If this
+	 *  key was previously set as an alternative RX-only key with
+	 *  %KEY_FLAG_PAIRWISE_RX | %KEY_FLAG_NEXT, the alternative RX-only key
+	 *  is taken into use for both TX and RX without changing the RX counter
+	 *  values.
 	 * %KEY_FLAG_PAIRWISE_RX
 	 *  Pairwise key not yet valid for TX. (Only usable when Extended
-	 *  Key ID is supported by the driver.)
+	 *  Key ID is supported by the driver or when configuring the next TK
+	 *  for RX-only with %KEY_FLAG_NEXT in which case the new TK can be used
+	 *  as an alternative key for decrypting received frames without
+	 *  replacing the possibly already configured old TK.)
 	 * %KEY_FLAG_PAIRWISE_RX_TX_MODIFY
 	 *  Enable TX for a pairwise key installed with
 	 *  KEY_FLAG_PAIRWISE_RX.
@@ -2141,7 +2155,6 @@
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE		0x00100000
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384	0x00200000
 #define WPA_DRIVER_CAPA_KEY_MGMT_CCKM		0x00400000
-#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN		0x00800000
 #define WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY	0x01000000
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY	0x02000000
 	/** Bitfield of supported key management suites */
@@ -2372,6 +2385,12 @@
 #define WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA	0x0000000000400000ULL
 /** Driver supports NAN offload */
 #define WPA_DRIVER_FLAGS2_NAN_OFFLOAD		0x0000000000800000ULL
+/** Driver/device supports SPP A-MSDUs */
+#define WPA_DRIVER_FLAGS2_SPP_AMSDU		0x0000000001000000ULL
+/** Driver supports P2P V2 */
+#define WPA_DRIVER_FLAGS2_P2P_FEATURE_V2	0x0000000002000000ULL
+/** Driver supports P2P PCC mode */
+#define WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE	0x0000000004000000ULL
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2492,6 +2511,10 @@
 	unsigned int mbssid_max_interfaces;
 	/* Maximum profile periodicity for enhanced MBSSID advertisement */
 	unsigned int ema_max_periodicity;
+
+	/* Maximum number of bytes of extra IE(s) that can be added to Probe
+	 * Request frames */
+	size_t max_probe_req_ie_len;
 };
 
 
@@ -2598,6 +2621,7 @@
 	bool mld_link_sta;
 	s8 mld_link_id;
 	const u8 *mld_link_addr;
+	u16 eml_cap;
 };
 
 struct mac_address {
@@ -2645,6 +2669,7 @@
 #define WPA_STA_TDLS_PEER BIT(4)
 #define WPA_STA_AUTHENTICATED BIT(5)
 #define WPA_STA_ASSOCIATED BIT(6)
+#define WPA_STA_SPP_AMSDU BIT(7)
 
 enum tdls_oper {
 	TDLS_DISCOVERY_REQ,
@@ -2772,7 +2797,6 @@
  * @beacon_after: Next beacon/probe resp/asooc resp info
  * @counter_offset_beacon: Offset to the count field in beacon's tail
  * @counter_offset_presp: Offset to the count field in probe resp.
- * @punct_bitmap - Preamble puncturing bitmap
  * @link_id: Link ID to determine the link for MLD; -1 for non-MLD
  * @ubpr: Unsolicited broadcast Probe Response frame data
  */
@@ -2787,7 +2811,6 @@
 	u16 counter_offset_beacon[2];
 	u16 counter_offset_presp[2];
 
-	u16 punct_bitmap;
 	int link_id;
 
 	struct unsol_bcast_probe_resp ubpr;
@@ -3146,6 +3169,53 @@
 	 * broadcast keys, so key index 0 is available for this kind of
 	 * configuration.
 	 *
+	 * For pairwise keys, there are potential race conditions between
+	 * enabling a new TK on each end of the connection and sending the first
+	 * protected frame. Drivers have multiple options on which style of key
+	 * configuration to support with the simplest option not providing any
+	 * protection for the race condition while the more complex options do
+	 * provide partial or full protection.
+	 *
+	 * Option 1: Do not support extended key IDs (i.e., use only Key ID 0
+	 * for pairwise keys) and do not support configuration of the next TK
+	 * as an alternative RX key. This provides no protection, but is simple
+	 * to support. The driver needs to ignore set_key() calls with
+	 * KEY_FLAG_NEXT.
+	 *
+	 * Option 2: Do not support extended key IDs (i.e., use only Key ID 0
+	 * for pairwise keys), but support configuration of the next TK as an
+	 * alternative RX key for the initial 4-way handshake. This provides
+	 * protection for the initial key setup at the beginning of an
+	 * association. The driver needs to configure the initial TK for RX-only
+	 * when receiving a set_key() call with KEY_FLAG_NEXT. This RX-only key
+	 * is ready for receiving protected Data frames from the peer before the
+	 * local device has enabled the key for TX. Unprotected EAPOL frames
+	 * need to be allowed even when this next TK is configured as RX-only
+	 * key. The same key is then set with KEY_FLAG_PAIRWISE_RX_TX to enable
+	 * its use for both TX and RX. The driver ignores set_key() calls with
+	 * KEY_FLAG_NEXT when a TK has been configured. When fully enabling the
+	 * TK for TX and RX, the RX counters associated with the TK must not be
+	 * cleared.
+	 *
+	 * Option 3: Same as option 2, but the driver supports multiple RX keys
+	 * in parallel during PTK rekeying. The driver processed set_key() calls
+	 * with KEY_FLAG_NEXT also when a TK has been configured. At that point
+	 * in the rekeying sequence the driver uses the previously configured TK
+	 * for TX and decrypts received frames with either the previously
+	 * configured TK or the next TK (RX-only).
+	 *
+	 * Option 4: The driver supports extended Key IDs and they are used for
+	 * an association but does not support KEY_FLAG_NEXT (options 2 and 3).
+	 * The next TK is configured as RX-only with KEY_FLAG_PAIRWISE_RX and
+	 * it is enabled for TX and RX with KEY_FLAG_PAIRWISE_RX_TX_MODIFY. When
+	 * extended key ID is not used for an association, the driver behaves
+	 * like in option 1.
+	 *
+	 * Option 5 and 6: Like option 4 but with support for KEY_FLAG_NEXT as
+	 * described above for options 2 and 3, respectively. Option 4 is used
+	 * for cases where extended key IDs are used for an association. Option
+	 * 2 or 3 is used for cases where extended key IDs are not used.
+	 *
 	 * Please note that TKIP keys include separate TX and RX MIC keys and
 	 * some drivers may expect them in different order than wpa_supplicant
 	 * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
@@ -3503,12 +3573,15 @@
 	 * e.g., wpa_supplicant_event()
 	 * @ifname: interface name, e.g., wlan0
 	 * @global_priv: private driver global data from global_init()
+	 * @p2p_mode: P2P mode for a GO (not applicable for other interface
+	 *	types)
 	 * Returns: Pointer to private data, %NULL on failure
 	 *
 	 * This function can be used instead of init() if the driver wrapper
 	 * uses global data.
 	 */
-	void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+	void * (*init2)(void *ctx, const char *ifname, void *global_priv,
+			enum wpa_p2p_mode p2p_mode);
 
 	/**
 	 * get_interfaces - Get information about available interfaces
@@ -4044,7 +4117,10 @@
 	 * @bssid: BSSID (Address 3)
 	 * @data: Frame body
 	 * @data_len: data length in octets
-	 @ @no_cck: Whether CCK rates must not be used to transmit this frame
+	 * @no_cck: Whether CCK rates must not be used to transmit this frame
+	 * @link_id: Link ID of the specified link; -1 for non-MLO cases and for
+	 *	frames that target the MLD instead of a specific link in MLO
+	 *	cases
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This command can be used to request the driver to transmit an action
@@ -4065,7 +4141,8 @@
 	 */
 	int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
 			   const u8 *dst, const u8 *src, const u8 *bssid,
-			   const u8 *data, size_t data_len, int no_cck);
+			   const u8 *data, size_t data_len, int no_cck,
+			   int link_id);
 
 	/**
 	 * send_action_cancel_wait - Cancel action frame TX wait
@@ -5333,6 +5410,25 @@
 	 */
 	int (*nan_cancel_subscribe)(void *priv, int subscribe_id);
 
+	/**
+	 * can_share_drv - Check whether driver interface can be shared
+	 * @ctx: Pointer to hostapd context
+	 * @params: Configuration for the driver wrapper
+	 * @hapd: Pointer for overwriting the hapd context or %NULL
+	 * 	if can't find a shared drv
+	 *
+	 * Checks whether the driver interface with same phy name is
+	 * already present under the global driver which can be shared
+	 * instead of creating a new driver interface instance. If present,
+	 * @hapd will be overwritten with the hapd pointer which this shared
+	 * drv's first BSS is using. This will help the caller to later call
+	 * if_add().
+	 *
+	 * Returns: true if it can be shared or else false.
+	 */
+	bool (*can_share_drv)(void *ctx, struct wpa_init_params *params,
+			      void **hapd);
+
 #ifdef CONFIG_TESTING_OPTIONS
 	int (*register_frame)(void *priv, u16 type,
 			      const u8 *match, size_t match_len,
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index 7186330..8fb23a8 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -504,6 +504,9 @@
 	const u8 *key = params->key;
 	size_t key_len = params->key_len;
 
+	if (params->key_flag & KEY_FLAG_NEXT)
+		return -1;
+
 	if (alg == WPA_ALG_NONE)
 		return atheros_del_key(drv, addr, key_idx);
 
@@ -1940,25 +1943,6 @@
 	wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
 			params->assocresp_ies);
 
-#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN))
-	if (params->osen) {
-		struct wpa_bss_params bss_params;
-
-		os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
-		bss_params.enabled = 1;
-		bss_params.wpa = 2;
-		bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
-		bss_params.wpa_group = WPA_CIPHER_CCMP;
-		bss_params.ieee802_1x = 1;
-
-		if (atheros_set_privacy(priv, 1) ||
-		    set80211param(priv, IEEE80211_PARAM_OSEN, 1))
-			return -1;
-
-		return atheros_set_ieee8021x(priv, &bss_params);
-	}
-#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
-
 	return 0;
 }
 
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 82d8a01..66155b4 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -325,6 +325,9 @@
 	const u8 *key = params->key;
 	size_t key_len = params->key_len;
 
+	if (params->key_flag & KEY_FLAG_NEXT)
+		return -1;
+
 	wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
 		   "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
 		   set_tx, seq_len, key_len);
@@ -998,7 +1001,8 @@
 }
 
 static void *
-bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params,
+	 enum wpa_p2p_mode p2p_mode)
 {
 	struct bsd_driver_data *drv;
 
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 9589183..7a9f18c 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -374,6 +374,7 @@
 	DF2S(MLO);
 	DF2S(SCAN_MIN_PREQ);
 	DF2S(SAE_OFFLOAD_STA);
+	DF2S(SPP_AMSDU);
 	}
 	return "UNKNOWN";
 #undef DF2S
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 3aa5860..74c7767 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -411,6 +411,9 @@
 	const u8 *key = params->key;
 	size_t key_len = params->key_len;
 
+	if (params->key_flag & KEY_FLAG_NEXT)
+		return -1;
+
 	blen = sizeof(*param) + key_len;
 	buf = os_zalloc(blen);
 	if (buf == NULL)
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 0351705..b030b0b 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -1037,6 +1037,9 @@
 wpa_driver_ndis_set_key_wrapper(void *priv,
 				struct wpa_driver_set_key_params *params)
 {
+	if (params->key_flag & KEY_FLAG_NEXT)
+		return -1;
+
 	return wpa_driver_ndis_set_key(params->ifname, priv,
 				       params->alg, params->addr,
 				       params->key_idx, params->set_tx,
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 5890ac6..0848d16 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -167,7 +167,8 @@
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
 				   const u8 *set_addr, int first,
-				   const char *driver_params);
+				   const char *driver_params,
+				   enum wpa_p2p_mode p2p_mode);
 static int nl80211_send_frame_cmd(struct i802_bss *bss,
 				  unsigned int freq, unsigned int wait,
 				  const u8 *buf, size_t buf_len,
@@ -351,6 +352,7 @@
 	int err;
 	struct nl_msg *orig_msg;
 	struct nl80211_err_info *err_info;
+	struct wpa_driver_nl80211_data *drv;
 };
 
 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
@@ -387,8 +389,12 @@
 	if (tb[NLMSGERR_ATTR_MSG]) {
 		len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
 			      nla_len(tb[NLMSGERR_ATTR_MSG]));
-		wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
-			   len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+		if (err_args->drv)
+			wpa_msg(err_args->drv->ctx, MSG_ERROR, "nl80211: kernel reports: %*s",
+				len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+		else
+			wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
+				   len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
 	}
 
 	if (!err_args->err_info)
@@ -493,13 +499,14 @@
 }
 
 
-int send_and_recv(struct nl80211_global *global,
-		  struct nl_sock *nl_handle, struct nl_msg *msg,
-		  int (*valid_handler)(struct nl_msg *, void *),
-		  void *valid_data,
-		  int (*ack_handler_custom)(struct nl_msg *, void *),
-		  void *ack_data,
-		  struct nl80211_err_info *err_info)
+int send_and_recv_glb(struct nl80211_global *global,
+		      struct wpa_driver_nl80211_data *drv, /* may be NULL */
+		      struct nl_sock *nl_handle, struct nl_msg *msg,
+		      int (*valid_handler)(struct nl_msg *, void *),
+		      void *valid_data,
+		      int (*ack_handler_custom)(struct nl_msg *, void *),
+		      void *ack_data,
+		      struct nl80211_err_info *err_info)
 {
 	struct nl_cb *cb, *s_nl_cb;
 	struct nl80211_ack_err_args err;
@@ -518,13 +525,19 @@
 
 	/* try to set NETLINK_EXT_ACK to 1, ignoring errors */
 	opt = 1;
-	setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
-		   NETLINK_EXT_ACK, &opt, sizeof(opt));
+	if (setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+		       NETLINK_EXT_ACK, &opt, sizeof(opt)) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: setsockopt(NETLINK_EXT_ACK) failed: %s (ignored)",
+			   strerror(errno));
 
 	/* try to set NETLINK_CAP_ACK to 1, ignoring errors */
 	opt = 1;
-	setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
-		   NETLINK_CAP_ACK, &opt, sizeof(opt));
+	if (setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+		       NETLINK_CAP_ACK, &opt, sizeof(opt)) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: setsockopt(NETLINK_CAP_ACK) failed: %s (ignored)",
+			   strerror(errno));
 
 	err.err = nl_send_auto_complete(nl_handle, msg);
 	if (err.err < 0) {
@@ -541,6 +554,7 @@
 	err.err = 1;
 	err.orig_msg = msg;
 	err.err_info = err_info;
+	err.drv = drv;
 
 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
@@ -656,8 +670,8 @@
 		return -1;
 	}
 
-	ret = send_and_recv(global, global->nl, msg, family_handler, &res,
-			    NULL, NULL, NULL);
+	ret = send_and_recv_glb(global, NULL, global->nl, msg, family_handler,
+				&res, NULL, NULL, NULL);
 	if (ret == 0)
 		ret = res.id;
 	return ret;
@@ -852,7 +866,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
+	ret = send_and_recv(drv, w->nl_beacons, msg, NULL, NULL,
 			    NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
@@ -1109,7 +1123,7 @@
 	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
 		struct nl_msg *msg;
 
-		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+		msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
 		if (send_and_recv_resp(drv, msg, get_mlo_info,
 				       &drv->sta_mlo_info))
 			return -1;
@@ -1220,7 +1234,8 @@
 		nl80211_check_global(drv->global);
 		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
 			   "interface");
-		if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+		if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL,
+						       WPA_P2P_MODE_WFD_R1) < 0)
 			return -1;
 		return 1;
 	}
@@ -1841,7 +1856,7 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
 	return send_and_recv_resp(drv, msg, get_channel_info, ci);
 }
 
@@ -2290,7 +2305,8 @@
 static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
 					  void *global_priv, int hostapd,
 					  const u8 *set_addr,
-					  const char *driver_params)
+					  const char *driver_params,
+					  enum wpa_p2p_mode p2p_mode)
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
@@ -2345,15 +2361,14 @@
 	bss->ctx = ctx;
 
 	os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
-	drv->monitor_ifidx = -1;
-	drv->monitor_sock = -1;
 	drv->eapol_tx_sock = -1;
 	drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
 
 	if (nl80211_init_bss(bss))
 		goto failed;
 
-	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
+	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params,
+					       p2p_mode))
 		goto failed;
 
 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
@@ -2374,9 +2389,7 @@
 				   "nl80211: wifi status sockopt failed: %s",
 				   strerror(errno));
 			drv->data_tx_status = 0;
-			if (!drv->use_monitor)
-				drv->capa.flags &=
-					~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+			drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 		} else {
 			eloop_register_read_sock(
 				drv->eapol_tx_sock,
@@ -2416,10 +2429,11 @@
  * Returns: Pointer to private data, %NULL on failure
  */
 static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
-				      void *global_priv)
+				      void *global_priv,
+				      enum wpa_p2p_mode p2p_mode)
 {
 	return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
-					   NULL);
+					   NULL, p2p_mode);
 }
 
 
@@ -2447,7 +2461,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
+	ret = send_and_recv(drv, nl_handle, msg, NULL, NULL,
 			    NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
@@ -2727,7 +2741,7 @@
 	int ret;
 
 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
-	ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
+	ret = send_and_recv(bss->drv, bss->nl_mgmt, msg, NULL, NULL,
 			    NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
@@ -2774,6 +2788,9 @@
 	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
 		ret = -1;
 #endif /* CONFIG_FST */
+	/* Vendor-specific Protected */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x7e", 1) < 0)
+		ret = -1;
 	/* Vendor-specific */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
 		ret = -1;
@@ -2976,10 +2993,67 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_set_p2p_mode(void *priv, enum wpa_p2p_mode mode)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret;
+	enum qca_wlan_vendor_p2p_mode drv_mode;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set P2P mode: %d", mode);
+
+	switch (mode) {
+	case WPA_P2P_MODE_WFD_R1:
+		drv_mode = QCA_P2P_MODE_WFD_R1;
+		break;
+	case WPA_P2P_MODE_WFD_R2:
+		drv_mode = QCA_P2P_MODE_WFD_R2;
+		break;
+	case WPA_P2P_MODE_WFD_PCC:
+		drv_mode = QCA_P2P_MODE_WFD_PCC;
+		break;
+	default:
+		return -1;
+	}
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE))
+		goto fail;
+
+	container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!container)
+		goto fail;
+
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG, drv_mode))
+		goto fail;
+
+	nla_nest_end(msg, container);
+
+	ret = send_and_recv_cmd(drv, msg);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set P2P Mode: ret=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
 				   const u8 *set_addr, int first,
-				   const char *driver_params)
+				   const char *driver_params,
+				   enum wpa_p2p_mode p2p_mode)
 {
 	struct i802_bss *bss = drv->first_bss;
 	int send_rfkill_event = 0;
@@ -3038,6 +3112,11 @@
 
 	wpa_driver_nl80211_drv_init_rfkill(drv);
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (nlmode == NL80211_IFTYPE_P2P_GO)
+		nl80211_set_p2p_mode(bss, p2p_mode);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
 	if (!rfkill_is_blocked(drv->rfkill)) {
 		int ret = i802_set_iface_flags(bss, 1);
 		if (ret) {
@@ -3171,8 +3250,6 @@
 				   bss->brname, strerror(errno));
 	}
 
-	nl80211_remove_monitor_interface(drv);
-
 	if (is_ap_interface(drv->nlmode)) {
 		wpa_driver_nl80211_del_beacon_all(bss);
 		nl80211_remove_links(bss);
@@ -3359,7 +3436,6 @@
 	__AKM(FT_SAE, FT_SAE);
 	__AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY);
 	__AKM(CCKM, CCKM);
-	__AKM(OSEN, OSEN);
 	__AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B);
 	__AKM(IEEE8021X_SUITE_B_192, 802_1X_SUITE_B_192);
 	__AKM(FILS_SHA256, FILS_SHA256);
@@ -3560,6 +3636,14 @@
 		return 0;
 	}
 
+	if (key_flag & KEY_FLAG_NEXT) {
+		/* For now, ignore these since this needs support from the
+		 * driver to handle the special cases of two active RX keys. */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: set_key for the next TK for RX-only - ignored");
+		return -EOPNOTSUPP;
+	}
+
 	ret = -ENOBUFS;
 	key_msg = nlmsg_alloc();
 	if (!key_msg)
@@ -3860,7 +3944,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, bss->nl_connect, msg,
+	ret = send_and_recv(drv, bss->nl_connect, msg,
 			    NULL, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -4027,6 +4111,33 @@
 }
 
 
+static int
+nl80211_put_bss_membership_selectors(struct wpa_driver_nl80211_data *drv,
+				     struct nl_msg *msg)
+{
+	u8 selectors[ARRAY_SIZE(drv->extra_bss_membership_selectors) + 1];
+	size_t selectors_len;
+
+	if (!nl80211_attr_supported(drv, NL80211_ATTR_SUPPORTED_SELECTORS))
+		return 0;
+
+	for (selectors_len = 0;
+	     drv->extra_bss_membership_selectors[selectors_len];
+	     selectors_len++) {
+		selectors[selectors_len] =
+			drv->extra_bss_membership_selectors[selectors_len];
+	}
+
+#ifdef CONFIG_SAE
+	/* Always add the SAE H2E selector as it is handled by wpa_supplicant */
+	selectors[selectors_len++] = BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+#endif /* CONFIG_SAE */
+
+	return nla_put(msg, NL80211_ATTR_SUPPORTED_SELECTORS,
+		       selectors_len, selectors);
+}
+
+
 static int wpa_driver_nl80211_authenticate(
 	struct i802_bss *bss, struct wpa_driver_auth_params *params)
 {
@@ -4062,7 +4173,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
 		   drv->ifindex);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_AUTHENTICATE);
 	if (!msg)
 		goto fail;
 
@@ -4128,6 +4239,10 @@
 			goto fail;
 	}
 
+	ret = nl80211_put_bss_membership_selectors(drv, msg);
+	if (ret)
+		goto fail;
+
 	if (params->mld && params->ap_mld_addr) {
 		wpa_printf(MSG_DEBUG, "  * MLD: link_id=%u, MLD addr=" MACSTR,
 			   params->mld_link_id, MAC2STR(params->ap_mld_addr));
@@ -4332,7 +4447,6 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt *mgmt;
-	int encrypt = !no_encrypt;
 	u16 fc;
 	int use_cookie = 1;
 	int res;
@@ -4381,21 +4495,8 @@
 		goto send_frame_cmd;
 	}
 
-	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
-		/*
-		 * Only one of the authentication frame types is encrypted.
-		 * In order for static WEP encryption to work properly (i.e.,
-		 * to not encrypt the frame), we need to tell mac80211 about
-		 * the frames that must not be encrypted.
-		 */
-		u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
-		u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
-		if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
-			encrypt = 0;
-	}
-
-	if (is_sta_interface(drv->nlmode) &&
+	if ((is_sta_interface(drv->nlmode) ||
+	     drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
 	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
 	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
 		if (freq == 0 &&
@@ -4442,29 +4543,17 @@
 		freq = link->freq;
 	}
 
-	if (drv->use_monitor && is_ap_interface(drv->nlmode)) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
-			   freq, link->freq);
-		return nl80211_send_monitor(drv, data, data_len, encrypt,
-					    noack);
-	}
-
 	if ((noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
 	     WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) &&
 	    link_id == NL80211_DRV_LINK_ID_NA)
 		use_cookie = 0;
 send_frame_cmd:
 #ifdef CONFIG_TESTING_OPTIONS
-	if (no_encrypt && !encrypt && !drv->use_monitor) {
+	if (no_encrypt) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Request to send an unencrypted frame - use a monitor interface for this");
-		if (nl80211_create_monitor_interface(drv) < 0)
-			return -1;
-		res = nl80211_send_monitor(drv, data, data_len, encrypt,
-					   noack);
-		nl80211_remove_monitor_interface(drv);
-		return res;
+		return nl80211_send_monitor(drv, data, data_len, !no_encrypt,
+					    noack);
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
@@ -4567,7 +4656,7 @@
 		nla_total_size(acl_nla_sz);
 	nlmsg_sz = nlmsg_total_size(nla_sz);
 	if (!(msg = nl80211_ifindex_msg_build(drv, nlmsg_alloc_size(nlmsg_sz),
-					      drv->ifindex, 0,
+					      bss->ifindex, 0,
 					      NL80211_CMD_SET_MAC_ACL)) ||
 	    nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
 			NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
@@ -4621,7 +4710,7 @@
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MESH_CONFIG);
 	if (!msg)
 		return -1;
 
@@ -4810,7 +4899,7 @@
 {
 	u8 sae_pwe;
 
-	wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
+	wpa_printf(MSG_DEBUG, "nl80211: sae_pwe=%d", pwe);
 	if (pwe == SAE_PWE_HUNT_AND_PECK)
 		sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
 	else if (pwe == SAE_PWE_HASH_TO_ELEMENT)
@@ -5164,8 +5253,7 @@
 		   beacon_set);
 	if (beacon_set)
 		cmd = NL80211_CMD_SET_BEACON;
-	else if (!drv->device_ap_sme && !drv->use_monitor &&
-		 !nl80211_get_wiphy_data_ap(bss))
+	else if (!drv->device_ap_sme && !nl80211_get_wiphy_data_ap(bss))
 		return -ENOBUFS;
 
 	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
@@ -5506,7 +5594,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		goto fail;
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -5577,7 +5665,7 @@
 		   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 :
+	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
 			      NL80211_CMD_SET_WIPHY);
 	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
 		nlmsg_free(msg);
@@ -5623,6 +5711,8 @@
 		f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
 	if (flags & WPA_STA_ASSOCIATED)
 		f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+	if (flags & WPA_STA_SPP_AMSDU)
+		f |= BIT(NL80211_STA_FLAG_SPP_AMSDU);
 
 	return f;
 }
@@ -5763,6 +5853,15 @@
 				goto fail;
 		}
 
+		/* Set EML capabilities of ML STA */
+		if (params->mld_link_addr && params->eml_cap) {
+			wpa_printf(MSG_DEBUG, "  * eml_cap =%u",
+				   params->eml_cap);
+			if (nla_put_u16(msg, NL80211_ATTR_EML_CAPABILITY,
+					params->eml_cap))
+				goto fail;
+		}
+
 		if (is_ap_interface(drv->nlmode) &&
 		    nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS,
 			       params->support_p2p_ps ?
@@ -6146,19 +6245,8 @@
 	    nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
 		goto fail;
 
-	if (iftype == NL80211_IFTYPE_MONITOR) {
-		struct nlattr *flags;
-
-		flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
-		if (!flags ||
-		    nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
-			goto fail;
-
-		nla_nest_end(msg, flags);
-	} else if (wds) {
-		if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
-			goto fail;
-	}
+	if (wds && nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
+		goto fail;
 
 	/*
 	 * Tell cfg80211 that the interface belongs to the socket that created
@@ -6266,8 +6354,8 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
-		   bss->ifname, drv->device_ap_sme, drv->use_monitor);
+	wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d",
+		   bss->ifname, drv->device_ap_sme);
 
 	/*
 	 * Disable Probe Request reporting unless we need it in this way for
@@ -6277,20 +6365,13 @@
 	if (!drv->device_ap_sme)
 		wpa_driver_nl80211_probe_req_report(bss, 0);
 
-	if (!drv->device_ap_sme && !drv->use_monitor)
-		if (nl80211_mgmt_subscribe_ap(bss))
-			return -1;
-
-	if (drv->device_ap_sme && !drv->use_monitor)
-		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
-			wpa_printf(MSG_DEBUG,
-				   "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
-
-	if (!drv->device_ap_sme && drv->use_monitor &&
-	    nl80211_create_monitor_interface(drv) &&
-	    !drv->device_ap_sme)
+	if (!drv->device_ap_sme && nl80211_mgmt_subscribe_ap(bss))
 		return -1;
 
+	if (drv->device_ap_sme && nl80211_mgmt_subscribe_ap_dev_sme(bss))
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
+
 	if (drv->device_ap_sme &&
 	    wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
@@ -6306,16 +6387,14 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d",
-		   bss->ifname, drv->device_ap_sme, drv->use_monitor);
+	wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d",
+		   bss->ifname, drv->device_ap_sme);
 	if (drv->device_ap_sme) {
 		wpa_driver_nl80211_probe_req_report(bss, 0);
-		if (!drv->use_monitor)
-			nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
-	} else if (drv->use_monitor)
-		nl80211_remove_monitor_interface(drv);
-	else
+		nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+	} else {
 		nl80211_mgmt_unsubscribe(bss, "AP teardown");
+	}
 
 	nl80211_put_wiphy_data_ap(bss);
 	if (bss->flink)
@@ -6353,7 +6432,7 @@
 
 	os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
 	ext_arg.ext_data = &cookie;
-	ret = send_and_recv(bss->drv->global, bss->drv->global->nl, msg,
+	ret = send_and_recv(bss->drv, bss->drv->global->nl, msg,
 			    NULL, NULL, ack_handler_cookie, &ext_arg, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
@@ -6401,8 +6480,6 @@
 }
 
 
-static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
 static int wpa_driver_nl80211_hapd_send_eapol(
 	void *priv, const u8 *addr, const u8 *data,
 	size_t data_len, int encrypt, const u8 *own_addr, u32 flags,
@@ -6410,11 +6487,6 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct ieee80211_hdr *hdr;
-	size_t len;
-	u8 *pos;
-	int res;
-	int qos = flags & WPA_STA_WMM;
 
 	/* For now, disable EAPOL TX over control port in AP mode by default
 	 * since it does not provide TX status notifications. */
@@ -6424,55 +6496,7 @@
 					       data, data_len, !encrypt,
 					       link_id);
 
-	if (drv->device_ap_sme || !drv->use_monitor)
-		return nl80211_send_eapol_data(bss, addr, data, data_len);
-
-	len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
-		data_len;
-	hdr = os_zalloc(len);
-	if (hdr == NULL) {
-		wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
-			   (unsigned long) len);
-		return -1;
-	}
-
-	hdr->frame_control =
-		IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
-	hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
-	if (encrypt)
-		hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-	if (qos) {
-		hdr->frame_control |=
-			host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
-	}
-
-	memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
-	memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
-	memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
-	pos = (u8 *) (hdr + 1);
-
-	if (qos) {
-		/* Set highest priority in QoS header */
-		pos[0] = 7;
-		pos[1] = 0;
-		pos += 2;
-	}
-
-	memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
-	pos += sizeof(rfc1042_header);
-	WPA_PUT_BE16(pos, ETH_P_PAE);
-	pos += 2;
-	memcpy(pos, data, data_len);
-
-	res = nl80211_send_monitor(drv, hdr, len, encrypt, 0);
-	if (res < 0) {
-		wpa_printf(MSG_ERROR,
-			   "hapd_send_eapol - packet len: %lu - failed",
-			   (unsigned long) len);
-	}
-	os_free(hdr);
-
-	return res;
+	return nl80211_send_eapol_data(bss, addr, data, data_len);
 }
 
 
@@ -6570,16 +6594,13 @@
 		nlmode = NL80211_IFTYPE_AP;
 
 	old_mode = drv->nlmode;
-	if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
-		nl80211_remove_monitor_interface(drv);
+	if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode))
 		return -1;
-	}
 
 	if (params->freq.freq &&
 	    nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
 		if (old_mode != nlmode)
 			wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
-		nl80211_remove_monitor_interface(drv);
 		return -1;
 	}
 
@@ -6594,7 +6615,7 @@
 	int ret;
 
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
-	ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+	ret = send_and_recv(drv, drv->first_bss->nl_connect, msg, NULL,
 			    NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
@@ -6744,7 +6765,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		goto fail;
-	ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+	ret = send_and_recv(drv, drv->first_bss->nl_connect, msg, NULL,
 			    NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -7001,7 +7022,6 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
-	    params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
@@ -7059,9 +7079,6 @@
 		case WPA_KEY_MGMT_PSK_SHA256:
 			mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256;
 			break;
-		case WPA_KEY_MGMT_OSEN:
-			mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN;
-			break;
 		case WPA_KEY_MGMT_SAE:
 			mgmt[0] = RSN_AUTH_KEY_MGMT_SAE;
 			break;
@@ -7217,6 +7234,12 @@
 	    nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT))
 		return -1;
 
+	if (params->spp_amsdu) {
+		wpa_printf(MSG_DEBUG, "  * SPP A-MSDU");
+		if (nla_put_flag(msg, NL80211_ATTR_ASSOC_SPP_AMSDU))
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -7298,7 +7321,7 @@
 
 	nl80211_connect_ext(drv, params);
 	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONNECT);
 	if (!msg)
 		return -1;
 
@@ -7375,7 +7398,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		goto fail;
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -7434,7 +7457,7 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct nl80211_err_info err_info;
+	struct nl80211_err_info err_info = { -1 };
 	int ret = -1;
 	struct nl_msg *msg;
 
@@ -7460,7 +7483,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
 		   drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ASSOCIATE);
 	if (!msg)
 		return -1;
 
@@ -7468,6 +7491,10 @@
 	if (ret)
 		goto fail;
 
+	ret = nl80211_put_bss_membership_selectors(drv, msg);
+	if (ret)
+		goto fail;
+
 	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
 	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
 		goto fail;
@@ -7491,7 +7518,7 @@
 	if (!TEST_FAIL_TAG("assoc")) {
 		if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 			goto fail;
-		ret = send_and_recv(drv->global, drv->first_bss->nl_connect,
+		ret = send_and_recv(drv, drv->first_bss->nl_connect,
 				    msg, NULL, NULL, NULL, NULL, &err_info);
 		msg = NULL;
 	} else {
@@ -7906,7 +7933,7 @@
 	else
 		val = rts;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) ||
 	    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -7934,7 +7961,7 @@
 	else
 		val = frag;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) ||
 	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -7967,6 +7994,8 @@
 	 * XXX: FIX! this needs to flush all VLANs too
 	 */
 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+	if (!msg)
+		goto fail;
 	if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
 	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
 		goto fail;
@@ -8796,7 +8825,8 @@
 
 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
 					  params->global_priv, 1,
-					  params->bssid, params->driver_params);
+					  params->bssid, params->driver_params,
+					  WPA_P2P_MODE_WFD_R1);
 	if (bss == NULL)
 		return NULL;
 
@@ -9330,7 +9360,7 @@
 					  const u8 *dst, const u8 *src,
 					  const u8 *bssid,
 					  const u8 *data, size_t data_len,
-					  int no_cck)
+					  int no_cck, int link_id)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ret = -1;
@@ -9347,9 +9377,9 @@
 
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: Send Action frame (ifindex=%d, freq=%u MHz wait=%d ms no_cck=%d offchanok=%d dst="
-		   MACSTR " src=" MACSTR " bssid=" MACSTR ")",
+		   MACSTR " src=" MACSTR " bssid=" MACSTR ", link_id=%d)",
 		   drv->ifindex, freq, wait_time, no_cck, offchanok,
-		   MAC2STR(dst), MAC2STR(src), MAC2STR(bssid));
+		   MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), link_id);
 
 	buf = os_zalloc(24 + data_len);
 	if (buf == NULL)
@@ -9392,18 +9422,15 @@
 	}
 #endif /* CONFIG_MESH */
 
-	if (is_ap_interface(drv->nlmode) &&
-	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
-	     (int) freq == bss->flink->freq || drv->device_ap_sme ||
-	     !drv->use_monitor))
+	if (is_ap_interface(drv->nlmode))
 		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
 						   0, freq, no_cck, offchanok,
-						   wait_time, NULL, 0, 0, -1);
+						   wait_time, NULL, 0, 0,
+						   link_id);
 	else
 		ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
 					     24 + data_len, 1, no_cck, 0,
-					     offchanok, NULL, 0,
-					     NL80211_DRV_LINK_ID_NA);
+					     offchanok, NULL, 0, link_id);
 
 	os_free(buf);
 	return ret;
@@ -10060,6 +10087,7 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	const char *pos;
 
 	if (param == NULL)
 		return 0;
@@ -10074,12 +10102,14 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (os_strstr(param, "use_monitor=1"))
-		drv->use_monitor = 1;
-
 	if (os_strstr(param, "force_connect_cmd=1")) {
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
 		drv->force_connect_cmd = 1;
+		/*
+		 * mac80211_hwsim does not implement SPP A-MSDU in
+		 * offload mode.
+		 */
+		drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_SPP_AMSDU;
 	}
 
 	if (os_strstr(param, "force_bss_selection=1"))
@@ -10128,6 +10158,33 @@
 	if (os_strstr(param, "rsn_override_in_driver=1"))
 		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA;
 
+	pos = os_strstr(param, "extra_bss_membership_selectors=");
+	if (pos) {
+		int i = 0;
+
+		pos += 31;
+
+		while (*pos) {
+			char *end;
+			int sel;
+
+			sel = strtol(pos, &end, 10);
+			if (pos == end)
+				return -EINVAL;
+
+			if (sel > 127 || sel < 0)
+				return -EINVAL;
+			if (i ==
+			    ARRAY_SIZE(drv->extra_bss_membership_selectors))
+				return -EINVAL;
+			drv->extra_bss_membership_selectors[i++] = sel;
+
+			pos = end;
+			if (*pos == ',')
+				pos++;
+		}
+	}
+
 	return 0;
 }
 
@@ -10486,7 +10543,7 @@
 
 	dl_list_init(&survey_results->survey_list);
 
-	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
 	if (!msg)
 		return -ENOBUFS;
 
@@ -10683,15 +10740,29 @@
 		return -1;
 	}
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_RADAR_DETECT)) ||
 	    nl80211_put_freq_params(msg, freq) < 0) {
 		nlmsg_free(msg);
 		return -1;
 	}
 
+	if (nl80211_link_valid(bss->valid_links, freq->link_id)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Radar detection (CAC) on link_id=%d",
+			   freq->link_id);
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
+			nlmsg_free(msg);
+			return -1;
+		}
+	}
+
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret == 0)
+	if (ret == 0) {
+		nl80211_link_set_freq(bss, freq->link_id, freq->freq);
 		return 0;
+	}
+
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
 		   "%d (%s)", ret, strerror(-ret));
 	return -1;
@@ -10779,7 +10850,7 @@
 	if (link_id < 0 && drv->sta_mlo_info.valid_links)
 		link_id = drv->sta_mlo_info.assoc_link_id;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_MGMT)) ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
 	    nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
 	    nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
@@ -10834,7 +10905,7 @@
 		return -EINVAL;
 	}
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_OPER)) ||
 	    nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
 		nlmsg_free(msg);
@@ -10977,8 +11048,7 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ret;
 
-	if (type != WPA_IF_AP_BSS ||
-	    !nl80211_link_valid(bss->valid_links, link_id))
+	if (type != WPA_IF_AP_BSS)
 		return -1;
 
 	wpa_printf(MSG_DEBUG,
@@ -11099,11 +11169,12 @@
 				      const u8 *dst, const u8 *src,
 				      const u8 *bssid,
 				      const u8 *data, size_t data_len,
-				      int no_cck)
+				      int no_cck, int link_id)
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
-					      bssid, data, data_len, no_cck);
+					      bssid, data, data_len, no_cck,
+					      link_id);
 }
 
 
@@ -11124,7 +11195,7 @@
 	u16 mdid = WPA_GET_LE16(md);
 
 	wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_FT_IES)) ||
 	    nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
 	    nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
 		nlmsg_free(msg);
@@ -11262,12 +11333,9 @@
 			  "prev_bssid=" MACSTR "\n"
 			  "associated=%d\n"
 			  "assoc_freq=%u\n"
-			  "monitor_sock=%d\n"
-			  "monitor_ifidx=%d\n"
-			  "monitor_refcount=%d\n"
 			  "last_mgmt_freq=%u\n"
 			  "eapol_tx_sock=%d\n"
-			  "%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",
 			  drv->phyname,
 			  MAC2STR(drv->perm_addr),
 			  drv->ifindex,
@@ -11279,9 +11347,6 @@
 			  MAC2STR(drv->prev_bssid),
 			  drv->associated,
 			  drv->assoc_freq,
-			  drv->monitor_sock,
-			  drv->monitor_ifidx,
-			  drv->monitor_refcount,
 			  drv->last_mgmt_freq,
 			  drv->eapol_tx_sock,
 			  drv->ignore_if_down_event ?
@@ -11299,7 +11364,6 @@
 			  drv->data_tx_status ? "data_tx_status=1\n" : "",
 			  drv->scan_for_auth ? "scan_for_auth=1\n" : "",
 			  drv->retry_auth ? "retry_auth=1\n" : "",
-			  drv->use_monitor ? "use_monitor=1\n" : "",
 			  drv->ignore_next_local_disconnect ?
 			  "ignore_next_local_disconnect\n" : "",
 			  drv->ignore_next_local_deauth ?
@@ -11373,7 +11437,8 @@
 				  "capa.max_sched_scan_plan_interval=%u\n"
 				  "capa.max_sched_scan_plan_iterations=%u\n"
 				  "capa.mbssid_max_interfaces=%u\n"
-				  "capa.ema_max_periodicity=%u\n",
+				  "capa.ema_max_periodicity=%u\n"
+				  "capa.max_probe_req_ie_len=%zu\n",
 				  drv->capa.key_mgmt,
 				  drv->capa.enc,
 				  drv->capa.auth,
@@ -11398,7 +11463,8 @@
 				  drv->capa.max_sched_scan_plan_interval,
 				  drv->capa.max_sched_scan_plan_iterations,
 				  drv->capa.mbssid_max_interfaces,
-				  drv->capa.ema_max_periodicity);
+				  drv->capa.ema_max_periodicity,
+				  drv->capa.max_probe_req_ie_len);
 		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
@@ -11470,7 +11536,7 @@
 		   settings->freq_params.bandwidth,
 		   settings->freq_params.center_freq1,
 		   settings->freq_params.center_freq2,
-		   settings->punct_bitmap,
+		   settings->freq_params.punct_bitmap,
 		   settings->link_id,
 		   settings->freq_params.ht_enabled ? " ht" : "",
 		   settings->freq_params.vht_enabled ? " vht" : "",
@@ -11543,9 +11609,9 @@
 	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
 	    (settings->block_tx &&
 	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
-	    (settings->punct_bitmap &&
+	    (settings->freq_params.punct_bitmap &&
 	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
-			 settings->punct_bitmap)) ||
+			 settings->freq_params.punct_bitmap)) ||
 	    (settings->link_id != NL80211_DRV_LINK_ID_NA &&
 	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->link_id)))
 		goto error;
@@ -11855,7 +11921,7 @@
 		/* This test vendor_cmd can be used with nl80211 commands that
 		 * need the connect nl_sock, so use the variant that takes in
 		 * bss->nl_connect as the handle. */
-		ret = send_and_recv(drv->global, bss->nl_connect, msg,
+		ret = send_and_recv(drv, bss->nl_connect, msg,
 				    cmd_reply_handler, buf, NULL, NULL, NULL);
 		if (ret)
 			wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
@@ -11942,7 +12008,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_WOWLAN);
 
 	ret = send_and_recv_resp(drv, msg, get_wowlan_handler, &wowlan_enabled);
 	if (ret) {
@@ -12015,7 +12081,7 @@
 		return -1;
 	}
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
@@ -12046,7 +12112,7 @@
 	if (!drv->set_wifi_conf_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
@@ -12081,7 +12147,7 @@
 	if (!drv->roam_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
@@ -12135,7 +12201,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Add STA node");
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE) ||
@@ -12319,7 +12385,7 @@
 	int ret = -1;
 
 	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_JOIN_MESH);
 	if (!msg ||
 	    nl80211_put_freq_params(msg, &params->freq) ||
 	    nl80211_put_basic_rates(msg, params->basic_rates) ||
@@ -12365,7 +12431,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		return -1;
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -12422,8 +12488,8 @@
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_LEAVE_MESH);
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
@@ -12452,7 +12518,7 @@
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_PROBE_MESH_LINK);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_MESH_LINK);
 	if (!msg ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
 	    nla_put(msg, NL80211_ATTR_FRAME, len, eth)) {
@@ -12889,7 +12955,7 @@
 		   "nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d",
 		   qca_band_mask, qca_band_value);
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
@@ -13086,7 +13152,7 @@
 	param.num = *num;
 	param.freq_list = freq_list;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13140,7 +13206,7 @@
 		   "nl80211: Set P2P probable operating freq %u for ifindex %d",
 		   freq, bss->ifindex);
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
@@ -13190,7 +13256,7 @@
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
@@ -13242,7 +13308,7 @@
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
@@ -13273,7 +13339,7 @@
 	else
 		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
@@ -13410,7 +13476,7 @@
 
 
 static struct wpa_bss_candidate_info *
-nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+qca_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -13443,7 +13509,7 @@
 	 */
 	info->num = params->n_candidates;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
@@ -13517,7 +13583,7 @@
 	if (!drv->set_wifi_conf_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
@@ -13565,7 +13631,7 @@
 	wpa_dbg(drv->ctx, MSG_DEBUG,
 		"nl80211: PASN authentication response for %d entries",
 		params->num_peers);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13648,7 +13714,7 @@
 		"nl80211: Secure ranging context for " MACSTR,
 		MAC2STR(params->peer_addr));
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13727,7 +13793,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD flush");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13743,12 +13809,10 @@
 	nla_nest_end(msg, container);
 
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD flush");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13774,7 +13838,7 @@
 		   params->freq, params->ttl);
 	wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13807,16 +13871,15 @@
 			params->freq) ||
 	    add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST,
 			  params->freq_list))
+		goto fail;
 	nla_nest_end(msg, attr);
 
 	nla_nest_end(msg, container);
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD publish");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13834,7 +13897,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel publish");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13854,12 +13917,10 @@
 	nla_nest_end(msg, container);
 
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD cancel publish");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13879,7 +13940,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD update publish: id=%d",
 		   publish_id);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13900,12 +13961,10 @@
 
 	nla_nest_end(msg, container);
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD update publish");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13931,7 +13990,7 @@
 		   params->freq, params->ttl);
 	wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13969,12 +14028,10 @@
 
 	nla_nest_end(msg, container);
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD subscribe");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13992,7 +14049,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel subscribe");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -14010,12 +14067,10 @@
 	nla_nest_end(msg, container);
 
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD cancel subscribe");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -14103,6 +14158,60 @@
 }
 
 
+#ifdef CONFIG_MBO
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+	/* This only exists for testing purposes, disable unless requested */
+	if (TEST_FAIL_TAG("simulate")) {
+		struct wpa_bss_candidate_info *info;
+		int i;
+
+		info = os_zalloc(sizeof(*info));
+		if (!info)
+			return NULL;
+
+		info->candidates = os_calloc(params->n_candidates,
+					     sizeof(*info->candidates));
+		if (!info->candidates) {
+			os_free(info);
+			return NULL;
+		}
+
+		info->num = params->n_candidates;
+		for (i = 0; i < params->n_candidates; i++) {
+			char bssid_str[ETH_ALEN * 3];
+
+			os_memcpy(info->candidates[i].bssid,
+				  &params->bssid[i * ETH_ALEN], ETH_ALEN);
+
+			os_snprintf(bssid_str, sizeof(bssid_str), MACSTR,
+				    MAC2STR(info->candidates[i].bssid));
+
+			if (TEST_FAIL_TAG(bssid_str)) {
+				info->candidates[i].is_accept = 0;
+				info->candidates[i].reject_reason =
+					MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+			} else {
+				info->candidates[i].is_accept = 1;
+				info->candidates[i].reject_reason = 0;
+			}
+		}
+
+		return info;
+	}
+#endif /* defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	return qca_get_bss_transition_status(priv, params);
+#else /* CONFIG_DRIVER_NL80211_QCA */
+	return NULL;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+}
+#endif /* CONFIG_MBO */
+
+
 static int nl80211_write_to_file(const char *name, unsigned int val)
 {
 	int fd, len;
@@ -14308,7 +14417,7 @@
 	}
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
 	if (!msg)
 		goto fail;
 
@@ -14368,7 +14477,7 @@
 	wpa_dbg(drv->ctx, MSG_DEBUG,
 		"nl80211: External auth status: %u", params->status);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_EXTERNAL_AUTH);
 	if (!msg ||
 	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
 	    (params->ssid && params->ssid_len &&
@@ -14565,6 +14674,7 @@
 
 
 #ifdef CONFIG_IEEE80211BE
+
 static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
 					      const u8 *addr)
 {
@@ -14591,6 +14701,124 @@
 
 	return ret;
 }
+
+
+static int wpa_driver_get_wiphy_name_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpa_driver_nl80211_data *drv = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_WIPHY_NAME])
+		return NL_SKIP;
+
+	os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+		   sizeof(drv->phyname));
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_get_phyname(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	u32 nl_flags = 0;
+	u32 feat = get_nl80211_protocol_features(drv);
+
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		nl_flags = NLM_F_DUMP;
+
+	if (!(msg = nl80211_cmd_msg(drv->first_bss, nl_flags,
+				    NL80211_CMD_GET_WIPHY)) ||
+	    nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	if (send_and_recv_resp(drv, msg, wpa_driver_get_wiphy_name_handler,
+			       drv))
+		return -1;
+
+	return 0;
+}
+
+
+static bool
+wpa_driver_nl80211_name_match(struct wpa_driver_nl80211_data *drv,
+			      struct wpa_driver_nl80211_data **match_drv)
+{
+	struct wpa_driver_nl80211_data *drv2;
+
+	dl_list_for_each(drv2, &drv->global->interfaces,
+			 struct wpa_driver_nl80211_data, list) {
+		if (os_strcmp(drv2->phyname, drv->phyname) == 0) {
+			if (match_drv)
+				*match_drv = drv2;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+
+static bool wpa_driver_nl80211_can_share_drv(void *ctx,
+					     struct wpa_init_params *params,
+					     void **hapd)
+{
+	struct wpa_driver_nl80211_data *drv, *match_drv = NULL;
+	struct i802_bss *bss;
+	bool ret = false;
+
+	if (!params->global_priv)
+		return false;
+
+	/* Create a temporary drv to read the phyname */
+	drv = os_zalloc(sizeof(*drv));
+	if (!drv)
+		return false;
+	drv->global = params->global_priv;
+	drv->ctx = ctx;
+
+	drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
+	if (!drv->first_bss) {
+		os_free(drv);
+		return false;
+	}
+
+	bss = drv->first_bss;
+	bss->drv = drv;
+	bss->ctx = ctx;
+
+	os_strlcpy(bss->ifname, params->ifname, sizeof(bss->ifname));
+
+	if (nl80211_init_bss(bss))
+		goto free_all;
+
+	drv->ifindex = if_nametoindex(bss->ifname);
+	bss->ifindex = drv->ifindex;
+
+	if (wpa_driver_get_phyname(drv) ||
+	    !wpa_driver_nl80211_name_match(drv, &match_drv) ||
+	    !match_drv)
+		goto free_all;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Driver for phy %s already exist",
+		   match_drv->phyname);
+
+	*hapd = match_drv->first_bss->ctx;
+	ret = true;
+
+free_all:
+	nl80211_destroy_bss(bss);
+	os_free(bss);
+	os_free(drv);
+	return ret;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
@@ -14775,7 +15003,6 @@
 	.set_default_scan_ies = nl80211_set_default_scan_ies,
 	.set_tdls_mode = nl80211_set_tdls_mode,
 #ifdef CONFIG_MBO
-	.get_bss_transition_status = nl80211_get_bss_transition_status,
 	.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
 #endif /* CONFIG_MBO */
 	.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
@@ -14794,6 +15021,9 @@
 #endif /* CONFIG_NAN_USD */
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.do_acs = nl80211_do_acs,
+#ifdef CONFIG_MBO
+	.get_bss_transition_status = nl80211_get_bss_transition_status,
+#endif /* CONFIG_MBO */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
 	.get_ext_capab = nl80211_get_ext_capab,
 	.get_mld_capab = nl80211_get_mld_capab,
@@ -14809,6 +15039,7 @@
 	.link_remove = driver_nl80211_link_remove,
 	.is_drv_shared = nl80211_is_drv_shared,
 	.link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+	.can_share_drv = wpa_driver_nl80211_can_share_drv,
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_TESTING_OPTIONS
 	.register_frame = testing_nl80211_register_frame,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index bf1bf4e..bf3442a 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -151,10 +151,6 @@
 	enum nl80211_iftype ap_scan_as_station;
 	unsigned int assoc_freq;
 
-	int monitor_sock;
-	int monitor_ifidx;
-	int monitor_refcount;
-
 	unsigned int disabled_11b_rates:1;
 	unsigned int pending_remain_on_chan:1;
 	unsigned int in_interface_list:1;
@@ -163,7 +159,6 @@
 	unsigned int data_tx_status:1;
 	unsigned int scan_for_auth:1;
 	unsigned int retry_auth:1;
-	unsigned int use_monitor:1;
 	unsigned int hostapd:1;
 	unsigned int start_mode_sta:1;
 	unsigned int start_iface_up:1;
@@ -202,6 +197,8 @@
 	unsigned int qca_ap_allowed_freqs:1;
 	unsigned int connect_ext_vendor_cmd_avail:1;
 
+	u8 extra_bss_membership_selectors[8];
+
 	u32 ignore_next_local_disconnect;
 	u32 ignore_next_local_deauth;
 
@@ -284,13 +281,28 @@
 				uint8_t cmd);
 struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
 
-int send_and_recv(struct nl80211_global *global,
-		  struct nl_sock *nl_handle, struct nl_msg *msg,
-		  int (*valid_handler)(struct nl_msg *, void *),
-		  void *valid_data,
-		  int (*ack_handler_custom)(struct nl_msg *, void *),
-		  void *ack_data,
-		  struct nl80211_err_info *err_info);
+int send_and_recv_glb(struct nl80211_global *global,
+		      struct wpa_driver_nl80211_data *drv, /* may be NULL */
+		      struct nl_sock *nl_handle, struct nl_msg *msg,
+		      int (*valid_handler)(struct nl_msg *, void *),
+		      void *valid_data,
+		      int (*ack_handler_custom)(struct nl_msg *, void *),
+		      void *ack_data,
+		      struct nl80211_err_info *err_info);
+
+static inline int
+send_and_recv(struct wpa_driver_nl80211_data *drv,
+	      struct nl_sock *nl_handle, struct nl_msg *msg,
+	      int (*valid_handler)(struct nl_msg *, void *),
+	      void *valid_data,
+	      int (*ack_handler_custom)(struct nl_msg *, void *),
+	      void *ack_data,
+	      struct nl80211_err_info *err_info)
+{
+	return send_and_recv_glb(drv->global, drv, nl_handle, msg,
+				 valid_handler, valid_data,
+				 ack_handler_custom, ack_data, err_info);
+}
 
 // This function is not used in supplicant anymore. But keeping this wrapper
 // functions for libraries outside wpa_supplicant to build (For eg: lib_driver_cmd_XX)
@@ -302,7 +314,7 @@
 		   int (*ack_handler_custom)(struct nl_msg *, void *),
 		   void *ack_data)
 {
-	return send_and_recv(drv->global, drv->global->nl, msg,
+	return send_and_recv(drv, drv->global->nl, msg,
 			     valid_handler, valid_data,
 			     ack_handler_custom, ack_data, NULL);
 }
@@ -311,7 +323,7 @@
 send_and_recv_cmd(struct wpa_driver_nl80211_data *drv,
 		  struct nl_msg *msg)
 {
-	return send_and_recv(drv->global, drv->global->nl, msg,
+	return send_and_recv(drv, drv->global->nl, msg,
 			     NULL, NULL, NULL, NULL, NULL);
 }
 
@@ -321,7 +333,7 @@
 		   int (*valid_handler)(struct nl_msg *, void *),
 		   void *valid_data)
 {
-	return send_and_recv(drv->global, drv->global->nl, msg,
+	return send_and_recv(drv, drv->global->nl, msg,
 			     valid_handler, valid_data, NULL, NULL, NULL);
 }
 
@@ -353,8 +365,6 @@
 			    int local_state_change,
 			    struct i802_bss *bss);
 
-int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
-void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
 int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
 			 const void *data, size_t len,
 			 int encrypt, int noack);
@@ -432,5 +442,6 @@
 int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
 struct hostapd_multi_hw_info *
 nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws);
+u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv);
 
 #endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 58fb71d..1dbfc22 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -35,7 +35,7 @@
 }
 
 
-static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
 {
 	u32 feat = 0;
 	struct nl_msg *msg;
@@ -302,9 +302,6 @@
 		case RSN_AUTH_KEY_MGMT_CCKM:
 			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM;
 			break;
-		case RSN_AUTH_KEY_MGMT_OSEN:
-			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN;
-			break;
 		case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
 			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
 			break;
@@ -719,6 +716,10 @@
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_SAE_OFFLOAD_AP))
 		capa->flags2 |= WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SPP_AMSDU;
 }
 
 
@@ -958,6 +959,10 @@
 		capa->max_scan_ssids =
 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
 
+	if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
+		capa->max_probe_req_ie_len =
+			nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
+
 	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
 		capa->max_sched_scan_ssids =
 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
@@ -1204,6 +1209,10 @@
 	info->capa = &drv->capa;
 	info->drv = drv;
 
+	/* Default to large buffer of extra IE(s) to maintain previous behavior
+	 * if the driver does not support reporting an accurate limit. */
+	info->capa->max_probe_req_ie_len = 1500;
+
 	feat = get_nl80211_protocol_features(drv);
 	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
 		flags = NLM_F_DUMP;
@@ -1463,6 +1472,12 @@
 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD, &info))
 		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_NAN_OFFLOAD;
 
+	if (!check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_V2, &info))
+		drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_P2P_FEATURE_V2;
+
+	if (!check_feature(QCA_WLAN_VENDOR_FEATURE_PCC_MODE, &info))
+		drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE;
+
 	os_free(info.flags);
 }
 
@@ -1577,20 +1592,18 @@
 	drv->have_low_prio_scan = info.have_low_prio_scan;
 
 	/*
-	 * If poll command and tx status are supported, mac80211 is new enough
-	 * to have everything we need to not need monitor interfaces.
-	 */
-	drv->use_monitor = !info.device_ap_sme &&
-		(!info.poll_command_supported || !info.data_tx_status);
-
-	/*
-	 * If we aren't going to use monitor interfaces, but the
-	 * driver doesn't support data TX status, we won't get TX
+	 * If the driver doesn't support data TX status, we won't get TX
 	 * status for EAPOL frames.
 	 */
-	if (!drv->use_monitor && !info.data_tx_status)
+	if (!info.data_tx_status)
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 
+	/* Enable P2P2 and PCC mode capabilities by default for the drivers
+	 * which can't explicitly indicate whether these capabilities are
+	 * supported. */
+	drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_V2;
+	drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE;
+
 #ifdef CONFIG_DRIVER_NL80211_QCA
 	if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
 		qca_nl80211_check_dfs_capa(drv);
@@ -1735,6 +1748,8 @@
 		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
 	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
 		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_320MHZ])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_320;
 
 	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
 		enum nl80211_dfs_state state =
@@ -1836,6 +1851,8 @@
 		[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
 		[NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
 		[NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_320MHZ] = { .type = NLA_FLAG },
+
 	};
 	int new_channels = 0;
 	struct hostapd_channel_data *channel;
@@ -1994,12 +2011,9 @@
 			  len);
 	}
 
-	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) {
-		u16 capa;
-
-		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_HE_6GHZ_CAPA])
+		he_capab->he_6ghz_capa =
+			nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]);
 
 	if (!tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] ||
 	    !tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY])
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index c6b8e72..9b84ceb 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -186,6 +186,8 @@
 	C2S(NL80211_CMD_SET_HW_TIMESTAMP)
 	C2S(NL80211_CMD_LINKS_REMOVED)
 	C2S(NL80211_CMD_SET_TID_TO_LINK_MAPPING)
+	C2S(NL80211_CMD_ASSOC_MLO_RECONF)
+	C2S(NL80211_CMD_EPCS_CFG)
 	C2S(__NL80211_CMD_AFTER_LAST)
 	}
 #undef C2S
@@ -1250,6 +1252,8 @@
 
 	os_memset(&data, 0, sizeof(data));
 	data.ch_switch.freq = nla_get_u32(freq);
+	if (is_6ghz_freq(data.ch_switch.freq))
+		ht_enabled = 0;
 	data.ch_switch.ht_enabled = ht_enabled;
 	data.ch_switch.ch_offset = chan_offset;
 	if (punct_bitmap)
@@ -1719,7 +1723,7 @@
 	}
 
 	if (cmd == NL80211_CMD_FRAME && stype == WLAN_FC_STYPE_AUTH &&
-	    auth_type == host_to_le16(WLAN_AUTH_PASN)) {
+	    auth_type == WLAN_AUTH_PASN) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: %s: Allow PASN frame for foreign address",
 			   bss->ifname);
@@ -2510,29 +2514,62 @@
 }
 
 
-static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
-				struct nlattr **tb)
+static void nl80211_process_radar_event(struct i802_bss *bss,
+					union wpa_event_data *data,
+					enum nl80211_radar_event event_type)
 {
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d",
+		   data->dfs_event.freq, data->dfs_event.ht_enabled,
+		   data->dfs_event.chan_offset, data->dfs_event.chan_width,
+		   data->dfs_event.cf1, data->dfs_event.cf2,
+		   data->dfs_event.link_id);
+
+	switch (event_type) {
+	case NL80211_RADAR_DETECTED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_RADAR_DETECTED, data);
+		break;
+	case NL80211_RADAR_CAC_FINISHED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_FINISHED, data);
+		break;
+	case NL80211_RADAR_CAC_ABORTED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_ABORTED, data);
+		break;
+	case NL80211_RADAR_NOP_FINISHED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_NOP_FINISHED, data);
+		break;
+	case NL80211_RADAR_PRE_CAC_EXPIRED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+				     data);
+		break;
+	case NL80211_RADAR_CAC_STARTED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_STARTED, data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Unknown radar event %d received",
+			   event_type);
+		break;
+	}
+}
+
+
+static void nl80211_radar_event(struct i802_bss *bss, struct nlattr **tb)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 	union wpa_event_data data;
 	enum nl80211_radar_event event_type;
+	struct i802_bss *bss_iter;
+	int i;
+	bool hit = false;
 
 	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
 		return;
 
 	os_memset(&data, 0, sizeof(data));
-	data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA;
 	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
 	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
 
-	if (tb[NL80211_ATTR_MLO_LINK_ID]) {
-		data.dfs_event.link_id =
-			nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
-	} else if (data.dfs_event.freq) {
-		data.dfs_event.link_id =
-			nl80211_get_link_id_by_freq(drv->first_bss,
-						    data.dfs_event.freq);
-	}
-
 	/* Check HT params */
 	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
 		data.dfs_event.ht_enabled = 1;
@@ -2564,37 +2601,62 @@
 		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d",
-		   data.dfs_event.freq, data.dfs_event.ht_enabled,
-		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
-		   data.dfs_event.cf1, data.dfs_event.cf2,
-		   data.dfs_event.link_id);
+		   "nl80211: Checking suitable BSS for the DFS event");
 
-	switch (event_type) {
-	case NL80211_RADAR_DETECTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
-		break;
-	case NL80211_RADAR_CAC_FINISHED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
-		break;
-	case NL80211_RADAR_CAC_ABORTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
-		break;
-	case NL80211_RADAR_NOP_FINISHED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
-		break;
-	case NL80211_RADAR_PRE_CAC_EXPIRED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
-				     &data);
-		break;
-	case NL80211_RADAR_CAC_STARTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
-			   "received", event_type);
-		break;
+	/* It is possible to have the event without ifidx and wdev_id, e.g.,
+	 * with NL80211_RADAR_NOP_FINISHED and NL80211_RADAR_PRE_CAC_EXPIRED.
+	 * Hence need to check on all BSSs. */
+	for (bss_iter = drv->first_bss; bss_iter; bss_iter = bss_iter->next) {
+		/* Find a link match based on the frequency. If
+		 * NL80211_DRV_LINK_ID_NA is returned, either a match was not
+		 * found or the BSS could be operating as a non-MLO. */
+		data.dfs_event.link_id = nl80211_get_link_id_by_freq(
+			bss_iter, data.dfs_event.freq);
+		/* If a link match is found, exit the loop after the handler is
+		 * called */
+		if (data.dfs_event.link_id != NL80211_DRV_LINK_ID_NA)
+			return nl80211_process_radar_event(bss_iter, &data,
+							   event_type);
+		if (data.dfs_event.link_id == NL80211_DRV_LINK_ID_NA) {
+			/* For non-MLO operation, frequency should still match
+			 */
+			if (!bss_iter->valid_links &&
+			    bss_iter->links[0].freq == data.dfs_event.freq)
+				return nl80211_process_radar_event(
+					bss_iter, &data, event_type);
+		}
+
+		/* For event like NL80211_RADAR_NOP_FINISHED, frequency
+		 * information will not match exactly the link frequency. Hence,
+		 * if the link frequency is 5 GHz, pass the event to it.
+		 */
+		for_each_link_default(bss_iter->valid_links, i, 0) {
+			if (bss_iter->links[i].freq < 5180 ||
+			    bss_iter->links[i].freq > 5900)
+				continue;
+
+			data.dfs_event.link_id = bss_iter->valid_links ?
+				i : NL80211_DRV_LINK_ID_NA;
+
+			/* Cannot just return after one match since if split
+			 * hardware is participating in MLO, possibly the event
+			 * is for 5 GHz upper band and this iteration has picked
+			 * a 5 GHz low band link, but 5 GHz freq check will be
+			 * true for both. Hence, iterate on all possible links.
+			 * The handler should take care whether the event is
+			 * actually for it. */
+			nl80211_process_radar_event(bss_iter, &data,
+						    event_type);
+
+			hit = true;
+		}
 	}
+
+	if (hit)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on unknown freq on %s",
+		   bss->ifname);
 }
 
 
@@ -4119,7 +4181,7 @@
 		mlme_event_ft_event(drv, tb);
 		break;
 	case NL80211_CMD_RADAR_DETECT:
-		nl80211_radar_event(drv, tb);
+		nl80211_radar_event(bss, tb);
 		break;
 	case NL80211_CMD_STOP_AP:
 		nl80211_stop_ap(bss, tb);
diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c
index 7ff55f1..ca9bb1e 100644
--- a/src/drivers/driver_nl80211_monitor.c
+++ b/src/drivers/driver_nl80211_monitor.c
@@ -23,259 +23,14 @@
 #include "driver_nl80211.h"
 
 
-static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
-{
-	struct ieee80211_hdr *hdr;
-	u16 fc;
-	union wpa_event_data event;
-
-	hdr = (struct ieee80211_hdr *) buf;
-	fc = le_to_host16(hdr->frame_control);
-
-	os_memset(&event, 0, sizeof(event));
-	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
-	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
-	event.tx_status.dst = hdr->addr1;
-	event.tx_status.data = buf;
-	event.tx_status.data_len = len;
-	event.tx_status.ack = ok;
-	wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
-}
-
-
-static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
-			     u8 *buf, size_t len)
-{
-	struct ieee80211_hdr *hdr = (void *)buf;
-	u16 fc;
-	union wpa_event_data event;
-
-	if (len < sizeof(*hdr))
-		return;
-
-	fc = le_to_host16(hdr->frame_control);
-
-	os_memset(&event, 0, sizeof(event));
-	event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
-	event.rx_from_unknown.addr = hdr->addr2;
-	event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
-		(WLAN_FC_FROMDS | WLAN_FC_TODS);
-	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
-}
-
-
-static void handle_frame(struct wpa_driver_nl80211_data *drv,
-			 u8 *buf, size_t len, int datarate, int ssi_signal)
-{
-	struct ieee80211_hdr *hdr;
-	u16 fc;
-	union wpa_event_data event;
-
-	if (!drv->use_monitor)
-		return;
-
-	hdr = (struct ieee80211_hdr *) buf;
-	fc = le_to_host16(hdr->frame_control);
-
-	switch (WLAN_FC_GET_TYPE(fc)) {
-	case WLAN_FC_TYPE_MGMT:
-		os_memset(&event, 0, sizeof(event));
-		event.rx_mgmt.frame = buf;
-		event.rx_mgmt.frame_len = len;
-		event.rx_mgmt.datarate = datarate;
-		event.rx_mgmt.ssi_signal = ssi_signal;
-		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-		break;
-	case WLAN_FC_TYPE_CTRL:
-		/* can only get here with PS-Poll frames */
-		wpa_printf(MSG_DEBUG, "CTRL");
-		from_unknown_sta(drv, buf, len);
-		break;
-	case WLAN_FC_TYPE_DATA:
-		from_unknown_sta(drv, buf, len);
-		break;
-	}
-}
-
-
-static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
-{
-	struct wpa_driver_nl80211_data *drv = eloop_ctx;
-	int len;
-	unsigned char buf[3000];
-	struct ieee80211_radiotap_iterator iter;
-	int ret;
-	int datarate = 0, ssi_signal = 0;
-	int injected = 0, failed = 0, rxflags = 0;
-
-	len = recv(sock, buf, sizeof(buf), 0);
-	if (len < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
-			   strerror(errno));
-		return;
-	}
-
-	if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
-		wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
-		return;
-	}
-
-	while (1) {
-		ret = ieee80211_radiotap_iterator_next(&iter);
-		if (ret == -ENOENT)
-			break;
-		if (ret) {
-			wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
-				   ret);
-			return;
-		}
-		switch (iter.this_arg_index) {
-		case IEEE80211_RADIOTAP_FLAGS:
-			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
-				len -= 4;
-			break;
-		case IEEE80211_RADIOTAP_RX_FLAGS:
-			rxflags = 1;
-			break;
-		case IEEE80211_RADIOTAP_TX_FLAGS:
-			injected = 1;
-			failed = le_to_host16((*(le16 *) iter.this_arg)) &
-					IEEE80211_RADIOTAP_F_TX_FAIL;
-			break;
-		case IEEE80211_RADIOTAP_DATA_RETRIES:
-			break;
-		case IEEE80211_RADIOTAP_CHANNEL:
-			/* TODO: convert from freq/flags to channel number */
-			break;
-		case IEEE80211_RADIOTAP_RATE:
-			datarate = *iter.this_arg * 5;
-			break;
-		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
-			ssi_signal = (s8) *iter.this_arg;
-			break;
-		}
-	}
-
-	if (rxflags && injected)
-		return;
-
-	if (!injected)
-		handle_frame(drv, buf + iter._max_length,
-			     len - iter._max_length, datarate, ssi_signal);
-	else
-		handle_tx_callback(drv->ctx, buf + iter._max_length,
-				   len - iter._max_length, !failed);
-}
-
-
-/*
- * we post-process the filter code later and rewrite
- * this to the offset to the last instruction
- */
-#define PASS	0xFF
-#define FAIL	0xFE
+#ifdef CONFIG_TESTING_OPTIONS
 
 static struct sock_filter msock_filter_insns[] = {
-	/*
-	 * do a little-endian load of the radiotap length field
-	 */
-	/* load lower byte into A */
-	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
-	/* put it into X (== index register) */
-	BPF_STMT(BPF_MISC| BPF_TAX, 0),
-	/* load upper byte into A */
-	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
-	/* left-shift it by 8 */
-	BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
-	/* or with X */
-	BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
-	/* put result into X */
-	BPF_STMT(BPF_MISC| BPF_TAX, 0),
-
-	/*
-	 * Allow management frames through, this also gives us those
-	 * management frames that we sent ourselves with status
-	 */
-	/* load the lower byte of the IEEE 802.11 frame control field */
-	BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
-	/* mask off frame type and version */
-	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
-	/* accept frame if it's both 0, fall through otherwise */
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
-
-	/*
-	 * TODO: add a bit to radiotap RX flags that indicates
-	 * that the sending station is not associated, then
-	 * add a filter here that filters on our DA and that flag
-	 * to allow us to deauth frames to that bad station.
-	 *
-	 * For now allow all To DS data frames through.
-	 */
-	/* load the IEEE 802.11 frame control field */
-	BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
-	/* mask off frame type, version and DS status */
-	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
-	/* accept frame if version 0, type 2 and To DS, fall through otherwise
-	 */
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
-
-#if 0
-	/*
-	 * drop non-data frames
-	 */
-	/* load the lower byte of the frame control field */
-	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
-	/* mask off QoS bit */
-	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
-	/* drop non-data frames */
-	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
-#endif
-	/* load the upper byte of the frame control field */
-	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
-	/* mask off toDS/fromDS */
-	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
-	/* accept WDS frames */
-	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
-
-	/*
-	 * add header length to index
-	 */
-	/* load the lower byte of the frame control field */
-	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
-	/* mask off QoS bit */
-	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
-	/* right shift it by 6 to give 0 or 2 */
-	BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
-	/* add data frame header length */
-	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
-	/* add index, was start of 802.11 header */
-	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
-	/* move to index, now start of LL header */
-	BPF_STMT(BPF_MISC | BPF_TAX, 0),
-
-	/*
-	 * Accept empty data frames, we use those for
-	 * polling activity.
-	 */
-	BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
-
-	/*
-	 * Accept EAPOL frames
-	 */
-	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
-	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
-
-	/* keep these last two statements or change the code below */
-	/* return 0 == "DROP" */
+	/* return 0 == "DROP", we don't want RX */
 	BPF_STMT(BPF_RET | BPF_K, 0),
-	/* return ~0 == "keep all" */
-	BPF_STMT(BPF_RET | BPF_K, ~0),
 };
 
-static struct sock_fprog msock_filter = {
+static const struct sock_fprog msock_filter = {
 	.len = ARRAY_SIZE(msock_filter_insns),
 	.filter = msock_filter_insns,
 };
@@ -283,32 +38,6 @@
 
 static int add_monitor_filter(int s)
 {
-	int idx;
-
-	/* rewrite all PASS/FAIL jump offsets */
-	for (idx = 0; idx < msock_filter.len; idx++) {
-		struct sock_filter *insn = &msock_filter_insns[idx];
-
-		if (BPF_CLASS(insn->code) == BPF_JMP) {
-			if (insn->code == (BPF_JMP|BPF_JA)) {
-				if (insn->k == PASS)
-					insn->k = msock_filter.len - idx - 2;
-				else if (insn->k == FAIL)
-					insn->k = msock_filter.len - idx - 3;
-			}
-
-			if (insn->jt == PASS)
-				insn->jt = msock_filter.len - idx - 2;
-			else if (insn->jt == FAIL)
-				insn->jt = msock_filter.len - idx - 3;
-
-			if (insn->jf == PASS)
-				insn->jf = msock_filter.len - idx - 2;
-			else if (insn->jf == FAIL)
-				insn->jf = msock_filter.len - idx - 3;
-		}
-	}
-
 	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
 		       &msock_filter, sizeof(msock_filter))) {
 		wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
@@ -320,40 +49,29 @@
 }
 
 
-void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv)
+static void
+nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv,
+				 int ifidx, int sock)
 {
-	if (drv->monitor_refcount > 0)
-		drv->monitor_refcount--;
-	wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d",
-		   drv->monitor_refcount);
-	if (drv->monitor_refcount > 0)
-		return;
+	wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface");
 
-	if (drv->monitor_ifidx >= 0) {
-		nl80211_remove_iface(drv, drv->monitor_ifidx);
-		drv->monitor_ifidx = -1;
-	}
-	if (drv->monitor_sock >= 0) {
-		eloop_unregister_read_sock(drv->monitor_sock);
-		close(drv->monitor_sock);
-		drv->monitor_sock = -1;
-	}
+	if (ifidx >= 0)
+		nl80211_remove_iface(drv, ifidx);
+	if (sock >= 0)
+		close(sock);
 }
 
 
-int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+static int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv,
+					    int *ifidx, int *sock)
 {
 	char buf[IFNAMSIZ];
 	struct sockaddr_ll ll;
 	int optval;
 	socklen_t optlen;
 
-	if (drv->monitor_ifidx >= 0) {
-		drv->monitor_refcount++;
-		wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
-			   drv->monitor_refcount);
-		return 0;
-	}
+	*ifidx = -1;
+	*sock = -1;
 
 	if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
 		/*
@@ -379,23 +97,10 @@
 
 	buf[IFNAMSIZ - 1] = '\0';
 
-	drv->monitor_ifidx =
-		nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
-				     0, NULL, NULL, 0);
+	*ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+				      0, NULL, NULL, 0);
 
-	if (drv->monitor_ifidx == -EOPNOTSUPP) {
-		/*
-		 * This is backward compatibility for a few versions of
-		 * the kernel only that didn't advertise the right
-		 * attributes for the only driver that then supported
-		 * AP mode w/o monitor -- ath6kl.
-		 */
-		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
-			   "monitor interface type - try to run without it");
-		drv->device_ap_sme = 1;
-	}
-
-	if (drv->monitor_ifidx < 0)
+	if (*ifidx < 0)
 		return -1;
 
 	if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
@@ -403,21 +108,21 @@
 
 	memset(&ll, 0, sizeof(ll));
 	ll.sll_family = AF_PACKET;
-	ll.sll_ifindex = drv->monitor_ifidx;
-	drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-	if (drv->monitor_sock < 0) {
+	ll.sll_ifindex = *ifidx;
+	*sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (*sock < 0) {
 		wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
 			   strerror(errno));
 		goto error;
 	}
 
-	if (add_monitor_filter(drv->monitor_sock)) {
+	if (add_monitor_filter(*sock)) {
 		wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
 			   "interface; do filtering in user space");
 		/* This works, but will cost in performance. */
 	}
 
-	if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+	if (bind(*sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
 		wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
 			   strerror(errno));
 		goto error;
@@ -425,30 +130,22 @@
 
 	optlen = sizeof(optval);
 	optval = 20;
-	if (setsockopt
-	    (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+	if (setsockopt(*sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
 		wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
 			   strerror(errno));
 		goto error;
 	}
 
-	if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
-				     drv, NULL)) {
-		wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
-		goto error;
-	}
-
-	drv->monitor_refcount++;
 	return 0;
  error:
-	nl80211_remove_monitor_interface(drv);
+	nl80211_remove_monitor_interface(drv, *ifidx, *sock);
 	return -1;
 }
 
 
-int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
-			 const void *data, size_t len,
-			 int encrypt, int noack)
+static int _nl80211_send_monitor(int monitor_sock,
+				 const void *data, size_t len,
+				 int encrypt, int noack)
 {
 	__u8 rtap_hdr[] = {
 		0x00, 0x00, /* radiotap version */
@@ -484,20 +181,32 @@
 	if (encrypt)
 		rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
 
-	if (drv->monitor_sock < 0) {
-		wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
-			   "for %s", __func__);
-		return -1;
-	}
-
 	if (noack)
 		txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
 	WPA_PUT_LE16(&rtap_hdr[12], txflags);
 
-	res = sendmsg(drv->monitor_sock, &msg, 0);
+	res = sendmsg(monitor_sock, &msg, 0);
 	if (res < 0) {
 		wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
 		return -1;
 	}
 	return 0;
 }
+
+
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+			 const void *data, size_t len,
+			 int encrypt, int noack)
+{
+	int res, ifidx, sock;
+
+	res = nl80211_create_monitor_interface(drv, &ifidx, &sock);
+	if (res < 0)
+		return res;
+
+	res = _nl80211_send_monitor(sock, data, len, encrypt, noack);
+	nl80211_remove_monitor_interface(drv, ifidx, sock);
+	return res;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index d0ed7ad..f0313c1 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -238,6 +238,11 @@
 	if (params->extra_ies) {
 		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
 			    params->extra_ies, params->extra_ies_len);
+		if (params->extra_ies_len > drv->capa.max_probe_req_ie_len)
+			wpa_printf(MSG_INFO,
+				   "nl80211: Extra IEs for scan do not fit driver limit (%zu > %zu) - this is likely to fail",
+				   params->extra_ies_len,
+				   drv->capa.max_probe_req_ie_len);
 		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
 			    params->extra_ies))
 			goto fail;
@@ -739,7 +744,7 @@
 		return android_pno_stop(bss);
 #endif /* ANDROID */
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_STOP_SCHED_SCAN);
 	ret = send_and_recv_cmd(drv, msg);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
@@ -1205,7 +1210,7 @@
 	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
 	drv->scan_for_auth = 0;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
@@ -1380,7 +1385,7 @@
 	if (!drv->set_wifi_conf_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
diff --git a/src/drivers/driver_openbsd.c b/src/drivers/driver_openbsd.c
index bfc2311..dac312a 100644
--- a/src/drivers/driver_openbsd.c
+++ b/src/drivers/driver_openbsd.c
@@ -77,6 +77,9 @@
 	const u8 *key = params->key;
 	size_t key_len = params->key_len;
 
+	if (params->key_flag & KEY_FLAG_NEXT)
+		return -1;
+
 	if (key_len > IEEE80211_PMK_LEN ||
 	    (key_flag & KEY_FLAG_PMK_MASK) != KEY_FLAG_PMK) {
 		return -1;
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index d6735b4..d7c6b01 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -219,6 +219,9 @@
 	const u8 *key = params->key;
 	size_t key_len = params->key_len;
 
+	if (params->key_flag & KEY_FLAG_NEXT)
+		return -1;
+
 	wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
 		   __func__, priv, alg, key_idx, set_tx);
 
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 2c656fb..c34c13b 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -1833,6 +1833,9 @@
 	const u8 *key = params->key;
 	size_t key_len = params->key_len;
 
+	if (params->key_flag & KEY_FLAG_NEXT)
+		return -1;
+
 	wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
 		   "key_len=%lu",
 		   __FUNCTION__, alg, key_idx, set_tx,
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index f97f5ad..f6c1b18 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -1329,6 +1329,13 @@
  *      %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the
  *      TID to Link mapping for downlink/uplink traffic.
  *
+ * @NL80211_CMD_ASSOC_MLO_RECONF: For a non-AP MLD station, request to
+ *      add/remove links to/from the association.
+ *
+ * @NL80211_CMD_EPCS_CFG: EPCS configuration for a station. Used by userland to
+ *	control EPCS configuration. Used to notify userland on the current state
+ *	of EPCS.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1586,6 +1593,9 @@
 
 	NL80211_CMD_SET_TID_TO_LINK_MAPPING,
 
+	NL80211_CMD_ASSOC_MLO_RECONF,
+	NL80211_CMD_EPCS_CFG,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -2868,6 +2878,21 @@
  *	nested item, it contains attributes defined in
  *	&enum nl80211_if_combination_attrs.
  *
+ * @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32).
+ *	A value of 0 means all radios.
+ *
+ * @NL80211_ATTR_SUPPORTED_SELECTORS: supported selectors, array of
+ *	supported selectors as defined by IEEE 802.11 7.3.2.2 but without the
+ *	length restriction (at most %NL80211_MAX_SUPP_SELECTORS).
+ *	This can be used to provide a list of selectors that are implemented
+ *	by the supplicant. If not given, support for SAE_H2E is assumed.
+ *
+ * @NL80211_ATTR_MLO_RECONF_REM_LINKS: (u16) A bitmask of the links requested
+ *      to be removed from the MLO association.
+ *
+ * @NL80211_ATTR_EPCS: Flag attribute indicating that EPCS is enabled for a
+ *	station interface.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3416,6 +3441,13 @@
 	NL80211_ATTR_WIPHY_RADIOS,
 	NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
 
+	NL80211_ATTR_VIF_RADIO_MASK,
+
+	NL80211_ATTR_SUPPORTED_SELECTORS,
+
+	NL80211_ATTR_MLO_RECONF_REM_LINKS,
+	NL80211_ATTR_EPCS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3460,6 +3492,7 @@
 #define NL80211_WIPHY_NAME_MAXLEN		64
 
 #define NL80211_MAX_SUPP_RATES			32
+#define NL80211_MAX_SUPP_SELECTORS		128
 #define NL80211_MAX_SUPP_HT_RATES		77
 #define NL80211_MAX_SUPP_REG_RULES		128
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
@@ -4698,6 +4731,7 @@
  *	overrides all other flags.
  * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
  *	and ACK incoming unicast packets.
+ * @NL80211_MNTR_FLAG_SKIP_TX: do not pass local tx packets
  *
  * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
  * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
@@ -4710,6 +4744,7 @@
 	NL80211_MNTR_FLAG_OTHER_BSS,
 	NL80211_MNTR_FLAG_COOK_FRAMES,
 	NL80211_MNTR_FLAG_ACTIVE,
+	NL80211_MNTR_FLAG_SKIP_TX,
 
 	/* keep last */
 	__NL80211_MNTR_FLAG_AFTER_LAST,
@@ -8031,6 +8066,8 @@
  * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
  *	combination for this radio. Attribute may be present multiple times
  *	and contains attributes defined in &enum nl80211_if_combination_attrs.
+ * @NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK: bitmask (u32) of antennas
+ *	connected to this radio.
  *
  * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
  * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
@@ -8041,6 +8078,7 @@
 	NL80211_WIPHY_RADIO_ATTR_INDEX,
 	NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
 	NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
+	NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK,
 
 	/* keep last */
 	__NL80211_WIPHY_RADIO_ATTR_LAST,
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index ff8ad8d..fa7ecd0 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -76,7 +76,7 @@
 	}
 
 	/* since we're expanding to a bit length, mask off the excess */
-	if (resultbitlen % 8) {
+	if (resultbytelen > 0 && (resultbitlen % 8)) {
 		u8 mask = 0xff;
 		mask <<= (8 - (resultbitlen % 8));
 		result[resultbytelen - 1] &= mask;
diff --git a/src/eap_common/eap_teap_common.c b/src/eap_common/eap_teap_common.c
index ffb9a62..aeb0e69 100644
--- a/src/eap_common/eap_teap_common.c
+++ b/src/eap_common/eap_teap_common.c
@@ -118,32 +118,7 @@
 }
 
 
-int eap_teap_derive_cmk_basic_pw_auth(u16 tls_cs, const u8 *s_imck_msk, u8 *cmk)
-{
-	u8 imsk[32], imck[EAP_TEAP_IMCK_LEN];
-	int res;
-
-	/* FIX: The Basic-Password-Auth (i.e., no inner EAP) case is
-	 * not fully defined in RFC 7170, so this CMK derivation may
-	 * need to be changed if a fixed definition is eventually
-	 * published. For now, derive CMK[0] based on S-IMCK[0] and
-	 * IMSK of 32 octets of zeros. */
-	os_memset(imsk, 0, 32);
-	res = eap_teap_tls_prf(tls_cs, s_imck_msk, EAP_TEAP_SIMCK_LEN,
-			       "Inner Methods Compound Keys",
-			       imsk, 32, imck, sizeof(imck));
-	if (res < 0)
-		return -1;
-	os_memcpy(cmk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN);
-	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: CMK[no-inner-EAP]",
-			cmk, EAP_TEAP_CMK_LEN);
-	forced_memzero(imck, sizeof(imck));
-	return 0;
-}
-
-
-int eap_teap_derive_imck(u16 tls_cs,
-			 const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk,
+int eap_teap_derive_imck(u16 tls_cs, const u8 *prev_s_imck,
 			 const u8 *msk, size_t msk_len,
 			 const u8 *emsk, size_t emsk_len,
 			 u8 *s_imck_msk, u8 *cmk_msk,
@@ -186,7 +161,7 @@
 				imsk, 32);
 
 		res = eap_teap_tls_prf(tls_cs,
-				       prev_s_imck_emsk, EAP_TEAP_SIMCK_LEN,
+				       prev_s_imck, EAP_TEAP_SIMCK_LEN,
 				       "Inner Methods Compound Keys",
 				       imsk, 32, imck, EAP_TEAP_IMCK_LEN);
 		forced_memzero(imsk, sizeof(imsk));
@@ -216,7 +191,7 @@
 		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Zero IMSK", imsk, 32);
 	}
 
-	res = eap_teap_tls_prf(tls_cs, prev_s_imck_msk, EAP_TEAP_SIMCK_LEN,
+	res = eap_teap_tls_prf(tls_cs, prev_s_imck, EAP_TEAP_SIMCK_LEN,
 			       "Inner Methods Compound Keys",
 			       imsk, 32, imck, EAP_TEAP_IMCK_LEN);
 	forced_memzero(imsk, sizeof(imsk));
@@ -342,10 +317,6 @@
 	if (res < 0)
 		return res;
 
-	/* FIX: RFC 7170 does not describe how to handle truncation of the
-	 * Compound MAC or if the fields are supposed to be of variable length
-	 * based on the negotiated TLS cipher suite (they are defined as having
-	 * fixed size of 20 octets in the TLV description) */
 	if (mac_len > sizeof(tmp))
 		mac_len = sizeof(tmp);
 	os_memcpy(mac, tmp, mac_len);
@@ -704,41 +675,3 @@
 	wpabuf_put_be16(buf, id);
 	return buf;
 }
-
-
-int eap_teap_allowed_anon_prov_phase2_method(int vendor, enum eap_type type)
-{
-	/* RFC 7170, Section 3.8.3: MUST provide mutual authentication,
-	 * provide key generation, and be resistant to dictionary attack.
-	 * Section 3.8 also mentions requirement for using EMSK Compound MAC. */
-	return vendor == EAP_VENDOR_IETF &&
-		(type == EAP_TYPE_PWD || type == EAP_TYPE_EKE);
-}
-
-
-int eap_teap_allowed_anon_prov_cipher_suite(u16 cs)
-{
-	/* RFC 7170, Section 3.8.3: anonymous ciphersuites MAY be supported as
-	 * long as the TLS pre-master secret is generated form contribution from
-	 * both peers. Accept the recommended TLS_DH_anon_WITH_AES_128_CBC_SHA
-	 * cipher suite and other ciphersuites that use DH in some form, have
-	 * SHA-1 or stronger MAC function, and use reasonable strong cipher. */
-	static const u16 ok_cs[] = {
-		/* DH-anon */
-		0x0034, 0x003a, 0x006c, 0x006d, 0x00a6, 0x00a7,
-		/* DHE-RSA */
-		0x0033, 0x0039, 0x0067, 0x006b, 0x009e, 0x009f,
-		/* ECDH-anon */
-		0xc018, 0xc019,
-		/* ECDH-RSA */
-		0xc003, 0xc00f, 0xc029, 0xc02a, 0xc031, 0xc032,
-		/* ECDH-ECDSA */
-		0xc004, 0xc005, 0xc025, 0xc026, 0xc02d, 0xc02e,
-		/* ECDHE-RSA */
-		0xc013, 0xc014, 0xc027, 0xc028, 0xc02f, 0xc030,
-		/* ECDHE-ECDSA */
-		0xc009, 0xc00a, 0xc023, 0xc024, 0xc02b, 0xc02c,
-	};
-
-	return tls_cipher_suite_match(ok_cs, ARRAY_SIZE(ok_cs), cs);
-}
diff --git a/src/eap_common/eap_teap_common.h b/src/eap_common/eap_teap_common.h
index 3a25879..cbf1315 100644
--- a/src/eap_common/eap_teap_common.h
+++ b/src/eap_common/eap_teap_common.h
@@ -205,10 +205,7 @@
 struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf);
 int eap_teap_derive_eap_msk(u16 tls_cs, const u8 *simck, u8 *msk);
 int eap_teap_derive_eap_emsk(u16 tls_cs, const u8 *simck, u8 *emsk);
-int eap_teap_derive_cmk_basic_pw_auth(u16 tls_cs, const u8 *s_imck_msk,
-				      u8 *cmk);
-int eap_teap_derive_imck(u16 tls_cs,
-			 const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk,
+int eap_teap_derive_imck(u16 tls_cs, const u8 *prev_s_imck,
 			 const u8 *msk, size_t msk_len,
 			 const u8 *emsk, size_t emsk_len,
 			 u8 *s_imck_msk, u8 *cmk_msk,
@@ -224,7 +221,5 @@
 struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error);
 struct wpabuf * eap_teap_tlv_identity_type(enum teap_identity_types id);
 enum eap_type;
-int eap_teap_allowed_anon_prov_phase2_method(int vendor, enum eap_type type);
-int eap_teap_allowed_anon_prov_cipher_suite(u16 cs);
 
 #endif /* EAP_TEAP_H */
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 1454447..43e100a 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -724,8 +724,6 @@
 		EXT_CERT_CHECK_GOOD,
 		EXT_CERT_CHECK_BAD,
 	} pending_ext_cert_check;
-
-	int teap_anon_dh;
 };
 
 
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
index ced7b16..b9c1ece 100644
--- a/src/eap_peer/eap_teap.c
+++ b/src/eap_peer/eap_teap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TEAP (RFC 7170)
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,11 +14,6 @@
 #include "eap_i.h"
 #include "eap_tls_common.h"
 #include "eap_config.h"
-#include "eap_teap_pac.h"
-
-#ifdef EAP_TEAP_DYNAMIC
-#include "eap_teap_pac.c"
-#endif /* EAP_TEAP_DYNAMIC */
 
 
 static void eap_teap_deinit(struct eap_sm *sm, void *priv);
@@ -43,13 +38,6 @@
 	struct eap_method_type *phase2_types;
 	size_t num_phase2_types;
 	int resuming; /* starting a resumed session */
-#define EAP_TEAP_PROV_UNAUTH 1
-#define EAP_TEAP_PROV_AUTH 2
-	int provisioning_allowed; /* Allowed PAC provisioning modes */
-	int provisioning; /* doing PAC provisioning (not the normal auth) */
-	int anon_provisioning; /* doing anonymous (unauthenticated)
-				* provisioning */
-	int session_ticket_used;
 	int test_outer_tlvs;
 
 	u8 key_data[EAP_TEAP_KEY_LEN];
@@ -58,96 +46,34 @@
 	u8 emsk[EAP_EMSK_LEN];
 	int success;
 
-	struct eap_teap_pac *pac;
-	struct eap_teap_pac *current_pac;
-	size_t max_pac_list_len;
-	int use_pac_binary_format;
-
+	u8 simck[EAP_TEAP_SIMCK_LEN];
 	u8 simck_msk[EAP_TEAP_SIMCK_LEN];
 	u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
 	int simck_idx;
-	int cmk_emsk_available;
+	bool cmk_emsk_available;
 
 	struct wpabuf *pending_phase2_req;
 	struct wpabuf *pending_resp;
 	struct wpabuf *server_outer_tlvs;
 	struct wpabuf *peer_outer_tlvs;
+
+	enum teap_compat {
+		TEAP_DEFAULT,
+		TEAP_FREERADIUS,
+	} teap_compat;
 };
 
 
-static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
-				      const u8 *client_random,
-				      const u8 *server_random,
-				      u8 *master_secret)
-{
-	struct eap_teap_data *data = ctx;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
-
-	if (!master_secret) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: SessionTicket failed - fall back to full TLS handshake");
-		data->session_ticket_used = 0;
-		if (data->provisioning_allowed) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Try to provision a new PAC-Key");
-			data->provisioning = 1;
-			data->current_pac = NULL;
-		}
-		return 0;
-	}
-
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket", ticket, len);
-
-	if (!data->current_pac) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: No PAC-Key available for using SessionTicket");
-		data->session_ticket_used = 0;
-		return 0;
-	}
-
-	/* EAP-TEAP uses PAC-Key as the TLS master_secret */
-	os_memcpy(master_secret, data->current_pac->pac_key,
-		  EAP_TEAP_PAC_KEY_LEN);
-
-	data->session_ticket_used = 1;
-
-	return 1;
-}
-
-
 static void eap_teap_parse_phase1(struct eap_teap_data *data,
 				  const char *phase1)
 {
-	const char *pos;
-
-	pos = os_strstr(phase1, "teap_provisioning=");
-	if (pos) {
-		data->provisioning_allowed = atoi(pos + 18);
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Automatic PAC provisioning mode: %d",
-			   data->provisioning_allowed);
-	}
-
-	pos = os_strstr(phase1, "teap_max_pac_list_len=");
-	if (pos) {
-		data->max_pac_list_len = atoi(pos + 22);
-		if (data->max_pac_list_len == 0)
-			data->max_pac_list_len = 1;
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Maximum PAC list length: %lu",
-			   (unsigned long) data->max_pac_list_len);
-	}
-
-	if (os_strstr(phase1, "teap_pac_format=binary")) {
-		data->use_pac_binary_format = 1;
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Using binary format for PAC list");
-	}
-
 #ifdef CONFIG_TESTING_OPTIONS
 	if (os_strstr(phase1, "teap_test_outer_tlvs=1"))
 		data->test_outer_tlvs = 1;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+	if (os_strstr(phase1, "teap_compat=freeradius"))
+		data->teap_compat = TEAP_FREERADIUS;
 }
 
 
@@ -163,21 +89,10 @@
 	if (!data)
 		return NULL;
 	data->teap_version = EAP_TEAP_VERSION;
-	data->max_pac_list_len = 10;
 
 	if (config->phase1)
 		eap_teap_parse_phase1(data, config->phase1);
 
-	if ((data->provisioning_allowed & EAP_TEAP_PROV_AUTH) &&
-	    !config->cert.ca_cert && !config->cert.ca_path) {
-		/* Prevent PAC provisioning without mutual authentication
-		 * (either by validating server certificate or by suitable
-		 * inner EAP method). */
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: Disable authenticated provisioning due to no ca_cert/ca_path");
-		data->provisioning_allowed &= ~EAP_TEAP_PROV_AUTH;
-	}
-
 	if (eap_peer_select_phase2_methods(config, "auth=",
 					   &data->phase2_types,
 					   &data->num_phase2_types, 0) < 0) {
@@ -188,44 +103,12 @@
 	data->phase2_type.vendor = EAP_VENDOR_IETF;
 	data->phase2_type.method = EAP_TYPE_NONE;
 
-	config->teap_anon_dh = !!(data->provisioning_allowed &
-				  EAP_TEAP_PROV_UNAUTH);
 	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TEAP)) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL");
 		eap_teap_deinit(sm, data);
 		return NULL;
 	}
 
-	if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
-						 eap_teap_session_ticket_cb,
-						 data) < 0) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: Failed to set SessionTicket callback");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-
-	if (!config->pac_file) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: No PAC file configured");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-
-	if (data->use_pac_binary_format &&
-	    eap_teap_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-
-	if (!data->use_pac_binary_format &&
-	    eap_teap_load_pac(sm, &data->pac, config->pac_file) < 0) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-	eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
-
 	return data;
 }
 
@@ -244,6 +127,7 @@
 	data->server_outer_tlvs = NULL;
 	wpabuf_free(data->peer_outer_tlvs);
 	data->peer_outer_tlvs = NULL;
+	forced_memzero(data->simck, EAP_TEAP_SIMCK_LEN);
 	forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
 	forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
 }
@@ -252,7 +136,6 @@
 static void eap_teap_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_teap_data *data = priv;
-	struct eap_teap_pac *pac, *prev;
 
 	if (!data)
 		return;
@@ -262,25 +145,20 @@
 	os_free(data->phase2_types);
 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
 
-	pac = data->pac;
-	prev = NULL;
-	while (pac) {
-		prev = pac;
-		pac = pac->next;
-		eap_teap_free_pac(prev);
-	}
-
 	os_free(data);
 }
 
 
 static int eap_teap_derive_msk(struct eap_teap_data *data)
 {
-	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
-	 * is used in this derivation */
-	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk,
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Derive MSK/EMSK (n=%d)",
+		   data->simck_idx);
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: S-IMCK[n]", data->simck,
+		    EAP_TEAP_SIMCK_LEN);
+
+	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck,
 				    data->key_data) < 0 ||
-	    eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk,
+	    eap_teap_derive_eap_emsk(data->tls_cs, data->simck,
 				     data->emsk) < 0)
 		return -1;
 	data->success = 1;
@@ -296,13 +174,14 @@
 	/* RFC 7170, Section 5.1 */
 	res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn,
 					TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
-					data->simck_msk, EAP_TEAP_SIMCK_LEN);
+					data->simck, EAP_TEAP_SIMCK_LEN);
 	if (res)
 		return res;
 	wpa_hexdump_key(MSG_DEBUG,
 			"EAP-TEAP: session_key_seed (S-IMCK[0])",
-			data->simck_msk, EAP_TEAP_SIMCK_LEN);
-	os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+			data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_msk, data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_emsk, data->simck, EAP_TEAP_SIMCK_LEN);
 	data->simck_idx = 0;
 	return 0;
 }
@@ -339,17 +218,6 @@
 {
 	size_t i;
 
-	/* TODO: TNC with anonymous provisioning; need to require both
-	 * completed inner EAP authentication (EAP-pwd or EAP-EKE) and TNC */
-
-	if (data->anon_provisioning &&
-	    !eap_teap_allowed_anon_prov_phase2_method(vendor, type)) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: EAP type %u:%u not allowed during unauthenticated provisioning",
-			   vendor, type);
-		return -1;
-	}
-
 #ifdef EAP_TNC
 	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_TNC) {
 		data->phase2_type.vendor = EAP_VENDOR_IETF;
@@ -518,28 +386,6 @@
 }
 
 
-static struct wpabuf * eap_teap_tlv_pac_ack(void)
-{
-	struct wpabuf *buf;
-	struct teap_tlv_result *res;
-	struct teap_tlv_pac_ack *ack;
-
-	buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
-	if (!buf)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (ack)");
-	ack = wpabuf_put(buf, sizeof(*ack));
-	ack->tlv_type = host_to_be16(TEAP_TLV_PAC | TEAP_TLV_MANDATORY);
-	ack->length = host_to_be16(sizeof(*ack) - sizeof(struct teap_tlv_hdr));
-	ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
-	ack->pac_len = host_to_be16(2);
-	ack->result = host_to_be16(TEAP_STATUS_SUCCESS);
-
-	return buf;
-}
-
-
 static struct wpabuf * eap_teap_add_identity_type(struct eap_sm *sm,
 						  struct wpabuf *msg)
 {
@@ -693,18 +539,21 @@
 				     sizeof(struct teap_tlv_hdr));
 	rbind->version = EAP_TEAP_VERSION;
 	rbind->received_version = data->received_version;
-	/* FIX: RFC 7170 is not clear on which Flags value to use when
-	 * Crypto-Binding TLV is used with Basic-Password-Auth */
-	flags = cmk_emsk ? TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
-		TEAP_CRYPTO_BINDING_MSK_CMAC;
 	subtype = TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+	if (cmk_emsk)
+		flags = TEAP_CRYPTO_BINDING_EMSK_CMAC;
+	else if (cmk_msk)
+		flags = TEAP_CRYPTO_BINDING_MSK_CMAC;
+	else
+		return -1;
 	rbind->subtype = (flags << 4) | subtype;
 	os_memcpy(rbind->nonce, cb->nonce, sizeof(cb->nonce));
 	inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
 	os_memset(rbind->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
 	os_memset(rbind->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
 
-	if (eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
+	if (cmk_msk &&
+	    eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
 				  data->peer_outer_tlvs, cmk_msk,
 				  rbind->msk_compound_mac) < 0)
 		return -1;
@@ -740,9 +589,7 @@
 		   data->simck_idx + 1);
 
 	if (!data->phase2_method)
-		return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
-							 data->simck_msk,
-							 cmk_msk);
+		goto out; /* no MSK derived in Basic-Password-Auth */
 
 	if (!data->phase2_method || !data->phase2_priv) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
@@ -773,17 +620,34 @@
 						     &emsk_len);
 	}
 
-	res = eap_teap_derive_imck(data->tls_cs,
-				   data->simck_msk, data->simck_emsk,
-				   msk, msk_len, emsk, emsk_len,
-				   data->simck_msk, cmk_msk,
-				   data->simck_emsk, cmk_emsk);
+out:
+	if (data->teap_compat == TEAP_FREERADIUS) {
+		u8 tmp_simck[EAP_TEAP_SIMCK_LEN];
+		u8 tmp_cmk[EAP_TEAP_CMK_LEN];
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: FreeRADIUS compatibility: use S-IMCK_MSK[j-1] and S-IMCK_EMSK[j-1] based on MSK/EMSK derivations instead of a single selected S-IMCK[j-1]");
+		res = eap_teap_derive_imck(data->tls_cs, data->simck_msk,
+					   msk, msk_len, emsk, emsk_len,
+					   data->simck_msk, cmk_msk,
+					   tmp_simck, tmp_cmk);
+		if (emsk)
+			res = eap_teap_derive_imck(data->tls_cs,
+						   data->simck_emsk,
+						   msk, msk_len, emsk, emsk_len,
+						   tmp_simck, tmp_cmk,
+						   data->simck_emsk, cmk_emsk);
+	} else {
+		res = eap_teap_derive_imck(data->tls_cs, data->simck,
+					   msk, msk_len, emsk, emsk_len,
+					   data->simck_msk, cmk_msk,
+					   data->simck_emsk, cmk_emsk);
+	}
 	bin_clear_free(msk, msk_len);
 	bin_clear_free(emsk, emsk_len);
 	if (res == 0) {
 		data->simck_idx++;
-		if (emsk)
-			data->cmk_emsk_available = 1;
+		data->cmk_emsk_available = emsk != NULL;
 	}
 	return res;
 }
@@ -802,7 +666,7 @@
 	data->session_id[0] = EAP_TYPE_TEAP;
 	res = tls_get_tls_unique(data->ssl.conn, data->session_id + 1,
 				 max_id_len - 1);
-	if (res < 0) {
+	if (res < 0 || (size_t) res >= max_id_len) {
 		os_free(data->session_id);
 		data->session_id = NULL;
 		wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id");
@@ -825,10 +689,12 @@
 	u8 *pos;
 	u8 cmk_msk[EAP_TEAP_CMK_LEN];
 	u8 cmk_emsk[EAP_TEAP_CMK_LEN];
+	const u8 *cmk_msk_ptr = NULL;
 	const u8 *cmk_emsk_ptr = NULL;
 	int res;
 	size_t len;
 	u8 flags;
+	bool server_msk, server_emsk;
 
 	if (eap_teap_validate_crypto_binding(data, cb) < 0 ||
 	    eap_teap_get_cmk(sm, data, cmk_msk, cmk_emsk) < 0)
@@ -836,9 +702,12 @@
 
 	/* Validate received MSK/EMSK Compound MAC */
 	flags = cb->subtype >> 4;
+	server_msk = flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+		flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC;
+	server_emsk = flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+		flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC;
 
-	if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
-	    flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+	if (server_msk) {
 		u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
 
 		if (eap_teap_compound_mac(data->tls_cs, cb,
@@ -860,9 +729,7 @@
 		}
 	}
 
-	if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
-	     flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
-	    data->cmk_emsk_available) {
+	if (server_emsk && data->cmk_emsk_available) {
 		u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
 
 		if (eap_teap_compound_mac(data->tls_cs, cb,
@@ -882,8 +749,6 @@
 				   "EAP-TEAP: EMSK Compound MAC did not match");
 			return NULL;
 		}
-
-		cmk_emsk_ptr = cmk_emsk;
 	}
 
 	if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
@@ -898,6 +763,20 @@
 	 * crypto binding to allow server to complete authentication.
 	 */
 
+	if (server_emsk && data->cmk_emsk_available) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK");
+		os_memcpy(data->simck, data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+		cmk_emsk_ptr = cmk_emsk;
+	} else if (server_msk) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_MSK");
+		os_memcpy(data->simck, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+		cmk_msk_ptr = cmk_msk;
+	} else {
+		return NULL;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK[j]",
+			data->simck, EAP_TEAP_SIMCK_LEN);
+
 	len = sizeof(struct teap_tlv_crypto_binding);
 	resp = wpabuf_alloc(len);
 	if (!resp)
@@ -920,7 +799,7 @@
 	pos = wpabuf_put(resp, sizeof(struct teap_tlv_crypto_binding));
 	if (eap_teap_write_crypto_binding(
 		    data, (struct teap_tlv_crypto_binding *) pos,
-		    cb, cmk_msk, cmk_emsk_ptr) < 0) {
+		    cb, cmk_msk_ptr, cmk_emsk_ptr) < 0) {
 		wpabuf_free(resp);
 		return NULL;
 	}
@@ -929,231 +808,6 @@
 }
 
 
-static void eap_teap_parse_pac_tlv(struct eap_teap_pac *entry, int type,
-				   u8 *pos, size_t len, int *pac_key_found)
-{
-	switch (type & 0x7fff) {
-	case PAC_TYPE_PAC_KEY:
-		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: PAC-Key", pos, len);
-		if (len != EAP_TEAP_PAC_KEY_LEN) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Invalid PAC-Key length %lu",
-				   (unsigned long) len);
-			break;
-		}
-		*pac_key_found = 1;
-		os_memcpy(entry->pac_key, pos, len);
-		break;
-	case PAC_TYPE_PAC_OPAQUE:
-		wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pos, len);
-		entry->pac_opaque = pos;
-		entry->pac_opaque_len = len;
-		break;
-	case PAC_TYPE_PAC_INFO:
-		wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Info", pos, len);
-		entry->pac_info = pos;
-		entry->pac_info_len = len;
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignored unknown PAC type %d",
-			   type);
-		break;
-	}
-}
-
-
-static int eap_teap_process_pac_tlv(struct eap_teap_pac *entry,
-				    u8 *pac, size_t pac_len)
-{
-	struct pac_attr_hdr *hdr;
-	u8 *pos;
-	size_t left, len;
-	int type, pac_key_found = 0;
-
-	pos = pac;
-	left = pac_len;
-
-	while (left > sizeof(*hdr)) {
-		hdr = (struct pac_attr_hdr *) pos;
-		type = be_to_host16(hdr->type);
-		len = be_to_host16(hdr->len);
-		pos += sizeof(*hdr);
-		left -= sizeof(*hdr);
-		if (len > left) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC TLV overrun (type=%d len=%lu left=%lu)",
-				   type, (unsigned long) len,
-				   (unsigned long) left);
-			return -1;
-		}
-
-		eap_teap_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
-
-		pos += len;
-		left -= len;
-	}
-
-	if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC TLV does not include all the required fields");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int eap_teap_parse_pac_info(struct eap_teap_pac *entry, int type,
-				   u8 *pos, size_t len)
-{
-	u16 pac_type;
-	u32 lifetime;
-	struct os_time now;
-
-	switch (type & 0x7fff) {
-	case PAC_TYPE_CRED_LIFETIME:
-		if (len != 4) {
-			wpa_hexdump(MSG_DEBUG,
-				    "EAP-TEAP: PAC-Info - Invalid CRED_LIFETIME length - ignored",
-				    pos, len);
-			return 0;
-		}
-
-		/*
-		 * This is not currently saved separately in PAC files since
-		 * the server can automatically initiate PAC update when
-		 * needed. Anyway, the information is available from PAC-Info
-		 * dump if it is needed for something in the future.
-		 */
-		lifetime = WPA_GET_BE32(pos);
-		os_get_time(&now);
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Info - CRED_LIFETIME %d (%d days)",
-			   lifetime, (lifetime - (u32) now.sec) / 86400);
-		break;
-	case PAC_TYPE_A_ID:
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID",
-				  pos, len);
-		entry->a_id = pos;
-		entry->a_id_len = len;
-		break;
-	case PAC_TYPE_I_ID:
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - I-ID",
-				  pos, len);
-		entry->i_id = pos;
-		entry->i_id_len = len;
-		break;
-	case PAC_TYPE_A_ID_INFO:
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID-Info",
-				  pos, len);
-		entry->a_id_info = pos;
-		entry->a_id_info_len = len;
-		break;
-	case PAC_TYPE_PAC_TYPE:
-		/* RFC 7170, Section 4.2.12.6 - PAC-Type TLV */
-		if (len != 2) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Invalid PAC-Type length %lu (expected 2)",
-				   (unsigned long) len);
-			wpa_hexdump_ascii(MSG_DEBUG,
-					  "EAP-TEAP: PAC-Info - PAC-Type",
-					  pos, len);
-			return -1;
-		}
-		pac_type = WPA_GET_BE16(pos);
-		if (pac_type != PAC_TYPE_TUNNEL_PAC) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Unsupported PAC Type %d",
-				   pac_type);
-			return -1;
-		}
-
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Info - PAC-Type %d",
-			   pac_type);
-		entry->pac_type = pac_type;
-		break;
-	default:
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Ignored unknown PAC-Info type %d", type);
-		break;
-	}
-
-	return 0;
-}
-
-
-static int eap_teap_process_pac_info(struct eap_teap_pac *entry)
-{
-	struct pac_attr_hdr *hdr;
-	u8 *pos;
-	size_t left, len;
-	int type;
-
-	/* RFC 7170, Section 4.2.12.4 */
-
-	/* PAC-Type defaults to Tunnel PAC (Type 1) */
-	entry->pac_type = PAC_TYPE_TUNNEL_PAC;
-
-	pos = entry->pac_info;
-	left = entry->pac_info_len;
-	while (left > sizeof(*hdr)) {
-		hdr = (struct pac_attr_hdr *) pos;
-		type = be_to_host16(hdr->type);
-		len = be_to_host16(hdr->len);
-		pos += sizeof(*hdr);
-		left -= sizeof(*hdr);
-		if (len > left) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC-Info overrun (type=%d len=%lu left=%lu)",
-				   type, (unsigned long) len,
-				   (unsigned long) left);
-			return -1;
-		}
-
-		if (eap_teap_parse_pac_info(entry, type, pos, len) < 0)
-			return -1;
-
-		pos += len;
-		left -= len;
-	}
-
-	if (!entry->a_id || !entry->a_id_info) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Info does not include all the required fields");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static struct wpabuf * eap_teap_process_pac(struct eap_sm *sm,
-					    struct eap_teap_data *data,
-					    struct eap_method_ret *ret,
-					    u8 *pac, size_t pac_len)
-{
-	struct eap_peer_config *config = eap_get_config(sm);
-	struct eap_teap_pac entry;
-
-	os_memset(&entry, 0, sizeof(entry));
-	if (eap_teap_process_pac_tlv(&entry, pac, pac_len) ||
-	    eap_teap_process_pac_info(&entry))
-		return NULL;
-
-	eap_teap_add_pac(&data->pac, &data->current_pac, &entry);
-	eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
-	if (data->use_pac_binary_format)
-		eap_teap_save_pac_bin(sm, data->pac, config->pac_file);
-	else
-		eap_teap_save_pac(sm, data->pac, config->pac_file);
-
-	wpa_printf(MSG_DEBUG,
-		   "EAP-TEAP: Send PAC-Acknowledgement - %s initiated provisioning completed successfully",
-		   data->provisioning ? "peer" : "server");
-	return eap_teap_tlv_pac_ack();
-}
-
-
 static int eap_teap_parse_decrypted(struct wpabuf *decrypted,
 				    struct eap_teap_tlv_parse *tlv,
 				    struct wpabuf **resp)
@@ -1208,38 +862,6 @@
 }
 
 
-static struct wpabuf * eap_teap_pac_request(void)
-{
-	struct wpabuf *req;
-	struct teap_tlv_request_action *act;
-	struct teap_tlv_hdr *pac;
-	struct teap_attr_pac_type *type;
-
-	req = wpabuf_alloc(sizeof(*act) + sizeof(*pac) + sizeof(*type));
-	if (!req)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Request Action TLV (Process TLV)");
-	act = wpabuf_put(req, sizeof(*act));
-	act->tlv_type = host_to_be16(TEAP_TLV_REQUEST_ACTION);
-	act->length = host_to_be16(2);
-	act->status = TEAP_STATUS_SUCCESS;
-	act->action = TEAP_REQUEST_ACTION_PROCESS_TLV;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (PAC-Type = Tunnel)");
-	pac = wpabuf_put(req, sizeof(*pac));
-	pac->tlv_type = host_to_be16(TEAP_TLV_PAC);
-	pac->length = host_to_be16(sizeof(*type));
-
-	type = wpabuf_put(req, sizeof(*type));
-	type->type = host_to_be16(PAC_TYPE_PAC_TYPE);
-	type->length = host_to_be16(2);
-	type->pac_type = host_to_be16(PAC_TYPE_TUNNEL_PAC);
-
-	return req;
-}
-
-
 static int eap_teap_process_decrypted(struct eap_sm *sm,
 				      struct eap_teap_data *data,
 				      struct eap_method_ret *ret,
@@ -1387,58 +1009,17 @@
 		}
 	}
 
-	if (data->result_success_done && data->session_ticket_used &&
+	if (data->result_success_done &&
+	    tls_connection_get_own_cert_used(data->ssl.conn) &&
 	    eap_teap_derive_msk(data) == 0) {
 		/* Assume the server might accept authentication without going
 		 * through inner authentication. */
 		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC used - server may decide to skip inner authentication");
-		ret->methodState = METHOD_MAY_CONT;
-		ret->decision = DECISION_COND_SUCC;
-	} else if (data->result_success_done &&
-		   tls_connection_get_own_cert_used(data->ssl.conn) &&
-		   eap_teap_derive_msk(data) == 0) {
-		/* Assume the server might accept authentication without going
-		 * through inner authentication. */
-		wpa_printf(MSG_DEBUG,
 			   "EAP-TEAP: Client certificate used - server may decide to skip inner authentication");
 		ret->methodState = METHOD_MAY_CONT;
 		ret->decision = DECISION_COND_SUCC;
 	}
 
-	if (tlv.pac) {
-		if (tlv.result == TEAP_STATUS_SUCCESS) {
-			tmp = eap_teap_process_pac(sm, data, ret,
-						   tlv.pac, tlv.pac_len);
-			resp = wpabuf_concat(resp, tmp);
-		} else {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC TLV without Result TLV acknowledging success");
-			failed = 1;
-			error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
-		}
-	}
-
-	if (!data->current_pac && data->provisioning && !failed && !tlv.pac &&
-	    tlv.crypto_binding &&
-	    (!data->anon_provisioning ||
-	     (data->phase2_success && data->phase2_method &&
-	      data->phase2_method->vendor == 0 &&
-	      eap_teap_allowed_anon_prov_cipher_suite(data->tls_cs) &&
-	      eap_teap_allowed_anon_prov_phase2_method(
-		      data->phase2_method->vendor,
-		      data->phase2_method->method))) &&
-	    (tlv.iresult == TEAP_STATUS_SUCCESS ||
-	     tlv.result == TEAP_STATUS_SUCCESS)) {
-		/*
-		 * Need to request Tunnel PAC when using authenticated
-		 * provisioning.
-		 */
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Request Tunnel PAC");
-		tmp = eap_teap_pac_request();
-		resp = wpabuf_concat(resp, tmp);
-	}
-
 done:
 	if (failed) {
 		tmp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
@@ -1470,8 +1051,7 @@
 		wpa_printf(MSG_DEBUG,
 			   "EAP-TEAP: Authentication completed successfully");
 		ret->methodState = METHOD_MAY_CONT;
-		data->on_tx_completion = data->provisioning ?
-			METHOD_MAY_CONT : METHOD_DONE;
+		data->on_tx_completion = METHOD_DONE;
 		ret->decision = DECISION_UNCOND_SUCC;
 	}
 
@@ -1562,76 +1142,11 @@
 }
 
 
-static void eap_teap_select_pac(struct eap_teap_data *data,
-				const u8 *a_id, size_t a_id_len)
-{
-	if (!a_id)
-		return;
-	data->current_pac = eap_teap_get_pac(data->pac, a_id, a_id_len,
-					     PAC_TYPE_TUNNEL_PAC);
-	if (data->current_pac) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC found for this A-ID (PAC-Type %d)",
-			   data->current_pac->pac_type);
-		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TEAP: A-ID-Info",
-				  data->current_pac->a_id_info,
-				  data->current_pac->a_id_info_len);
-	}
-}
-
-
-static int eap_teap_use_pac_opaque(struct eap_sm *sm,
-				   struct eap_teap_data *data,
-				   struct eap_teap_pac *pac)
-{
-	u8 *tlv;
-	size_t tlv_len, olen;
-	struct teap_tlv_hdr *ehdr;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC-Opaque TLS extension");
-	olen = pac->pac_opaque_len;
-	tlv_len = sizeof(*ehdr) + olen;
-	tlv = os_malloc(tlv_len);
-	if (tlv) {
-		ehdr = (struct teap_tlv_hdr *) tlv;
-		ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
-		ehdr->length = host_to_be16(olen);
-		os_memcpy(ehdr + 1, pac->pac_opaque, olen);
-	}
-	if (!tlv ||
-	    tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
-					    TLS_EXT_PAC_OPAQUE,
-					    tlv, tlv_len) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to add PAC-Opaque TLS extension");
-		os_free(tlv);
-		return -1;
-	}
-	os_free(tlv);
-
-	return 0;
-}
-
-
-static int eap_teap_clear_pac_opaque_ext(struct eap_sm *sm,
-					 struct eap_teap_data *data)
-{
-	if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
-					    TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to remove PAC-Opaque TLS extension");
-		return -1;
-	}
-	return 0;
-}
-
-
 static int eap_teap_process_start(struct eap_sm *sm,
 				  struct eap_teap_data *data, u8 flags,
 				  const u8 *pos, size_t left)
 {
 	const u8 *a_id = NULL;
-	size_t a_id_len = 0;
 
 	/* TODO: Support (mostly theoretical) case of TEAP/Start request being
 	 * fragmented */
@@ -1725,7 +1240,6 @@
 					return -1;
 				}
 				a_id = outer_pos;
-				a_id_len = tlv_len;
 			} else {
 				wpa_printf(MSG_DEBUG,
 					   "EAP-TEAP: Ignore unknown Outer TLV (Type %u)",
@@ -1740,28 +1254,6 @@
 		return -1;
 	}
 
-	eap_teap_select_pac(data, a_id, a_id_len);
-
-	if (data->resuming && data->current_pac) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Trying to resume session - do not add PAC-Opaque to TLS ClientHello");
-		if (eap_teap_clear_pac_opaque_ext(sm, data) < 0)
-			return -1;
-	} else if (data->current_pac) {
-		/*
-		 * PAC found for the A-ID and we are not resuming an old
-		 * session, so add PAC-Opaque extension to ClientHello.
-		 */
-		if (eap_teap_use_pac_opaque(sm, data, data->current_pac) < 0)
-			return -1;
-	} else if (data->provisioning_allowed) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: No PAC found - starting provisioning");
-		if (eap_teap_clear_pac_opaque_ext(sm, data) < 0)
-			return -1;
-		data->provisioning = 1;
-	}
-
 	return 0;
 }
 
@@ -1939,8 +1431,6 @@
 		}
 
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
-			char cipher[80];
-
 			wpa_printf(MSG_DEBUG,
 				   "EAP-TEAP: TLS done, proceed to Phase 2");
 			data->tls_cs =
@@ -1949,19 +1439,6 @@
 				   "EAP-TEAP: TLS cipher suite 0x%04x",
 				   data->tls_cs);
 
-			if (data->provisioning &&
-			    (!(data->provisioning_allowed &
-			       EAP_TEAP_PROV_AUTH) ||
-			     tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
-					    cipher, sizeof(cipher)) < 0 ||
-			     os_strstr(cipher, "ADH-") ||
-			     os_strstr(cipher, "anon"))) {
-				wpa_printf(MSG_DEBUG,
-					   "EAP-TEAP: Using anonymous (unauthenticated) provisioning");
-				data->anon_provisioning = 1;
-			} else {
-				data->anon_provisioning = 0;
-			}
 			data->resuming = 0;
 			if (eap_teap_derive_key_auth(sm, data) < 0) {
 				wpa_printf(MSG_DEBUG,
@@ -2037,8 +1514,6 @@
 	data->iresult_verified = 0;
 	data->done_on_tx_completion = 0;
 	data->resuming = 1;
-	data->provisioning = 0;
-	data->anon_provisioning = 0;
 	data->simck_idx = 0;
 	return priv;
 }
diff --git a/src/eap_peer/eap_teap_pac.c b/src/eap_peer/eap_teap_pac.c
deleted file mode 100644
index 34a2743..0000000
--- a/src/eap_peer/eap_teap_pac.c
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- * EAP peer method: EAP-TEAP PAC file processing
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "eap_config.h"
-#include "eap_i.h"
-#include "eap_teap_pac.h"
-
-/* TODO: encrypt PAC-Key in the PAC file */
-
-
-/* Text data format */
-static const char *pac_file_hdr =
-	"wpa_supplicant EAP-TEAP PAC file - version 1";
-
-/*
- * Binary data format
- * 4-octet magic value: 6A E4 92 1C
- * 2-octet version (big endian)
- * <version specific data>
- *
- * version=0:
- * Sequence of PAC entries:
- *   2-octet PAC-Type (big endian)
- *   32-octet PAC-Key
- *   2-octet PAC-Opaque length (big endian)
- *   <variable len> PAC-Opaque data (length bytes)
- *   2-octet PAC-Info length (big endian)
- *   <variable len> PAC-Info data (length bytes)
- */
-
-#define EAP_TEAP_PAC_BINARY_MAGIC 0x6ae4921c
-#define EAP_TEAP_PAC_BINARY_FORMAT_VERSION 0
-
-
-/**
- * eap_teap_free_pac - Free PAC data
- * @pac: Pointer to the PAC entry
- *
- * Note that the PAC entry must not be in a list since this function does not
- * remove the list links.
- */
-void eap_teap_free_pac(struct eap_teap_pac *pac)
-{
-	os_free(pac->pac_opaque);
-	os_free(pac->pac_info);
-	os_free(pac->a_id);
-	os_free(pac->i_id);
-	os_free(pac->a_id_info);
-	os_free(pac);
-}
-
-
-/**
- * eap_teap_get_pac - Get a PAC entry based on A-ID
- * @pac_root: Pointer to root of the PAC list
- * @a_id: A-ID to search for
- * @a_id_len: Length of A-ID
- * @pac_type: PAC-Type to search for
- * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
- */
-struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root,
-				       const u8 *a_id, size_t a_id_len,
-				       u16 pac_type)
-{
-	struct eap_teap_pac *pac = pac_root;
-
-	while (pac) {
-		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
-		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
-			return pac;
-		}
-		pac = pac->next;
-	}
-	return NULL;
-}
-
-
-static void eap_teap_remove_pac(struct eap_teap_pac **pac_root,
-				struct eap_teap_pac **pac_current,
-				const u8 *a_id, size_t a_id_len, u16 pac_type)
-{
-	struct eap_teap_pac *pac, *prev;
-
-	pac = *pac_root;
-	prev = NULL;
-
-	while (pac) {
-		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
-		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
-			if (!prev)
-				*pac_root = pac->next;
-			else
-				prev->next = pac->next;
-			if (*pac_current == pac)
-				*pac_current = NULL;
-			eap_teap_free_pac(pac);
-			break;
-		}
-		prev = pac;
-		pac = pac->next;
-	}
-}
-
-
-static int eap_teap_copy_buf(u8 **dst, size_t *dst_len,
-			     const u8 *src, size_t src_len)
-{
-	if (src) {
-		*dst = os_memdup(src, src_len);
-		if (!(*dst))
-			return -1;
-		*dst_len = src_len;
-	}
-	return 0;
-}
-
-
-/**
- * eap_teap_add_pac - Add a copy of a PAC entry to a list
- * @pac_root: Pointer to PAC list root pointer
- * @pac_current: Pointer to the current PAC pointer
- * @entry: New entry to clone and add to the list
- * Returns: 0 on success, -1 on failure
- *
- * This function makes a clone of the given PAC entry and adds this copied
- * entry to the list (pac_root). If an old entry for the same A-ID is found,
- * it will be removed from the PAC list and in this case, pac_current entry
- * is set to %NULL if it was the removed entry.
- */
-int eap_teap_add_pac(struct eap_teap_pac **pac_root,
-		     struct eap_teap_pac **pac_current,
-		     struct eap_teap_pac *entry)
-{
-	struct eap_teap_pac *pac;
-
-	if (!entry || !entry->a_id)
-		return -1;
-
-	/* Remove a possible old entry for the matching A-ID. */
-	eap_teap_remove_pac(pac_root, pac_current,
-			    entry->a_id, entry->a_id_len, entry->pac_type);
-
-	/* Allocate a new entry and add it to the list of PACs. */
-	pac = os_zalloc(sizeof(*pac));
-	if (!pac)
-		return -1;
-
-	pac->pac_type = entry->pac_type;
-	os_memcpy(pac->pac_key, entry->pac_key, EAP_TEAP_PAC_KEY_LEN);
-	if (eap_teap_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
-			      entry->pac_opaque, entry->pac_opaque_len) < 0 ||
-	    eap_teap_copy_buf(&pac->pac_info, &pac->pac_info_len,
-			      entry->pac_info, entry->pac_info_len) < 0 ||
-	    eap_teap_copy_buf(&pac->a_id, &pac->a_id_len,
-			      entry->a_id, entry->a_id_len) < 0 ||
-	    eap_teap_copy_buf(&pac->i_id, &pac->i_id_len,
-			      entry->i_id, entry->i_id_len) < 0 ||
-	    eap_teap_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
-			      entry->a_id_info, entry->a_id_info_len) < 0) {
-		eap_teap_free_pac(pac);
-		return -1;
-	}
-
-	pac->next = *pac_root;
-	*pac_root = pac;
-
-	return 0;
-}
-
-
-struct eap_teap_read_ctx {
-	FILE *f;
-	const char *pos;
-	const char *end;
-	int line;
-	char *buf;
-	size_t buf_len;
-};
-
-static int eap_teap_read_line(struct eap_teap_read_ctx *rc, char **value)
-{
-	char *pos;
-
-	rc->line++;
-	if (rc->f) {
-		if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
-			return -1;
-	} else {
-		const char *l_end;
-		size_t len;
-
-		if (rc->pos >= rc->end)
-			return -1;
-		l_end = rc->pos;
-		while (l_end < rc->end && *l_end != '\n')
-			l_end++;
-		len = l_end - rc->pos;
-		if (len >= rc->buf_len)
-			len = rc->buf_len - 1;
-		os_memcpy(rc->buf, rc->pos, len);
-		rc->buf[len] = '\0';
-		rc->pos = l_end + 1;
-	}
-
-	rc->buf[rc->buf_len - 1] = '\0';
-	pos = rc->buf;
-	while (*pos != '\0') {
-		if (*pos == '\n' || *pos == '\r') {
-			*pos = '\0';
-			break;
-		}
-		pos++;
-	}
-
-	pos = os_strchr(rc->buf, '=');
-	if (pos)
-		*pos++ = '\0';
-	*value = pos;
-
-	return 0;
-}
-
-
-static u8 * eap_teap_parse_hex(const char *value, size_t *len)
-{
-	int hlen;
-	u8 *buf;
-
-	if (!value)
-		return NULL;
-	hlen = os_strlen(value);
-	if (hlen & 1)
-		return NULL;
-	*len = hlen / 2;
-	buf = os_malloc(*len);
-	if (!buf)
-		return NULL;
-	if (hexstr2bin(value, buf, *len)) {
-		os_free(buf);
-		return NULL;
-	}
-	return buf;
-}
-
-
-static int eap_teap_init_pac_data(struct eap_sm *sm, const char *pac_file,
-				  struct eap_teap_read_ctx *rc)
-{
-	os_memset(rc, 0, sizeof(*rc));
-
-	rc->buf_len = 2048;
-	rc->buf = os_malloc(rc->buf_len);
-	if (!rc->buf)
-		return -1;
-
-	if (os_strncmp(pac_file, "blob://", 7) == 0) {
-		const struct wpa_config_blob *blob;
-
-		blob = eap_get_config_blob(sm, pac_file + 7);
-		if (!blob) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned",
-				   pac_file + 7);
-			os_free(rc->buf);
-			return -1;
-		}
-		rc->pos = (char *) blob->data;
-		rc->end = (char *) blob->data + blob->len;
-	} else {
-		rc->f = fopen(pac_file, "rb");
-		if (!rc->f) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned",
-				   pac_file);
-			os_free(rc->buf);
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-
-static void eap_teap_deinit_pac_data(struct eap_teap_read_ctx *rc)
-{
-	os_free(rc->buf);
-	if (rc->f)
-		fclose(rc->f);
-}
-
-
-static const char * eap_teap_parse_start(struct eap_teap_pac **pac)
-{
-	if (*pac)
-		return "START line without END";
-
-	*pac = os_zalloc(sizeof(struct eap_teap_pac));
-	if (!(*pac))
-		return "No memory for PAC entry";
-	(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_end(struct eap_teap_pac **pac_root,
-				       struct eap_teap_pac **pac)
-{
-	if (!(*pac))
-		return "END line without START";
-	if (*pac_root) {
-		struct eap_teap_pac *end = *pac_root;
-
-		while (end->next)
-			end = end->next;
-		end->next = *pac;
-	} else
-		*pac_root = *pac;
-
-	*pac = NULL;
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_pac_type(struct eap_teap_pac *pac,
-					    char *pos)
-{
-	if (!pos)
-		return "Cannot parse pac type";
-	pac->pac_type = atoi(pos);
-	if (pac->pac_type != PAC_TYPE_TUNNEL_PAC)
-		return "Unrecognized PAC-Type";
-
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_pac_key(struct eap_teap_pac *pac, char *pos)
-{
-	u8 *key;
-	size_t key_len;
-
-	key = eap_teap_parse_hex(pos, &key_len);
-	if (!key || key_len != EAP_TEAP_PAC_KEY_LEN) {
-		os_free(key);
-		return "Invalid PAC-Key";
-	}
-
-	os_memcpy(pac->pac_key, key, EAP_TEAP_PAC_KEY_LEN);
-	os_free(key);
-
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_pac_opaque(struct eap_teap_pac *pac,
-					      char *pos)
-{
-	os_free(pac->pac_opaque);
-	pac->pac_opaque = eap_teap_parse_hex(pos, &pac->pac_opaque_len);
-	if (!pac->pac_opaque)
-		return "Invalid PAC-Opaque";
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_a_id(struct eap_teap_pac *pac, char *pos)
-{
-	os_free(pac->a_id);
-	pac->a_id = eap_teap_parse_hex(pos, &pac->a_id_len);
-	if (!pac->a_id)
-		return "Invalid A-ID";
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_i_id(struct eap_teap_pac *pac, char *pos)
-{
-	os_free(pac->i_id);
-	pac->i_id = eap_teap_parse_hex(pos, &pac->i_id_len);
-	if (!pac->i_id)
-		return "Invalid I-ID";
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_a_id_info(struct eap_teap_pac *pac,
-					     char *pos)
-{
-	os_free(pac->a_id_info);
-	pac->a_id_info = eap_teap_parse_hex(pos, &pac->a_id_info_len);
-	if (!pac->a_id_info)
-		return "Invalid A-ID-Info";
-	return NULL;
-}
-
-
-/**
- * eap_teap_load_pac - Load PAC entries (text format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Pointer to root of the PAC list (to be filled)
- * @pac_file: Name of the PAC file/blob to load
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-		      const char *pac_file)
-{
-	struct eap_teap_read_ctx rc;
-	struct eap_teap_pac *pac = NULL;
-	int count = 0;
-	char *pos;
-	const char *err = NULL;
-
-	if (!pac_file)
-		return -1;
-
-	if (eap_teap_init_pac_data(sm, pac_file, &rc) < 0)
-		return 0;
-
-	if (eap_teap_read_line(&rc, &pos) < 0) {
-		/* empty file - assume it is fine to overwrite */
-		eap_teap_deinit_pac_data(&rc);
-		return 0;
-	}
-	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
-		err = "Unrecognized header line";
-
-	while (!err && eap_teap_read_line(&rc, &pos) == 0) {
-		if (os_strcmp(rc.buf, "START") == 0)
-			err = eap_teap_parse_start(&pac);
-		else if (os_strcmp(rc.buf, "END") == 0) {
-			err = eap_teap_parse_end(pac_root, &pac);
-			count++;
-		} else if (!pac)
-			err = "Unexpected line outside START/END block";
-		else if (os_strcmp(rc.buf, "PAC-Type") == 0)
-			err = eap_teap_parse_pac_type(pac, pos);
-		else if (os_strcmp(rc.buf, "PAC-Key") == 0)
-			err = eap_teap_parse_pac_key(pac, pos);
-		else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
-			err = eap_teap_parse_pac_opaque(pac, pos);
-		else if (os_strcmp(rc.buf, "A-ID") == 0)
-			err = eap_teap_parse_a_id(pac, pos);
-		else if (os_strcmp(rc.buf, "I-ID") == 0)
-			err = eap_teap_parse_i_id(pac, pos);
-		else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
-			err = eap_teap_parse_a_id_info(pac, pos);
-	}
-
-	if (pac) {
-		if (!err)
-			err = "PAC block not terminated with END";
-		eap_teap_free_pac(pac);
-	}
-
-	eap_teap_deinit_pac_data(&rc);
-
-	if (err) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: %s in '%s:%d'",
-			   err, pac_file, rc.line);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %d PAC entries from '%s'",
-		   count, pac_file);
-
-	return 0;
-}
-
-
-static void eap_teap_write(char **buf, char **pos, size_t *buf_len,
-			   const char *field, const u8 *data,
-			   size_t len, int txt)
-{
-	size_t i, need;
-	int ret;
-	char *end;
-
-	if (!data || !buf || !(*buf) || !pos || !(*pos) || *pos < *buf)
-		return;
-
-	need = os_strlen(field) + len * 2 + 30;
-	if (txt)
-		need += os_strlen(field) + len + 20;
-
-	if (*pos - *buf + need > *buf_len) {
-		char *nbuf = os_realloc(*buf, *buf_len + need);
-
-		if (!nbuf) {
-			os_free(*buf);
-			*buf = NULL;
-			return;
-		}
-		*pos = nbuf + (*pos - *buf);
-		*buf = nbuf;
-		*buf_len += need;
-	}
-	end = *buf + *buf_len;
-
-	ret = os_snprintf(*pos, end - *pos, "%s=", field);
-	if (os_snprintf_error(end - *pos, ret))
-		return;
-	*pos += ret;
-	*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
-	ret = os_snprintf(*pos, end - *pos, "\n");
-	if (os_snprintf_error(end - *pos, ret))
-		return;
-	*pos += ret;
-
-	if (txt) {
-		ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
-		if (os_snprintf_error(end - *pos, ret))
-			return;
-		*pos += ret;
-		for (i = 0; i < len; i++) {
-			ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
-			if (os_snprintf_error(end - *pos, ret))
-				return;
-			*pos += ret;
-		}
-		ret = os_snprintf(*pos, end - *pos, "\n");
-		if (os_snprintf_error(end - *pos, ret))
-			return;
-		*pos += ret;
-	}
-}
-
-
-static int eap_teap_write_pac(struct eap_sm *sm, const char *pac_file,
-			      char *buf, size_t len)
-{
-	if (os_strncmp(pac_file, "blob://", 7) == 0) {
-		struct wpa_config_blob *blob;
-
-		blob = os_zalloc(sizeof(*blob));
-		if (!blob)
-			return -1;
-		blob->data = (u8 *) buf;
-		blob->len = len;
-		buf = NULL;
-		blob->name = os_strdup(pac_file + 7);
-		if (!blob->name) {
-			os_free(blob);
-			return -1;
-		}
-		eap_set_config_blob(sm, blob);
-	} else {
-		FILE *f;
-
-		f = fopen(pac_file, "wb");
-		if (!f) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Failed to open PAC file '%s' for writing",
-				   pac_file);
-			return -1;
-		}
-		if (fwrite(buf, 1, len, f) != len) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Failed to write all PACs into '%s'",
-				   pac_file);
-			fclose(f);
-			return -1;
-		}
-		os_free(buf);
-		fclose(f);
-	}
-
-	return 0;
-}
-
-
-static int eap_teap_add_pac_data(struct eap_teap_pac *pac, char **buf,
-				 char **pos, size_t *buf_len)
-{
-	int ret;
-
-	ret = os_snprintf(*pos, *buf + *buf_len - *pos,
-			  "START\nPAC-Type=%d\n", pac->pac_type);
-	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
-		return -1;
-
-	*pos += ret;
-	eap_teap_write(buf, pos, buf_len, "PAC-Key",
-		       pac->pac_key, EAP_TEAP_PAC_KEY_LEN, 0);
-	eap_teap_write(buf, pos, buf_len, "PAC-Opaque",
-		       pac->pac_opaque, pac->pac_opaque_len, 0);
-	eap_teap_write(buf, pos, buf_len, "PAC-Info",
-		       pac->pac_info, pac->pac_info_len, 0);
-	eap_teap_write(buf, pos, buf_len, "A-ID",
-		       pac->a_id, pac->a_id_len, 0);
-	eap_teap_write(buf, pos, buf_len, "I-ID",
-		       pac->i_id, pac->i_id_len, 1);
-	eap_teap_write(buf, pos, buf_len, "A-ID-Info",
-		       pac->a_id_info, pac->a_id_info_len, 1);
-	if (!(*buf)) {
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: No memory for PAC data");
-		return -1;
-	}
-	ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
-	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
-		return -1;
-	*pos += ret;
-
-	return 0;
-}
-
-
-/**
- * eap_teap_save_pac - Save PAC entries (text format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Root of the PAC list
- * @pac_file: Name of the PAC file/blob
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-		      const char *pac_file)
-{
-	struct eap_teap_pac *pac;
-	int ret, count = 0;
-	char *buf, *pos;
-	size_t buf_len;
-
-	if (!pac_file)
-		return -1;
-
-	buf_len = 1024;
-	pos = buf = os_malloc(buf_len);
-	if (!buf)
-		return -1;
-
-	ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
-	if (os_snprintf_error(buf + buf_len - pos, ret)) {
-		os_free(buf);
-		return -1;
-	}
-	pos += ret;
-
-	pac = pac_root;
-	while (pac) {
-		if (eap_teap_add_pac_data(pac, &buf, &pos, &buf_len)) {
-			os_free(buf);
-			return -1;
-		}
-		count++;
-		pac = pac->next;
-	}
-
-	if (eap_teap_write_pac(sm, pac_file, buf, pos - buf)) {
-		os_free(buf);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %d PAC entries into '%s'",
-		   count, pac_file);
-
-	return 0;
-}
-
-
-/**
- * eap_teap_pac_list_truncate - Truncate a PAC list to the given length
- * @pac_root: Root of the PAC list
- * @max_len: Maximum length of the list (>= 1)
- * Returns: Number of PAC entries removed
- */
-size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root,
-				  size_t max_len)
-{
-	struct eap_teap_pac *pac, *prev;
-	size_t count;
-
-	pac = pac_root;
-	prev = NULL;
-	count = 0;
-
-	while (pac) {
-		count++;
-		if (count > max_len)
-			break;
-		prev = pac;
-		pac = pac->next;
-	}
-
-	if (count <= max_len || !prev)
-		return 0;
-
-	count = 0;
-	prev->next = NULL;
-
-	while (pac) {
-		prev = pac;
-		pac = pac->next;
-		eap_teap_free_pac(prev);
-		count++;
-	}
-
-	return count;
-}
-
-
-static void eap_teap_pac_get_a_id(struct eap_teap_pac *pac)
-{
-	u8 *pos, *end;
-	u16 type, len;
-
-	pos = pac->pac_info;
-	end = pos + pac->pac_info_len;
-
-	while (end - pos > 4) {
-		type = WPA_GET_BE16(pos);
-		pos += 2;
-		len = WPA_GET_BE16(pos);
-		pos += 2;
-		if (len > (unsigned int) (end - pos))
-			break;
-
-		if (type == PAC_TYPE_A_ID) {
-			os_free(pac->a_id);
-			pac->a_id = os_memdup(pos, len);
-			if (!pac->a_id)
-				break;
-			pac->a_id_len = len;
-		}
-
-		if (type == PAC_TYPE_A_ID_INFO) {
-			os_free(pac->a_id_info);
-			pac->a_id_info = os_memdup(pos, len);
-			if (!pac->a_id_info)
-				break;
-			pac->a_id_info_len = len;
-		}
-
-		pos += len;
-	}
-}
-
-
-/**
- * eap_teap_load_pac_bin - Load PAC entries (binary format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Pointer to root of the PAC list (to be filled)
- * @pac_file: Name of the PAC file/blob to load
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-			  const char *pac_file)
-{
-	const struct wpa_config_blob *blob = NULL;
-	u8 *buf, *end, *pos;
-	size_t len, count = 0;
-	struct eap_teap_pac *pac, *prev;
-
-	*pac_root = NULL;
-
-	if (!pac_file)
-		return -1;
-
-	if (os_strncmp(pac_file, "blob://", 7) == 0) {
-		blob = eap_get_config_blob(sm, pac_file + 7);
-		if (!blob) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned",
-				   pac_file + 7);
-			return 0;
-		}
-		buf = blob->data;
-		len = blob->len;
-	} else {
-		buf = (u8 *) os_readfile(pac_file, &len);
-		if (!buf) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned",
-				   pac_file);
-			return 0;
-		}
-	}
-
-	if (len == 0) {
-		if (!blob)
-			os_free(buf);
-		return 0;
-	}
-
-	if (len < 6 || WPA_GET_BE32(buf) != EAP_TEAP_PAC_BINARY_MAGIC ||
-	    WPA_GET_BE16(buf + 4) != EAP_TEAP_PAC_BINARY_FORMAT_VERSION) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: Invalid PAC file '%s' (bin)",
-			   pac_file);
-		if (!blob)
-			os_free(buf);
-		return -1;
-	}
-
-	pac = prev = NULL;
-	pos = buf + 6;
-	end = buf + len;
-	while (pos < end) {
-		u16 val;
-
-		if (end - pos < 2 + EAP_TEAP_PAC_KEY_LEN + 2 + 2) {
-			pac = NULL;
-			goto parse_fail;
-		}
-
-		pac = os_zalloc(sizeof(*pac));
-		if (!pac)
-			goto parse_fail;
-
-		pac->pac_type = WPA_GET_BE16(pos);
-		pos += 2;
-		os_memcpy(pac->pac_key, pos, EAP_TEAP_PAC_KEY_LEN);
-		pos += EAP_TEAP_PAC_KEY_LEN;
-		val = WPA_GET_BE16(pos);
-		pos += 2;
-		if (val > end - pos)
-			goto parse_fail;
-		pac->pac_opaque_len = val;
-		pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
-		if (!pac->pac_opaque)
-			goto parse_fail;
-		pos += pac->pac_opaque_len;
-		if (end - pos < 2)
-			goto parse_fail;
-		val = WPA_GET_BE16(pos);
-		pos += 2;
-		if (val > end - pos)
-			goto parse_fail;
-		pac->pac_info_len = val;
-		pac->pac_info = os_memdup(pos, pac->pac_info_len);
-		if (!pac->pac_info)
-			goto parse_fail;
-		pos += pac->pac_info_len;
-		eap_teap_pac_get_a_id(pac);
-
-		count++;
-		if (prev)
-			prev->next = pac;
-		else
-			*pac_root = pac;
-		prev = pac;
-	}
-
-	if (!blob)
-		os_free(buf);
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %lu PAC entries from '%s' (bin)",
-		   (unsigned long) count, pac_file);
-
-	return 0;
-
-parse_fail:
-	wpa_printf(MSG_INFO, "EAP-TEAP: Failed to parse PAC file '%s' (bin)",
-		   pac_file);
-	if (!blob)
-		os_free(buf);
-	if (pac)
-		eap_teap_free_pac(pac);
-	return -1;
-}
-
-
-/**
- * eap_teap_save_pac_bin - Save PAC entries (binary format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Root of the PAC list
- * @pac_file: Name of the PAC file/blob
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-			  const char *pac_file)
-{
-	size_t len, count = 0;
-	struct eap_teap_pac *pac;
-	u8 *buf, *pos;
-
-	len = 6;
-	pac = pac_root;
-	while (pac) {
-		if (pac->pac_opaque_len > 65535 ||
-		    pac->pac_info_len > 65535)
-			return -1;
-		len += 2 + EAP_TEAP_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
-			2 + pac->pac_info_len;
-		pac = pac->next;
-	}
-
-	buf = os_malloc(len);
-	if (!buf)
-		return -1;
-
-	pos = buf;
-	WPA_PUT_BE32(pos, EAP_TEAP_PAC_BINARY_MAGIC);
-	pos += 4;
-	WPA_PUT_BE16(pos, EAP_TEAP_PAC_BINARY_FORMAT_VERSION);
-	pos += 2;
-
-	pac = pac_root;
-	while (pac) {
-		WPA_PUT_BE16(pos, pac->pac_type);
-		pos += 2;
-		os_memcpy(pos, pac->pac_key, EAP_TEAP_PAC_KEY_LEN);
-		pos += EAP_TEAP_PAC_KEY_LEN;
-		WPA_PUT_BE16(pos, pac->pac_opaque_len);
-		pos += 2;
-		os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
-		pos += pac->pac_opaque_len;
-		WPA_PUT_BE16(pos, pac->pac_info_len);
-		pos += 2;
-		os_memcpy(pos, pac->pac_info, pac->pac_info_len);
-		pos += pac->pac_info_len;
-
-		pac = pac->next;
-		count++;
-	}
-
-	if (eap_teap_write_pac(sm, pac_file, (char *) buf, len)) {
-		os_free(buf);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %lu PAC entries into '%s' (bin)",
-		   (unsigned long) count, pac_file);
-
-	return 0;
-}
diff --git a/src/eap_peer/eap_teap_pac.h b/src/eap_peer/eap_teap_pac.h
deleted file mode 100644
index edf4c57..0000000
--- a/src/eap_peer/eap_teap_pac.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * EAP peer method: EAP-TEAP PAC file processing
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef EAP_TEAP_PAC_H
-#define EAP_TEAP_PAC_H
-
-#include "eap_common/eap_teap_common.h"
-
-struct eap_teap_pac {
-	struct eap_teap_pac *next;
-
-	u8 pac_key[EAP_TEAP_PAC_KEY_LEN];
-	u8 *pac_opaque;
-	size_t pac_opaque_len;
-	u8 *pac_info;
-	size_t pac_info_len;
-	u8 *a_id;
-	size_t a_id_len;
-	u8 *i_id;
-	size_t i_id_len;
-	u8 *a_id_info;
-	size_t a_id_info_len;
-	u16 pac_type;
-};
-
-
-void eap_teap_free_pac(struct eap_teap_pac *pac);
-struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root,
-				       const u8 *a_id, size_t a_id_len,
-				       u16 pac_type);
-int eap_teap_add_pac(struct eap_teap_pac **pac_root,
-		     struct eap_teap_pac **pac_current,
-		     struct eap_teap_pac *entry);
-int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-		      const char *pac_file);
-int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-		      const char *pac_file);
-size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root,
-				  size_t max_len);
-int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-			  const char *pac_file);
-int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-			  const char *pac_file);
-
-#endif /* EAP_TEAP_PAC_H */
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index 4167e99..7e167f0 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -106,33 +106,6 @@
 #endif /* EAP_UNAUTH_TLS */
 
 
-#ifdef CONFIG_HS20
-static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
-{
-	struct eap_tls_data *data;
-	struct eap_peer_config *config = eap_get_config(sm);
-
-	data = os_zalloc(sizeof(*data));
-	if (data == NULL)
-		return NULL;
-
-	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
-		sm->ssl_ctx;
-
-	if (eap_peer_tls_ssl_init(sm, &data->ssl, config,
-				  EAP_WFA_UNAUTH_TLS_TYPE)) {
-		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
-		eap_tls_deinit(sm, data);
-		return NULL;
-	}
-
-	data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
-
-	return data;
-}
-#endif /* CONFIG_HS20 */
-
-
 static void eap_tls_free_key(struct eap_tls_data *data)
 {
 	if (data->key_data) {
@@ -478,31 +451,3 @@
 	return eap_peer_method_register(eap);
 }
 #endif /* EAP_UNAUTH_TLS */
-
-
-#ifdef CONFIG_HS20
-int eap_peer_wfa_unauth_tls_register(void)
-{
-	struct eap_method *eap;
-
-	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
-				    EAP_VENDOR_WFA_NEW,
-				    EAP_VENDOR_WFA_UNAUTH_TLS,
-				    "WFA-UNAUTH-TLS");
-	if (eap == NULL)
-		return -1;
-
-	eap->init = eap_wfa_unauth_tls_init;
-	eap->deinit = eap_tls_deinit;
-	eap->process = eap_tls_process;
-	eap->isKeyAvailable = eap_tls_isKeyAvailable;
-	eap->getKey = eap_tls_getKey;
-	eap->get_status = eap_tls_get_status;
-	eap->has_reauth_data = eap_tls_has_reauth_data;
-	eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
-	eap->init_for_reauth = eap_tls_init_for_reauth;
-	eap->get_emsk = eap_tls_get_emsk;
-
-	return eap_peer_method_register(eap);
-}
-#endif /* CONFIG_HS20 */
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 5594216..47d21c6 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -23,10 +23,6 @@
 		return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
 				     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
 				     code, identifier);
-	if (type == EAP_WFA_UNAUTH_TLS_TYPE)
-		return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
-				     EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
-				     code, identifier);
 	return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
 			     identifier);
 }
@@ -184,8 +180,6 @@
 		/* RFC 7170 requires TLS v1.2 or newer to be used with TEAP */
 		params->flags |= TLS_CONN_DISABLE_TLSv1_0 |
 			TLS_CONN_DISABLE_TLSv1_1;
-		if (config->teap_anon_dh)
-			params->flags |= TLS_CONN_TEAP_ANON_DH;
 	}
 	if (data->eap_type == EAP_TYPE_FAST ||
 	    data->eap_type == EAP_TYPE_TEAP ||
@@ -197,8 +191,7 @@
 	}
 #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) {
+	    data->eap_type == EAP_UNAUTH_TLS_TYPE) {
 		/* While the current EAP-TLS implementation is more or less
 		 * complete for TLS v1.3, there has been only minimal
 		 * interoperability testing with other implementations, so
@@ -934,10 +927,6 @@
 		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
 				       EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
 				       &left);
-	else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
-		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
-				       EAP_VENDOR_WFA_UNAUTH_TLS, reqData,
-				       &left);
 	else
 		pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
 				       &left);
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 3348634..2551ff5 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -99,7 +99,6 @@
 
 /* stub type used as a flag for UNAUTH-TLS */
 #define EAP_UNAUTH_TLS_TYPE 255
-#define EAP_WFA_UNAUTH_TLS_TYPE 254
 
 
 int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index d965a25..dd02b0c 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -35,7 +35,6 @@
 	size_t salt_len;
 	int phase2;
 	int force_version;
-	unsigned int remediation:1;
 	unsigned int macacl:1;
 	int ttls_auth; /* bitfield of
 			* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
@@ -199,7 +198,6 @@
 	 */
 	int pac_key_refresh_time;
 	int eap_teap_auth;
-	int eap_teap_pac_no_inner;
 	int eap_teap_separate_result;
 	enum eap_teap_id {
 		EAP_TEAP_ID_ALLOW_ANY = 0,
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
index e32c6e4..d624523 100644
--- a/src/eap_server/eap_server_teap.c
+++ b/src/eap_server/eap_server_teap.c
@@ -1,6 +1,6 @@
 /*
  * EAP-TEAP server (RFC 7170)
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,7 +9,6 @@
 #include "includes.h"
 
 #include "common.h"
-#include "crypto/aes_wrap.h"
 #include "crypto/tls.h"
 #include "crypto/random.h"
 #include "eap_common/eap_teap_common.h"
@@ -20,17 +19,11 @@
 static void eap_teap_reset(struct eap_sm *sm, void *priv);
 
 
-/* Private PAC-Opaque TLV types */
-#define PAC_OPAQUE_TYPE_PAD 0
-#define PAC_OPAQUE_TYPE_KEY 1
-#define PAC_OPAQUE_TYPE_LIFETIME 2
-#define PAC_OPAQUE_TYPE_IDENTITY 3
-
 struct eap_teap_data {
 	struct eap_ssl_data ssl;
 	enum {
 		START, PHASE1, PHASE1B, PHASE2_START, PHASE2_ID,
-		PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC,
+		PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING,
 		FAILURE_SEND_RESULT, SUCCESS_SEND_RESULT, SUCCESS, FAILURE
 	} state;
 
@@ -44,34 +37,29 @@
 	u8 crypto_binding_nonce[32];
 	int final_result;
 
+	u8 simck[EAP_TEAP_SIMCK_LEN];
 	u8 simck_msk[EAP_TEAP_SIMCK_LEN];
 	u8 cmk_msk[EAP_TEAP_CMK_LEN];
 	u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
 	u8 cmk_emsk[EAP_TEAP_CMK_LEN];
 	int simck_idx;
-	int cmk_emsk_available;
+	bool cmk_emsk_available;
 
-	u8 pac_opaque_encr[16];
 	u8 *srv_id;
 	size_t srv_id_len;
 	char *srv_id_info;
 
 	unsigned int basic_auth_not_done:1;
 	unsigned int inner_eap_not_done:1;
-	int anon_provisioning;
 	int skipped_inner_auth;
-	int send_new_pac; /* server triggered re-keying of Tunnel PAC */
 	struct wpabuf *pending_phase2_resp;
 	struct wpabuf *server_outer_tlvs;
 	struct wpabuf *peer_outer_tlvs;
-	u8 *identity; /* from PAC-Opaque or client certificate */
+	u8 *identity; /* from client certificate */
 	size_t identity_len;
 	int eap_seq;
 	int tnc_started;
 
-	int pac_key_lifetime;
-	int pac_key_refresh_time;
-
 	enum teap_error_codes error_code;
 	enum teap_identity_types cur_id_type;
 
@@ -104,8 +92,6 @@
 		return "PHASE2_METHOD";
 	case CRYPTO_BINDING:
 		return "CRYPTO_BINDING";
-	case REQUEST_PAC:
-		return "REQUEST_PAC";
 	case FAILURE_SEND_RESULT:
 		return "FAILURE_SEND_RESULT";
 	case SUCCESS_SEND_RESULT:
@@ -137,158 +123,6 @@
 }
 
 
-static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
-				      const u8 *client_random,
-				      const u8 *server_random,
-				      u8 *master_secret)
-{
-	struct eap_teap_data *data = ctx;
-	const u8 *pac_opaque;
-	size_t pac_opaque_len;
-	u8 *buf, *pos, *end, *pac_key = NULL;
-	os_time_t lifetime = 0;
-	struct os_time now;
-	u8 *identity = NULL;
-	size_t identity_len = 0;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket (PAC-Opaque)",
-		    ticket, len);
-
-	if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) {
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore invalid SessionTicket");
-		return 0;
-	}
-
-	pac_opaque_len = WPA_GET_BE16(ticket + 2);
-	pac_opaque = ticket + 4;
-	if (pac_opaque_len < 8 || pac_opaque_len % 8 ||
-	    pac_opaque_len > len - 4) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Ignore invalid PAC-Opaque (len=%lu left=%lu)",
-			   (unsigned long) pac_opaque_len,
-			   (unsigned long) len);
-		return 0;
-	}
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Received PAC-Opaque",
-		    pac_opaque, pac_opaque_len);
-
-	buf = os_malloc(pac_opaque_len - 8);
-	if (!buf) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to allocate memory for decrypting PAC-Opaque");
-		return 0;
-	}
-
-	if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
-		       (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) {
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Failed to decrypt PAC-Opaque");
-		os_free(buf);
-		/*
-		 * This may have been caused by server changing the PAC-Opaque
-		 * encryption key, so just ignore this PAC-Opaque instead of
-		 * failing the authentication completely. Provisioning can now
-		 * be used to provision a new PAC.
-		 */
-		return 0;
-	}
-
-	end = buf + pac_opaque_len - 8;
-	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Decrypted PAC-Opaque",
-			buf, end - buf);
-
-	pos = buf;
-	while (end - pos > 1) {
-		u8 id, elen;
-
-		id = *pos++;
-		elen = *pos++;
-		if (elen > end - pos)
-			break;
-
-		switch (id) {
-		case PAC_OPAQUE_TYPE_PAD:
-			goto done;
-		case PAC_OPAQUE_TYPE_KEY:
-			if (elen != EAP_TEAP_PAC_KEY_LEN) {
-				wpa_printf(MSG_DEBUG,
-					   "EAP-TEAP: Invalid PAC-Key length %d",
-					   elen);
-				os_free(buf);
-				return -1;
-			}
-			pac_key = pos;
-			wpa_hexdump_key(MSG_DEBUG,
-					"EAP-TEAP: PAC-Key from decrypted PAC-Opaque",
-					pac_key, EAP_TEAP_PAC_KEY_LEN);
-			break;
-		case PAC_OPAQUE_TYPE_LIFETIME:
-			if (elen != 4) {
-				wpa_printf(MSG_DEBUG,
-					   "EAP-TEAP: Invalid PAC-Key lifetime length %d",
-					   elen);
-				os_free(buf);
-				return -1;
-			}
-			lifetime = WPA_GET_BE32(pos);
-			break;
-		case PAC_OPAQUE_TYPE_IDENTITY:
-			identity = pos;
-			identity_len = elen;
-			break;
-		}
-
-		pos += elen;
-	}
-done:
-
-	if (!pac_key) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: No PAC-Key included in PAC-Opaque");
-		os_free(buf);
-		return -1;
-	}
-
-	if (identity) {
-		wpa_hexdump_ascii(MSG_DEBUG,
-				  "EAP-TEAP: Identity from PAC-Opaque",
-				  identity, identity_len);
-		os_free(data->identity);
-		data->identity = os_malloc(identity_len);
-		if (data->identity) {
-			os_memcpy(data->identity, identity, identity_len);
-			data->identity_len = identity_len;
-		}
-	}
-
-	if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Key not valid anymore (lifetime=%ld now=%ld)",
-			   lifetime, now.sec);
-		data->send_new_pac = 2;
-		/*
-		 * Allow PAC to be used to allow a PAC update with some level
-		 * of server authentication (i.e., do not fall back to full TLS
-		 * handshake since we cannot be sure that the peer would be
-		 * able to validate server certificate now). However, reject
-		 * the authentication since the PAC was not valid anymore. Peer
-		 * can connect again with the newly provisioned PAC after this.
-		 */
-	} else if (lifetime - now.sec < data->pac_key_refresh_time) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Key soft timeout; send an update if authentication succeeds");
-		data->send_new_pac = 1;
-	}
-
-	/* EAP-TEAP uses PAC-Key as the TLS master_secret */
-	os_memcpy(master_secret, pac_key, EAP_TEAP_PAC_KEY_LEN);
-
-	os_free(buf);
-
-	return 1;
-}
-
-
 static int eap_teap_derive_key_auth(struct eap_sm *sm,
 				    struct eap_teap_data *data)
 {
@@ -297,13 +131,14 @@
 	/* RFC 7170, Section 5.1 */
 	res = tls_connection_export_key(sm->cfg->ssl_ctx, data->ssl.conn,
 					TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
-					data->simck_msk, EAP_TEAP_SIMCK_LEN);
+					data->simck, EAP_TEAP_SIMCK_LEN);
 	if (res)
 		return res;
 	wpa_hexdump_key(MSG_DEBUG,
 			"EAP-TEAP: session_key_seed (S-IMCK[0])",
-			data->simck_msk, EAP_TEAP_SIMCK_LEN);
-	os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+			data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_msk, data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_emsk, data->simck, EAP_TEAP_SIMCK_LEN);
 	data->simck_idx = 0;
 	return 0;
 }
@@ -319,9 +154,7 @@
 		   data->simck_idx + 1);
 
 	if (sm->cfg->eap_teap_auth == 1)
-		return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
-							 data->simck_msk,
-							 data->cmk_msk);
+		goto out; /* no MSK derived in Basic-Password-Auth */
 
 	if (!data->phase2_method || !data->phase2_priv) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
@@ -343,8 +176,8 @@
 						     &emsk_len);
 	}
 
-	res = eap_teap_derive_imck(data->tls_cs,
-				   data->simck_msk, data->simck_emsk,
+out:
+	res = eap_teap_derive_imck(data->tls_cs, data->simck,
 				   msk, msk_len, emsk, emsk_len,
 				   data->simck_msk, data->cmk_msk,
 				   data->simck_emsk, data->cmk_emsk);
@@ -352,8 +185,7 @@
 	bin_clear_free(emsk, emsk_len);
 	if (res == 0) {
 		data->simck_idx++;
-		if (emsk)
-			data->cmk_emsk_available = 1;
+		data->cmk_emsk_available = emsk != NULL;
 	}
 	return 0;
 }
@@ -377,28 +209,6 @@
 		return NULL;
 	}
 
-	/* TODO: Add anon-DH TLS cipher suites (and if one is negotiated,
-	 * enforce inner EAP with mutual authentication to be used) */
-
-	if (tls_connection_set_session_ticket_cb(sm->cfg->ssl_ctx,
-						 data->ssl.conn,
-						 eap_teap_session_ticket_cb,
-						 data) < 0) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: Failed to set SessionTicket callback");
-		eap_teap_reset(sm, data);
-		return NULL;
-	}
-
-	if (!sm->cfg->pac_opaque_encr_key) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: No PAC-Opaque encryption key configured");
-		eap_teap_reset(sm, data);
-		return NULL;
-	}
-	os_memcpy(data->pac_opaque_encr, sm->cfg->pac_opaque_encr_key,
-		  sizeof(data->pac_opaque_encr));
-
 	if (!sm->cfg->eap_fast_a_id) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID configured");
 		eap_teap_reset(sm, data);
@@ -424,16 +234,6 @@
 		return NULL;
 	}
 
-	/* PAC-Key lifetime in seconds (hard limit) */
-	data->pac_key_lifetime = sm->cfg->pac_key_lifetime;
-
-	/*
-	 * PAC-Key refresh time in seconds (soft limit on remaining hard
-	 * limit). The server will generate a new PAC-Key when this number of
-	 * seconds (or fewer) of the lifetime remains.
-	 */
-	data->pac_key_refresh_time = sm->cfg->pac_key_refresh_time;
-
 	return data;
 }
 
@@ -457,7 +257,6 @@
 	forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
 	forced_memzero(data->cmk_msk, EAP_TEAP_CMK_LEN);
 	forced_memzero(data->cmk_emsk, EAP_TEAP_CMK_LEN);
-	forced_memzero(data->pac_opaque_encr, sizeof(data->pac_opaque_encr));
 	bin_clear_free(data, sizeof(*data));
 }
 
@@ -504,8 +303,6 @@
 
 static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data)
 {
-	char cipher[64];
-
 	wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2");
 
 	if (!data->identity && sm->cfg->eap_teap_auth == 2) {
@@ -525,18 +322,6 @@
 	wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x",
 		   data->tls_cs);
 
-	if (tls_get_cipher(sm->cfg->ssl_ctx, data->ssl.conn,
-			   cipher, sizeof(cipher)) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to get cipher information");
-		eap_teap_state(data, FAILURE);
-		return -1;
-	}
-	data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
-
-	if (data->anon_provisioning)
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Anonymous provisioning");
-
 	if (eap_teap_derive_key_auth(sm, data) < 0) {
 		eap_teap_state(data, FAILURE);
 		return -1;
@@ -626,8 +411,7 @@
 	if (!buf)
 		return NULL;
 
-	if (data->send_new_pac || data->anon_provisioning ||
-	    data->basic_auth_not_done || data->inner_eap_not_done ||
+	if (data->basic_auth_not_done || data->inner_eap_not_done ||
 	    data->phase2_method || sm->cfg->eap_teap_separate_result)
 		data->final_result = 0;
 	else
@@ -663,8 +447,6 @@
 	cb->length = host_to_be16(sizeof(*cb) - sizeof(struct teap_tlv_hdr));
 	cb->version = EAP_TEAP_VERSION;
 	cb->received_version = data->peer_version;
-	/* FIX: RFC 7170 is not clear on which Flags value to use when
-	 * Crypto-Binding TLV is used with Basic-Password-Auth */
 	flags = data->cmk_emsk_available ?
 		TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
 		TEAP_CRYPTO_BINDING_MSK_CMAC;
@@ -714,144 +496,6 @@
 }
 
 
-static struct wpabuf * eap_teap_build_pac(struct eap_sm *sm,
-					  struct eap_teap_data *data)
-{
-	u8 pac_key[EAP_TEAP_PAC_KEY_LEN];
-	u8 *pac_buf, *pac_opaque;
-	struct wpabuf *buf;
-	u8 *pos;
-	size_t buf_len, srv_id_info_len, pac_len;
-	struct teap_tlv_hdr *pac_tlv;
-	struct pac_attr_hdr *pac_info;
-	struct teap_tlv_result *result;
-	struct os_time now;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Build a new PAC");
-
-	if (random_get_bytes(pac_key, EAP_TEAP_PAC_KEY_LEN) < 0 ||
-	    os_get_time(&now) < 0)
-		return NULL;
-	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Generated PAC-Key",
-			pac_key, EAP_TEAP_PAC_KEY_LEN);
-
-	pac_len = (2 + EAP_TEAP_PAC_KEY_LEN) + (2 + 4) +
-		(2 + sm->identity_len) + 8;
-	pac_buf = os_malloc(pac_len);
-	if (!pac_buf)
-		return NULL;
-
-	srv_id_info_len = os_strlen(data->srv_id_info);
-
-	pos = pac_buf;
-	*pos++ = PAC_OPAQUE_TYPE_KEY;
-	*pos++ = EAP_TEAP_PAC_KEY_LEN;
-	os_memcpy(pos, pac_key, EAP_TEAP_PAC_KEY_LEN);
-	pos += EAP_TEAP_PAC_KEY_LEN;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Key lifetime: %u seconds",
-		   data->pac_key_lifetime);
-	*pos++ = PAC_OPAQUE_TYPE_LIFETIME;
-	*pos++ = 4;
-	WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
-	pos += 4;
-
-	if (sm->identity) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Opaque Identity",
-				  sm->identity, sm->identity_len);
-		*pos++ = PAC_OPAQUE_TYPE_IDENTITY;
-		*pos++ = sm->identity_len;
-		os_memcpy(pos, sm->identity, sm->identity_len);
-		pos += sm->identity_len;
-	}
-
-	pac_len = pos - pac_buf;
-	while (pac_len % 8) {
-		*pos++ = PAC_OPAQUE_TYPE_PAD;
-		pac_len++;
-	}
-
-	pac_opaque = os_malloc(pac_len + 8);
-	if (!pac_opaque) {
-		os_free(pac_buf);
-		return NULL;
-	}
-	if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
-		     pac_len / 8, pac_buf, pac_opaque) < 0) {
-		os_free(pac_buf);
-		os_free(pac_opaque);
-		return NULL;
-	}
-	os_free(pac_buf);
-
-	pac_len += 8;
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pac_opaque, pac_len);
-
-	buf_len = sizeof(*pac_tlv) +
-		sizeof(struct pac_attr_hdr) + EAP_TEAP_PAC_KEY_LEN +
-		sizeof(struct pac_attr_hdr) + pac_len +
-		data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
-	buf = wpabuf_alloc(buf_len);
-	if (!buf) {
-		os_free(pac_opaque);
-		return NULL;
-	}
-
-	/* Result TLV */
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Result TLV (status=SUCCESS)");
-	result = wpabuf_put(buf, sizeof(*result));
-	WPA_PUT_BE16((u8 *) &result->tlv_type,
-		     TEAP_TLV_MANDATORY | TEAP_TLV_RESULT);
-	WPA_PUT_BE16((u8 *) &result->length, 2);
-	WPA_PUT_BE16((u8 *) &result->status, TEAP_STATUS_SUCCESS);
-
-	/* PAC TLV */
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV");
-	pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
-	pac_tlv->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_PAC);
-
-	/* PAC-Key */
-	eap_teap_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_TEAP_PAC_KEY_LEN);
-
-	/* PAC-Opaque */
-	eap_teap_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len);
-	os_free(pac_opaque);
-
-	/* PAC-Info */
-	pac_info = wpabuf_put(buf, sizeof(*pac_info));
-	pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO);
-
-	/* PAC-Lifetime (inside PAC-Info) */
-	eap_teap_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
-	wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
-
-	/* A-ID (inside PAC-Info) */
-	eap_teap_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
-
-	/* Note: headers may be misaligned after A-ID */
-
-	if (sm->identity) {
-		eap_teap_put_tlv(buf, PAC_TYPE_I_ID, sm->identity,
-				 sm->identity_len);
-	}
-
-	/* A-ID-Info (inside PAC-Info) */
-	eap_teap_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
-			 srv_id_info_len);
-
-	/* PAC-Type (inside PAC-Info) */
-	eap_teap_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
-	wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC);
-
-	/* Update PAC-Info and PAC TLV Length fields */
-	pos = wpabuf_put(buf, 0);
-	pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
-	pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
-
-	return buf;
-}
-
-
 static int eap_teap_encrypt_phase2(struct eap_sm *sm,
 				   struct eap_teap_data *data,
 				   struct wpabuf *plain, int piggyback)
@@ -976,9 +620,6 @@
 				eap_teap_state(data, PHASE2_METHOD);
 		}
 		break;
-	case REQUEST_PAC:
-		req = eap_teap_build_pac(sm, data);
-		break;
 	case FAILURE_SEND_RESULT:
 		req = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
 		if (data->error_code)
@@ -1166,18 +807,9 @@
 		}
 
 		eap_teap_state(data, PHASE2_METHOD);
-		if (data->anon_provisioning) {
-			/* TODO: Allow any inner EAP method that provides
-			 * mutual authentication and EMSK derivation (i.e.,
-			 * EAP-pwd or EAP-EKE). */
-			next_vendor = EAP_VENDOR_IETF;
-			next_type = EAP_TYPE_PWD;
-			sm->user_eap_method_index = 0;
-		} else {
-			next_vendor = sm->user->methods[0].vendor;
-			next_type = sm->user->methods[0].method;
-			sm->user_eap_method_index = 1;
-		}
+		next_vendor = sm->user->methods[0].vendor;
+		next_type = sm->user->methods[0].method;
+		sm->user_eap_method_index = 1;
 		wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %u:%u",
 			   next_vendor, next_type);
 		break;
@@ -1509,25 +1141,23 @@
 		return -1;
 	}
 
+	if (data->cmk_emsk_available &&
+	    (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+	     flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC)) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK");
+		os_memcpy(data->simck, data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+	} else if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+		   flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK");
+		os_memcpy(data->simck, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK[j]",
+			data->simck, EAP_TEAP_SIMCK_LEN);
+
 	return 0;
 }
 
 
-static int eap_teap_pac_type(u8 *pac, size_t len, u16 type)
-{
-	struct teap_attr_pac_type *tlv;
-
-	if (!pac || len != sizeof(*tlv))
-		return 0;
-
-	tlv = (struct teap_attr_pac_type *) pac;
-
-	return be_to_host16(tlv->type) == PAC_TYPE_PAC_TYPE &&
-		be_to_host16(tlv->length) == 2 &&
-		be_to_host16(tlv->pac_type) == type;
-}
-
-
 static void eap_teap_process_phase2_tlvs(struct eap_sm *sm,
 					 struct eap_teap_data *data,
 					 struct wpabuf *in_data)
@@ -1556,34 +1186,6 @@
 		return;
 	}
 
-	if (data->state == REQUEST_PAC) {
-		u16 type, len, res;
-
-		if (!tlv.pac || tlv.pac_len < 6) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: No PAC Acknowledgement received");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		type = WPA_GET_BE16(tlv.pac);
-		len = WPA_GET_BE16(tlv.pac + 2);
-		res = WPA_GET_BE16(tlv.pac + 4);
-
-		if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 ||
-		    res != TEAP_STATUS_SUCCESS) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC TLV did not contain acknowledgement");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Acknowledgement received - PAC provisioning succeeded");
-		eap_teap_state(data, SUCCESS);
-		return;
-	}
-
 	if (check_crypto_binding) {
 		if (!tlv.crypto_binding) {
 			wpa_printf(MSG_DEBUG,
@@ -1623,42 +1225,10 @@
 				   "EAP-TEAP: Authentication completed successfully");
 		}
 
-		if (data->anon_provisioning &&
-		    sm->cfg->eap_fast_prov != ANON_PROV &&
-		    sm->cfg->eap_fast_prov != BOTH_PROV) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Client is trying to use unauthenticated provisioning which is disabled");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		if (sm->cfg->eap_fast_prov != AUTH_PROV &&
-		    sm->cfg->eap_fast_prov != BOTH_PROV &&
-		    tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
-		    eap_teap_pac_type(tlv.pac, tlv.pac_len,
-				      PAC_TYPE_TUNNEL_PAC)) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Client is trying to use authenticated provisioning which is disabled");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		if (data->anon_provisioning ||
-		    (tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
-		     eap_teap_pac_type(tlv.pac, tlv.pac_len,
-				       PAC_TYPE_TUNNEL_PAC))) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Requested a new Tunnel PAC");
-			eap_teap_state(data, REQUEST_PAC);
-		} else if (data->send_new_pac) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Server triggered re-keying of Tunnel PAC");
-			eap_teap_state(data, REQUEST_PAC);
-		} else if (data->final_result) {
+		if (data->final_result)
 			eap_teap_state(data, SUCCESS);
-		} else if (sm->cfg->eap_teap_separate_result) {
+		else if (sm->cfg->eap_teap_separate_result)
 			eap_teap_state(data, SUCCESS_SEND_RESULT);
-		}
 	}
 
 	if (tlv.basic_auth_resp) {
@@ -1810,7 +1380,7 @@
 	enum eap_type next_type;
 
 	if (data->identity) {
-		/* Used PAC and identity is from PAC-Opaque */
+		/* Identity is from client certificate */
 		os_free(sm->identity);
 		sm->identity = data->identity;
 		data->identity = NULL;
@@ -1823,17 +1393,16 @@
 			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_NONE;
 			eap_teap_state(data, PHASE2_METHOD);
-		} else if (sm->cfg->eap_teap_pac_no_inner ||
-			sm->cfg->eap_teap_auth == 2) {
+		} else if (sm->cfg->eap_teap_auth == 2) {
 			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Used PAC or client certificate and identity already known - skip inner auth");
+				   "EAP-TEAP: Used client certificate and identity already known - skip inner auth");
 			data->skipped_inner_auth = 1;
-			/* FIX: Need to derive CMK here. However, how is that
-			 * supposed to be done? RFC 7170 does not tell that for
-			 * the no-inner-auth case. */
-			eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
-							  data->simck_msk,
-							  data->cmk_msk);
+			if (eap_teap_derive_imck(data->tls_cs, data->simck,
+						 NULL, 0, NULL, 0,
+						 data->simck_msk, data->cmk_msk,
+						 data->simck_emsk,
+						 data->cmk_emsk))
+				return -1;
 			eap_teap_state(data, CRYPTO_BINDING);
 			return 1;
 		} else if (sm->cfg->eap_teap_auth == 1) {
@@ -1880,7 +1449,6 @@
 	case PHASE2_BASIC_AUTH:
 	case PHASE2_METHOD:
 	case CRYPTO_BINDING:
-	case REQUEST_PAC:
 	case SUCCESS_SEND_RESULT:
 		eap_teap_process_phase2(sm, data, data->ssl.tls_in);
 		break;
@@ -2043,9 +1611,7 @@
 	if (!eapKeyData)
 		return NULL;
 
-	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
-	 * is used in this derivation */
-	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk,
+	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck,
 				    eapKeyData) < 0) {
 		os_free(eapKeyData);
 		return NULL;
@@ -2068,9 +1634,7 @@
 	if (!eapKeyData)
 		return NULL;
 
-	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
-	 * is used in this derivation */
-	if (eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk,
+	if (eap_teap_derive_eap_emsk(data->tls_cs, data->simck,
 				     eapKeyData) < 0) {
 		os_free(eapKeyData);
 		return NULL;
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 443c293..0caa4c3 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -114,29 +114,6 @@
 #endif /* EAP_SERVER_UNAUTH_TLS */
 
 
-#ifdef CONFIG_HS20
-static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
-{
-	struct eap_tls_data *data;
-
-	data = os_zalloc(sizeof(*data));
-	if (data == NULL)
-		return NULL;
-	data->state = START;
-
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
-				    EAP_WFA_UNAUTH_TLS_TYPE)) {
-		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
-		eap_tls_reset(sm, data);
-		return NULL;
-	}
-
-	data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
-	return data;
-}
-#endif /* CONFIG_HS20 */
-
-
 static void eap_tls_reset(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
@@ -237,10 +214,6 @@
 		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
 				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
 				       &len);
-	else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
-		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
-				       EAP_VENDOR_WFA_UNAUTH_TLS, respData,
-				       &len);
 	else
 		pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
 				       respData, &len);
@@ -474,30 +447,3 @@
 	return eap_server_method_register(eap);
 }
 #endif /* EAP_SERVER_UNAUTH_TLS */
-
-
-#ifdef CONFIG_HS20
-int eap_server_wfa_unauth_tls_register(void)
-{
-	struct eap_method *eap;
-
-	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
-				      EAP_VENDOR_WFA_NEW,
-				      EAP_VENDOR_WFA_UNAUTH_TLS,
-				      "WFA-UNAUTH-TLS");
-	if (eap == NULL)
-		return -1;
-
-	eap->init = eap_wfa_unauth_tls_init;
-	eap->reset = eap_tls_reset;
-	eap->buildReq = eap_tls_buildReq;
-	eap->check = eap_tls_check;
-	eap->process = eap_tls_process;
-	eap->isDone = eap_tls_isDone;
-	eap->getKey = eap_tls_getKey;
-	eap->isSuccess = eap_tls_isSuccess;
-	eap->get_emsk = eap_tls_get_emsk;
-
-	return eap_server_method_register(eap);
-}
-#endif /* CONFIG_HS20 */
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 717af2e..81d1eed 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -25,10 +25,6 @@
 		return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
 				     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
 				     code, identifier);
-	else if (type == EAP_WFA_UNAUTH_TLS_TYPE)
-		return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
-				     EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
-				     code, identifier);
 	return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
 			     identifier);
 }
@@ -541,10 +537,6 @@
 		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
 				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
 				       &left);
-	else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
-		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
-				       EAP_VENDOR_WFA_UNAUTH_TLS, respData,
-				       &left);
 	else
 		pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData,
 				       &left);
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index ad28c79..2a8faf9 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -72,7 +72,6 @@
 
 /* stub type used as a flag for UNAUTH-TLS */
 #define EAP_UNAUTH_TLS_TYPE 255
-#define EAP_WFA_UNAUTH_TLS_TYPE 254
 
 
 struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len,
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index e1b82eb..af962ee 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -237,7 +237,7 @@
 	if (!from_initialize && !pre_auth_logoff) {
 		if (sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
 					   sm->flags & EAPOL_SM_PREAUTH,
-					   sm->remediation, logoff)) {
+					   logoff)) {
 			wpa_printf(MSG_DEBUG,
 				   "EAPOL: Do not restart since lower layers will disconnect the port after EAPOL-Logoff");
 			sm->stopped = true;
@@ -298,8 +298,7 @@
 				   eap_server_get_name(0, sm->eap_type_supp));
 	}
 	sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
-			       sm->flags & EAPOL_SM_PREAUTH, sm->remediation,
-			       false);
+			       sm->flags & EAPOL_SM_PREAUTH, false);
 }
 
 
@@ -327,8 +326,7 @@
 	if (sm->authSuccess)
 		sm->authenticated++;
 	sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1,
-			       sm->flags & EAPOL_SM_PREAUTH, sm->remediation,
-			       false);
+			       sm->flags & EAPOL_SM_PREAUTH, false);
 }
 
 
@@ -1029,13 +1027,9 @@
 				 struct eap_user *user)
 {
 	struct eapol_state_machine *sm = ctx;
-	int ret;
 
-	ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
-					 identity_len, phase2, user);
-	if (user->remediation)
-		sm->remediation = 1;
-	return ret;
+	return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
+					  identity_len, phase2, user);
 }
 
 
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 7296a3a..83f5c5d 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -47,7 +47,7 @@
 	void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data,
 			 size_t datalen);
 	bool (*finished)(void *ctx, void *sta_ctx, int success, int preauth,
-			 int remediation, bool logoff);
+			 bool logoff);
 	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
 			    int phase2, struct eap_user *user);
 	int (*sta_entry_alive)(void *ctx, const u8 *addr);
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index a0cef0f..c970e73 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -168,8 +168,6 @@
 
 	void *sta; /* station context pointer to use in callbacks */
 
-	int remediation;
-
 	u64 acct_multi_session_id;
 
 	unsigned int authenticated; /* The number of times authentication has
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index f3742ea..4503830 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -247,8 +247,16 @@
 	peer->go_neg_conf = NULL;
 	p2p->go_neg_peer = NULL;
 
+#ifdef CONFIG_PASN
+	if (peer->p2p2 && peer->pasn)
+		wpa_pasn_reset(peer->pasn);
+	os_memset(p2p->dev_sae_password, 0, sizeof(p2p->dev_sae_password));
+	os_memset(p2p->peer_sae_password, 0, sizeof(p2p->peer_sae_password));
+#endif /* CONFIG_PASN */
+
 	os_memset(&res, 0, sizeof(res));
 	res.status = status;
+	res.p2p2 = peer->p2p2;
 	os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
 	os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -962,6 +970,16 @@
 	}
 
 	os_free(dev->bootstrap_params);
+
+	wpabuf_free(dev->action_frame_wrapper);
+
+#ifdef CONFIG_PASN
+	if (dev->pasn) {
+		wpa_pasn_reset(dev->pasn);
+		pasn_data_deinit(dev->pasn);
+	}
+#endif /* CONFIG_PASN */
+
 	wpabuf_free(dev->info.wfd_subelems);
 	wpabuf_free(dev->info.vendor_elems);
 	wpabuf_free(dev->go_neg_conf);
@@ -1850,10 +1868,27 @@
 		params->passphrase[p2p->cfg->passphrase_len] = '\0';
 	}
 	p2p->passphrase_set = 0;
+	params->cipher = WPA_CIPHER_CCMP;
+	if (p2p->cfg->pairing_config.pasn_type & 0xc)
+		params->cipher |= WPA_CIPHER_GCMP_256;
+
+	if (params->p2p2) {
+		os_strlcpy(p2p->dev_sae_password, params->passphrase,
+			   sizeof(p2p->dev_sae_password));
+		os_strlcpy(params->sae_password, p2p->dev_sae_password,
+			   sizeof(params->sae_password));
+	}
+
 	return 0;
 }
 
 
+void p2p_set_go_role(struct p2p_data *p2p, bool val)
+{
+	p2p->go_role = val;
+}
+
+
 void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
 {
 	struct p2p_go_neg_results res;
@@ -1915,8 +1950,57 @@
 	wpabuf_free(peer->go_neg_conf);
 	peer->go_neg_conf = NULL;
 
-	p2p_set_state(p2p, P2P_PROVISIONING);
-	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+#ifdef CONFIG_PASN
+	if (peer->p2p2 && peer->pasn) {
+		res.p2p2 = peer->p2p2;
+		res.akmp = peer->pasn->akmp;
+		res.cipher = peer->pasn->cipher;
+
+		if (res.akmp == WPA_KEY_MGMT_PASN) {
+			if (go) {
+				os_strlcpy(res.sae_password,
+					   p2p->dev_sae_password,
+					   sizeof(res.sae_password));
+			} else {
+				if (!os_strlen(p2p->peer_sae_password)) {
+					p2p_dbg(p2p, "No password from peer GO for P2P2 group formation");
+					return;
+				}
+				os_strlcpy(res.sae_password,
+					   p2p->peer_sae_password,
+					   sizeof(res.sae_password));
+			}
+		} else if (res.akmp == WPA_KEY_MGMT_SAE) {
+			if (peer->role == P2P_ROLE_PAIRING_INITIATOR) {
+				pasn_initiator_pmksa_cache_get(
+					peer->pasn->pmksa,
+					peer->pasn->peer_addr,
+					res.pmkid, res.pmk, &res.pmk_len);
+			} else {
+				pasn_responder_pmksa_cache_get(
+					peer->pasn->pmksa,
+					peer->pasn->peer_addr,
+					res.pmkid, res.pmk, &res.pmk_len);
+			}
+		}
+
+		os_memset(p2p->dev_sae_password, 0,
+			  sizeof(p2p->dev_sae_password));
+		os_memset(p2p->peer_sae_password, 0,
+			  sizeof(p2p->peer_sae_password));
+		wpa_pasn_reset(peer->pasn);
+	}
+#endif /* CONFIG_PASN */
+
+	if (p2p->go_role && peer->p2p2) {
+		p2p->cfg->set_go_security_config(p2p->cfg->cb_ctx, &res);
+		p2p->go_role = false;
+	} else {
+		p2p_set_state(p2p, P2P_PROVISIONING);
+		p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+	}
+
+	forced_memzero(&res, sizeof(res));
 }
 
 
@@ -2998,6 +3082,13 @@
 }
 
 
+void p2p_set_dev_addr(struct p2p_data *p2p, const u8 *addr)
+{
+	if (p2p && addr)
+		os_memcpy(p2p->cfg->dev_addr, addr, ETH_ALEN);
+}
+
+
 static void p2p_pairing_info_deinit(struct p2p_data *p2p)
 {
 #ifdef CONFIG_PASN
@@ -3032,8 +3123,10 @@
 	os_memcpy(pairing_info->dev_ik.dik_data,
 		  p2p->cfg->pairing_config.dik_data,
 		  p2p->cfg->pairing_config.dik_len);
+	pairing_info->dev_ik.expiration = 24; /* hours */
 
 	p2p_pairing_info_deinit(p2p);
+
 	p2p->pairing_info = pairing_info;
 #ifdef CONFIG_PASN
 	p2p->initiator_pmksa = pasn_initiator_pmksa_cache_init();
@@ -3928,7 +4021,7 @@
 		p2p_invitation_req_cb(p2p, success);
 		break;
 	case P2P_PENDING_INVITATION_RESPONSE:
-		p2p_invitation_resp_cb(p2p, success);
+		p2p_invitation_resp_cb(p2p, dst, success);
 		break;
 	case P2P_PENDING_DEV_DISC_REQUEST:
 		p2p_dev_disc_req_cb(p2p, success);
@@ -4046,6 +4139,20 @@
 }
 
 
+void p2p_listen_failed(struct p2p_data *p2p, unsigned int freq)
+{
+	if (freq != p2p->pending_listen_freq) {
+		p2p_dbg(p2p,
+			"Unexpected listen failed callback for freq=%u (pending_listen_freq=%u)",
+			freq, p2p->pending_listen_freq);
+		return;
+	}
+
+	p2p_dbg(p2p, "Listen failed on freq=%u", freq);
+	p2p->pending_listen_freq = 0;
+}
+
+
 static void p2p_timeout_connect(struct p2p_data *p2p)
 {
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
@@ -4226,9 +4333,11 @@
 			p2p_dbg(p2p, "Invitation Request retry limit reached");
 			if (p2p->cfg->invitation_result)
 				p2p->cfg->invitation_result(
-					p2p->cfg->cb_ctx, -1, NULL, NULL,
+					p2p->cfg->cb_ctx, -1, NULL, 0, NULL,
+					NULL,
 					p2p->invite_peer->info.p2p_device_addr,
-					0, 0);
+					0, 0, NULL, NULL, 0,
+					p2p->invite_go_dev_addr);
 		}
 		p2p_set_state(p2p, P2P_IDLE);
 	}
@@ -4868,6 +4977,108 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+
+void p2p_set_pairing_setup(struct p2p_data *p2p, int pairing_setup)
+{
+	p2p_dbg(p2p, "Pairing Setup %s",
+		pairing_setup ? "Enabled" : "Disabled");
+	if (pairing_setup) {
+		p2p->cfg->pairing_config.pairing_capable = true;
+		p2p->cfg->pairing_config.enable_pairing_setup = true;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_setup = true;
+	} else {
+		p2p->cfg->pairing_config.pairing_capable = false;
+		p2p->cfg->pairing_config.enable_pairing_setup = false;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_setup = false;
+	}
+}
+
+
+void p2p_set_pairing_cache(struct p2p_data *p2p, int pairing_cache)
+{
+	p2p_dbg(p2p, "Pairing Cache %s",
+		pairing_cache ? "Enabled" : "Disabled");
+	if (pairing_cache) {
+		p2p->cfg->pairing_config.enable_pairing_cache = true;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_cache = true;
+	} else {
+		p2p->cfg->pairing_config.enable_pairing_cache = false;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_cache = false;
+	}
+}
+
+
+void p2p_set_bootstrapmethods(struct p2p_data *p2p, int bootstrap_methods)
+{
+	p2p_dbg(p2p, "Bootstraping methods: 0x%x", bootstrap_methods);
+	p2p->cfg->pairing_config.bootstrap_methods = bootstrap_methods;
+	if (p2p->pairing_info)
+		p2p->pairing_info->supported_bootstrap = bootstrap_methods;
+}
+
+
+void p2p_set_pasn_type(struct p2p_data *p2p, u8 pasn_type)
+{
+	p2p_dbg(p2p, "PASN type: 0x%x", pasn_type);
+	p2p->cfg->pairing_config.pasn_type = pasn_type;
+}
+
+
+void p2p_set_comeback_after(struct p2p_data *p2p, int comeback_after)
+{
+	p2p_dbg(p2p, "Comeback after: %d", comeback_after);
+	p2p->cfg->comeback_after = comeback_after;
+}
+
+
+void p2p_set_reg_info(struct p2p_data *p2p, u8 val)
+{
+	p2p->cfg->reg_info = val;
+}
+
+
+void p2p_set_twt_power_mgmt(struct p2p_data *p2p, int val)
+{
+	p2p_dbg(p2p, "TWT-based P2P Power Mgmt: %s",
+		     val ? "Enabled" : "Disabled");
+	if (val)
+		p2p->cfg->twt_power_mgmt = true;
+	else
+		p2p->cfg->twt_power_mgmt = false;
+}
+
+
+void p2p_set_chan_switch_req_enable(struct p2p_data *p2p, bool val)
+{
+	p2p->cfg->chan_switch_req_enable = val;
+}
+
+
+void p2p_set_invitation_op_freq(struct p2p_data *p2p, int freq)
+{
+	u8 op_class, channel;
+
+	if (freq == -1) {
+		p2p->cfg->inv_op_class = 0;
+		p2p->cfg->inv_op_channel = 0;
+		return;
+	}
+
+	if (p2p_freq_to_channel(freq, &op_class, &channel) < 0)
+		return;
+
+	p2p->cfg->inv_op_class = op_class;
+	p2p->cfg->inv_op_channel = channel;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
 				 u8 *op_channel,
 				 struct wpa_freq_range_list *avoid_list,
@@ -5026,6 +5237,28 @@
 }
 
 
+int p2p_get_dev_identity_key(struct p2p_data *p2p, const u8 *dev_addr,
+			     const u8 **dik_data, size_t *dik_len, u8 *cipher)
+{
+	if (!p2p || !p2p->peer_dik_len) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P2: Failed to get device identity key for "
+			   MACSTR, MAC2STR(dev_addr));
+		return -1;
+	}
+
+	*dik_data = p2p->peer_dik_data;
+	*dik_len = p2p->peer_dik_len;
+	*cipher = p2p->dik_cipher_version;
+
+	/* Reset DIK length to invalidate DIK for successive iteration of a new
+	 * peer. */
+	p2p->peer_dik_len = 0;
+
+	return 0;
+}
+
+
 void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
 {
 	os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
@@ -5839,6 +6072,52 @@
 }
 
 
+static int p2p_validate_dira(struct p2p_data *p2p, struct p2p_device *dev,
+			     const u8 *dira, u16 dira_len)
+{
+	if (dira_len < 1 || dira[0] != DIRA_CIPHER_VERSION_128) {
+		p2p_dbg(p2p, "Unsupported DIRA cipher version %d",
+			dira[0]);
+		return 0;
+	}
+
+	if (dira_len < 1 + DEVICE_IDENTITY_NONCE_LEN + DEVICE_IDENTITY_TAG_LEN)
+	{
+		p2p_dbg(p2p, "Truncated DIRA (length %u)", dira_len);
+		return 0;
+	}
+
+	if (p2p->cfg->validate_dira) {
+		const u8 *nonce = &dira[1];
+		const u8 *tag = &dira[1 + DEVICE_IDENTITY_NONCE_LEN];
+
+		return p2p->cfg->validate_dira(p2p->cfg->cb_ctx,
+					       dev->info.p2p_device_addr,
+					       nonce, tag);
+	}
+
+	return 0;
+}
+
+
+void p2p_usd_service_hash(struct p2p_data *p2p, const char *service_name)
+{
+	u8 buf[P2PS_HASH_LEN];
+
+	p2p->usd_service = false;
+
+	if (!service_name)
+		return;
+
+	if (!p2ps_gen_hash(p2p, service_name, buf))
+		return;
+	p2p_dbg(p2p, "USD service %s hash " MACSTR,
+		service_name, MAC2STR(buf));
+	p2p->usd_service = true;
+	os_memcpy(&p2p->p2p_service_hash, buf, P2PS_HASH_LEN);
+}
+
+
 struct wpabuf * p2p_usd_elems(struct p2p_data *p2p)
 {
 	struct wpabuf *buf;
@@ -5884,7 +6163,6 @@
 	if (p2p->pairing_info &&
 	    p2p->cfg->pairing_config.pairing_capable &&
 	    p2p->cfg->pairing_config.enable_pairing_cache &&
-	    p2p->cfg->pairing_config.enable_pairing_verification &&
 	    p2p_derive_nonce_tag(p2p) == 0)
 		p2p_buf_add_dira(buf, p2p);
 
@@ -5950,6 +6228,21 @@
 	if (!ether_addr_equal(peer_addr, p2p_dev_addr))
 		os_memcpy(dev->interface_addr, peer_addr, ETH_ALEN);
 
+	if (msg.dira && msg.dira_len) {
+		dev->info.nonce_tag_valid = false;
+		dev->info.dik_id = p2p_validate_dira(p2p, dev, msg.dira,
+						     msg.dira_len);
+		if (dev->info.dik_id) {
+			os_memcpy(dev->info.nonce, &msg.dira[1],
+				  DEVICE_IDENTITY_NONCE_LEN);
+			os_memcpy(dev->info.tag,
+				  &msg.dira[1 + DEVICE_IDENTITY_NONCE_LEN],
+				  DEVICE_IDENTITY_TAG_LEN);
+			dev->info.pairing_config.dik_cipher = msg.dira[0];
+			dev->info.nonce_tag_valid = true;
+		}
+	}
+
 	p2p_dbg(p2p, "Updated device entry based on USD frame: " MACSTR
 		" dev_capab=0x%x group_capab=0x%x listen_freq=%d",
 		MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
@@ -5957,6 +6250,1165 @@
 
 	p2p->cfg->dev_found(p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
 			    &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE));
+	dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 
 	p2p_parse_free(&msg);
 }
+
+
+int p2p_get_dik_id(struct p2p_data *p2p, const u8 *peer)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer);
+	if (!dev)
+		return 0;
+
+	return dev->info.dik_id;
+}
+
+
+#ifdef CONFIG_PASN
+
+int p2p_config_sae_password(struct p2p_data *p2p, const char *pw)
+{
+	os_memset(p2p->dev_sae_password, 0, sizeof(p2p->dev_sae_password));
+	if (os_strlen(pw) >= sizeof(p2p->dev_sae_password))
+		return -1;
+
+	os_strlcpy(p2p->dev_sae_password, pw, sizeof(p2p->dev_sae_password));
+	return 0;
+}
+
+
+static int p2p_prepare_pasn_extra_ie(struct p2p_data *p2p,
+				     struct wpabuf *extra_ies,
+				     const struct wpabuf *frame, bool add_dira)
+{
+	struct wpabuf *buf, *buf2;
+	size_t len;
+
+	len = 100;
+	if (frame)
+		len += wpabuf_len(frame);
+	buf = wpabuf_alloc(len);
+	if (!buf)
+		return -1;
+
+	/* P2P Capability Extension attribute */
+	p2p_buf_add_pcea(buf, p2p);
+
+	if (add_dira) {
+		/* Device Identity Resolution attribute */
+		p2p_buf_add_dira(buf, p2p);
+	}
+
+	if (frame) {
+		p2p_dbg(p2p, "Add Action frame wrapper for PASN");
+		wpabuf_put_u8(buf, P2P_ATTR_ACTION_FRAME_WRAPPER);
+		wpabuf_put_le16(buf, wpabuf_len(frame));
+		wpabuf_put_buf(buf, frame);
+	}
+
+	buf2 = p2p_encaps_ie(buf, P2P2_IE_VENDOR_TYPE);
+	wpabuf_free(buf);
+
+	if (wpabuf_tailroom(extra_ies) < wpabuf_len(buf2)) {
+		p2p_err(p2p, "Not enough room for P2P2 IE in PASN extra IEs");
+		wpabuf_free(buf2);
+		return -1;
+	}
+	wpabuf_put_buf(extra_ies, buf2);
+	wpabuf_free(buf2);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_pasn_service_hash(struct p2p_data *p2p,
+					     struct wpabuf *extra_ies)
+{
+	struct wpabuf *buf;
+	u8 *ie_len = NULL;
+
+	if (!p2p->usd_service)
+		return extra_ies;
+
+	p2p_dbg(p2p, "Add P2P2 USD service hash in extra IE");
+	buf = wpabuf_alloc(100);
+	if (!buf) {
+		wpabuf_free(extra_ies);
+		return NULL;
+	}
+
+	ie_len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_usd_service_hash(buf, p2p);
+	p2p_buf_update_ie_hdr(buf, ie_len);
+
+	return wpabuf_concat(buf, extra_ies);
+}
+
+
+static struct wpabuf * p2p_pairing_generate_rsnxe(struct p2p_data *p2p,
+						  int akmp)
+{
+	u32 capab;
+	size_t flen = 0;
+	struct wpabuf *buf;
+
+	capab = BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN);
+
+	if (wpa_key_mgmt_sae(akmp))
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+
+	while (capab >> flen * 8)
+		flen++;
+
+	buf = wpabuf_alloc(2 + flen);
+	if (!buf)
+		return NULL;
+
+	if (wpabuf_tailroom(buf) < 2 + flen) {
+		p2p_dbg(p2p, "wpabuf tail room too small");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+	p2p_dbg(p2p, "RSNXE capabilities: %04x", capab);
+	wpabuf_put_u8(buf, WLAN_EID_RSNX);
+	wpabuf_put_u8(buf, flen);
+	while (flen--) {
+		wpabuf_put_u8(buf, (capab & 0xff));
+		capab = capab >> 8;
+	}
+	return buf;
+}
+
+
+/* SSID used for deriving SAE pt for pairing */
+#define P2P_PAIRING_SSID "516F9A020000"
+
+static void p2p_pairing_set_password(struct pasn_data *pasn, u8 pasn_type,
+				     const char *passphrase)
+{
+	int pasn_groups[4] = { 0 };
+	size_t len;
+
+	if (!passphrase)
+		return;
+
+	len = os_strlen(passphrase);
+
+	if (pasn_type & 0xc && pasn_type & 0x3) {
+		pasn_groups[0] = 20;
+		pasn_groups[1] = 19;
+	} else if (pasn_type & 0xc) {
+		pasn_groups[0] = 20;
+	} else {
+		pasn_groups[0] = 19;
+	}
+	pasn->pt = sae_derive_pt(pasn_groups, (const u8 *) P2P_PAIRING_SSID,
+				 os_strlen(P2P_PAIRING_SSID),
+				 (const u8 *) passphrase, len, NULL);
+	/* Set passphrase for pairing responder to validate PASN auth 1 frame */
+	pasn->password = passphrase;
+}
+
+
+void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev,
+			 const u8 *addr, int freq, bool verify, bool derive_kek)
+{
+	struct pasn_data *pasn;
+	struct wpabuf *rsnxe;
+
+	if (!p2p || !dev)
+		return;
+
+	if (dev->pasn) {
+		wpa_pasn_reset(dev->pasn);
+	} else {
+		dev->pasn = pasn_data_init();
+		if (!dev->pasn)
+			return;
+	}
+
+	pasn = dev->pasn;
+
+	os_memcpy(pasn->own_addr, p2p->cfg->dev_addr, ETH_ALEN);
+	os_memcpy(pasn->peer_addr, addr, ETH_ALEN);
+
+	os_memcpy(pasn->bssid, dev->role == P2P_ROLE_PAIRING_INITIATOR ?
+		  pasn->peer_addr : pasn->own_addr, ETH_ALEN);
+
+	pasn->noauth = 1;
+
+	if ((p2p->cfg->pairing_config.pasn_type & 0xc) &&
+	    (dev->info.pairing_config.pasn_type & 0xc)) {
+		pasn->group = 20;
+		pasn->cipher = WPA_CIPHER_GCMP_256;
+		pasn->kek_len = 32;
+		pasn->derive_kek = true;
+	} else {
+		pasn->group = 19;
+		pasn->cipher = WPA_CIPHER_CCMP;
+		pasn->kek_len = 16;
+		pasn->derive_kek = true;
+	}
+
+	if (!derive_kek) {
+		pasn->derive_kek = false;
+		pasn->kek_len = 0;
+	}
+
+	if (dev->password[0]) {
+		pasn->akmp = WPA_KEY_MGMT_SAE;
+		p2p_pairing_set_password(pasn,
+					 p2p->cfg->pairing_config.pasn_type,
+					 dev->password);
+	} else if (verify) {
+		pasn->akmp = WPA_KEY_MGMT_SAE;
+	} else {
+		pasn->akmp = WPA_KEY_MGMT_PASN;
+	}
+
+	pasn->rsn_pairwise = pasn->cipher;
+	pasn->wpa_key_mgmt = pasn->akmp;
+
+	rsnxe = p2p_pairing_generate_rsnxe(p2p, pasn->akmp);
+	if (rsnxe) {
+		os_free(pasn->rsnxe_ie);
+		pasn->rsnxe_ie = os_memdup(wpabuf_head_u8(rsnxe),
+					   wpabuf_len(rsnxe));
+		if (!pasn->rsnxe_ie) {
+			wpabuf_free(rsnxe);
+			return;
+		}
+		wpabuf_free(rsnxe);
+	}
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR)
+		pasn->pmksa = p2p->initiator_pmksa;
+	else
+		pasn->pmksa = p2p->responder_pmksa;
+
+	pasn->cb_ctx = p2p->cfg->cb_ctx;
+	pasn->send_mgmt = p2p->cfg->pasn_send_mgmt;
+	pasn->prepare_data_element = p2p->cfg->prepare_data_element;
+	pasn->parse_data_element = p2p->cfg->parse_data_element;
+	pasn->validate_custom_pmkid = p2p->cfg->pasn_validate_pmkid;
+
+	pasn->freq = freq;
+}
+
+
+int p2p_get_listen_freq(struct p2p_data *p2p, const u8 *peer_addr)
+{
+	int freq;
+	struct p2p_device *dev;
+
+	if (!peer_addr) {
+		p2p_dbg(p2p, "Peer address NULL");
+		return -1;
+	}
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Peer not known");
+		return -1;
+	}
+
+	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0)
+		freq = dev->oob_go_neg_freq;
+	if (freq <= 0) {
+		p2p_dbg(p2p, "No listen/operating frequency known for the peer "
+			MACSTR, MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+	return freq;
+}
+
+
+int p2p_initiate_pasn_verify(struct p2p_data *p2p, const u8 *peer_addr,
+			     int freq, enum p2p_invite_role role,
+			     const u8 *bssid, const u8 *ssid, size_t ssid_len,
+			     unsigned int force_freq, const u8 *go_dev_addr,
+			     unsigned int pref_freq)
+{
+	struct pasn_data *pasn;
+	struct p2p_device *dev;
+	struct wpabuf *extra_ies, *req;
+	int ret = 0;
+	u8 *pasn_extra_ies = NULL;
+	u8 pmkid[PMKID_LEN];
+
+	if (!peer_addr) {
+		p2p_dbg(p2p, "Peer address NULL");
+		return -1;
+	}
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Peer not known");
+		return -1;
+	}
+
+	if (p2p_invite(p2p, peer_addr, role, bssid, ssid, ssid_len, force_freq,
+		       go_dev_addr, 1, pref_freq, -1, 1)) {
+		p2p_dbg(p2p, "p2p_invite() failed");
+		return -1;
+	}
+
+	dev->role = P2P_ROLE_PAIRING_INITIATOR;
+	p2p_pasn_initialize(p2p, dev, peer_addr, freq, true, true);
+	pasn = dev->pasn;
+
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, -1);
+	if (!req)
+		return -1;
+
+	p2p_set_state(p2p, P2P_INVITE);
+	p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
+	p2p->invite_peer = dev;
+	dev->invitation_reqs++;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies) {
+		wpabuf_free(req);
+		p2p_dbg(p2p, "Memory allocation failed for extra_ies");
+		return -1;
+	}
+
+	if (os_get_random(pmkid, PMKID_LEN) < 0) {
+		wpabuf_free(req);
+		wpabuf_free(extra_ies);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG,
+		    "P2P2: Use new random PMKID for pairing verification",
+		    pmkid, PMKID_LEN);
+	pasn_set_custom_pmkid(pasn, pmkid);
+
+	if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req, true)) {
+		p2p_dbg(p2p, "Prepare PASN extra IEs failed");
+		ret = -1;
+		goto out;
+	}
+
+	extra_ies = p2p_pasn_service_hash(p2p, extra_ies);
+	if (!extra_ies)
+		goto out;
+
+	pasn_extra_ies = os_memdup(wpabuf_head_u8(extra_ies),
+				   wpabuf_len(extra_ies));
+	if (!pasn_extra_ies) {
+		p2p_dbg(p2p, "Memory allocation failed for PASN extra IEs");
+		ret = -1;
+		goto out;
+	}
+
+	pasn->extra_ies = pasn_extra_ies;
+	pasn->extra_ies_len = wpabuf_len(extra_ies);
+
+	/* Start PASN verify */
+	if (wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
+			    pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
+			    NULL, 0, NULL, 0, NULL)) {
+		p2p_dbg(p2p, "PASN verify failed");
+		ret = -1;
+	} else {
+		dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK;
+	}
+out:
+	pasn->extra_ies = NULL;
+	pasn->extra_ies_len = 0;
+	os_free(pasn_extra_ies);
+	wpabuf_free(req);
+	wpabuf_free(extra_ies);
+	return ret;
+}
+
+
+int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq)
+{
+	struct pasn_data *pasn;
+	struct p2p_device *dev;
+	struct wpabuf *extra_ies, *req;
+	u8 *ies = NULL;
+	int ret = 0;
+	size_t ies_len;
+
+	if (!addr) {
+		p2p_dbg(p2p, "Peer address NULL");
+		return -1;
+	}
+
+	dev = p2p_get_device(p2p, addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Peer not known");
+		return -1;
+	}
+
+	if (freq == 0)
+		freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+
+	dev->role = P2P_ROLE_PAIRING_INITIATOR;
+	p2p_pasn_initialize(p2p, dev, addr, freq, false, true);
+	pasn = dev->pasn;
+
+	pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr);
+
+	req = p2p_build_go_neg_req(p2p, dev);
+	if (!req)
+		return -1;
+
+	p2p->go_neg_peer = dev;
+	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies) {
+		wpabuf_free(req);
+		return -1;
+	}
+
+	if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req, false)) {
+		p2p_dbg(p2p, "Failed to prepare PASN extra elements");
+		ret = -1;
+		goto out;
+	}
+
+	extra_ies = p2p_pasn_service_hash(p2p, extra_ies);
+	if (!extra_ies)
+		goto out;
+
+	ies_len = wpabuf_len(extra_ies);
+	ies = os_memdup(wpabuf_head_u8(extra_ies), ies_len);
+	if (!ies) {
+		ret = -1;
+		goto out;
+	}
+
+	pasn->extra_ies = ies;
+	pasn->extra_ies_len = ies_len;
+
+	/* Start PASN authentication */
+	if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
+			    pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
+			    NULL, 0, NULL, 0, NULL)) {
+		p2p_dbg(p2p, "Failed to start PASN");
+		ret = -1;
+	}
+out:
+	os_free(ies);
+	pasn->extra_ies = NULL;
+	pasn->extra_ies_len = 0;
+	wpabuf_free(req);
+	wpabuf_free(extra_ies);
+	return ret;
+}
+
+
+static int p2p_pasn_handle_action_wrapper(struct p2p_data *p2p,
+					  struct p2p_device *dev,
+					  const struct ieee80211_mgmt *mgmt,
+					  size_t len, int freq, int trans_seq)
+{
+	const u8 *ies;
+	size_t ies_len;
+	size_t data_len = 0;
+	bool derive_kek;
+	const u8 *data = NULL;
+	struct p2p_message msg;
+	struct ieee802_11_elems elems;
+
+	ies = mgmt->u.auth.variable;
+	ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ies, ies_len, &msg)) {
+		p2p_dbg(p2p,
+			"Failed to parse P2P IE from PASN Authentication frame");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.action_frame_wrapper && msg.action_frame_wrapper_len) {
+		data = msg.action_frame_wrapper;
+		data_len = msg.action_frame_wrapper_len;
+		if (data_len >= 2 &&
+		    data[0] == WLAN_ACTION_PUBLIC &&
+		    data[1] == WLAN_PA_VENDOR_SPECIFIC) {
+			data += 2;
+			data_len -= 2;
+			if (data_len < 4 ||
+			    WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) {
+				p2p_parse_free(&msg);
+				return -1;
+			}
+			data += 4;
+			data_len -= 4;
+		} else {
+			p2p_dbg(p2p,
+				"Invalid category in Action frame wrapper in Authentication frame seq %d",
+				trans_seq);
+			p2p_parse_free(&msg);
+			return -1;
+		}
+	}
+
+	if (trans_seq == 1) {
+		if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+					   len - offsetof(struct ieee80211_mgmt,
+							  u.auth.variable),
+					   &elems, 0) == ParseFailed) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed parsing Authentication frame");
+			return -1;
+		}
+		derive_kek = ieee802_11_rsnx_capab_len(
+			elems.rsnxe, elems.rsnxe_len,
+			WLAN_RSNX_CAPAB_KEK_IN_PASN);
+		if (data && data_len >= 1 && data[0] == P2P_INVITATION_REQ) {
+			struct wpabuf *resp;
+
+			resp = p2p_process_invitation_req(p2p, mgmt->sa,
+							  data + 1,
+							  data_len - 1, freq,
+							  true);
+			if (!resp)
+				p2p_dbg(p2p, "No Invitation Response found");
+
+			dev->role = P2P_ROLE_PAIRING_RESPONDER;
+			p2p_pasn_initialize(p2p, dev, mgmt->sa, freq, true,
+					    derive_kek);
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = resp;
+			if (msg.dira && msg.dira_len &&
+			    p2p_validate_dira(p2p, dev, msg.dira,
+					      msg.dira_len)) {
+				struct wpa_ie_data rsn_data;
+
+				if (wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2,
+							 elems.rsn_ie_len + 2,
+							 &rsn_data) == 0 &&
+				    rsn_data.num_pmkid)
+					pasn_set_custom_pmkid(dev->pasn,
+							      rsn_data.pmkid);
+			}
+		} else if (data && data_len >= 1 && data[0] == P2P_GO_NEG_REQ) {
+			struct wpabuf *resp;
+
+			if (!derive_kek) {
+				p2p_dbg(p2p, "KEK-in-PASN not set in RSNXE");
+				return -1;
+			}
+			resp = p2p_process_go_neg_req(p2p, mgmt->sa, data + 1,
+						      data_len - 1, freq, true);
+			if (!resp)
+				p2p_dbg(p2p,
+					"No GO Negotiation Response found");
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = resp;
+		} else {
+			p2p_dbg(p2p, "Invalid action frame wrapper in Auth1");
+		}
+	} else if (trans_seq == 2) {
+		if (data && data_len >= 1 && data[0] == P2P_INVITATION_RESP) {
+			p2p_process_invitation_resp(p2p, mgmt->sa, data + 1,
+						    data_len - 1);
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = NULL;
+		} else if (data && data_len >= 1 &&
+			   data[0] == P2P_GO_NEG_RESP) {
+			struct wpabuf *conf;
+
+			conf = p2p_process_go_neg_resp(p2p, mgmt->sa, data + 1,
+						       data_len - 1, freq,
+						       true);
+			if (!conf)
+				p2p_dbg(p2p, "No GO Negotiation Confirm found");
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = conf;
+		} else {
+			p2p_dbg(p2p, "Invalid action frame wrapper in Auth2");
+		}
+	} else if (trans_seq == 3) {
+		if (data && data_len >= 1 && data[0] == P2P_GO_NEG_CONF)
+			p2p_handle_go_neg_conf(p2p, mgmt->sa, data + 1,
+					       data_len - 1, true);
+		else
+			p2p_invitation_resp_cb(p2p, mgmt->sa,
+					       P2P_SEND_ACTION_SUCCESS);
+	}
+	p2p_parse_free(&msg);
+	return 0;
+}
+
+
+static int p2p_pasn_add_encrypted_data(struct p2p_data *p2p,
+				       struct p2p_device *dev,
+				       struct wpabuf *buf)
+{
+	struct pasn_data *pasn;
+	struct wpabuf *p2p2_ie;
+	u8 *dika_len, *p2p2_ie_len;
+	int ret;
+
+	if (!p2p || !dev || !dev->pasn)
+		return 0;
+
+	pasn = dev->pasn;
+
+	if (dev->req_bootstrap_method != P2P_PBMA_OPPORTUNISTIC &&
+	    !p2p->pairing_info->enable_pairing_cache)
+		return 0;
+
+	p2p2_ie = wpabuf_alloc(100);
+	if (!p2p2_ie)
+		return -1;
+
+	p2p2_ie_len = p2p_buf_add_p2p2_ie_hdr(p2p2_ie);
+
+	if (p2p->pairing_info->enable_pairing_cache) {
+		wpabuf_put_u8(p2p2_ie, P2P_ATTR_DEVICE_IDENTITY_KEY);
+		dika_len = wpabuf_put(p2p2_ie, 2);
+
+		wpabuf_put_u8(p2p2_ie,
+			      p2p->pairing_info->dev_ik.cipher_version);
+		wpabuf_put_data(p2p2_ie, p2p->pairing_info->dev_ik.dik_data,
+				p2p->pairing_info->dev_ik.dik_len);
+		wpabuf_put_be32(p2p2_ie, p2p->pairing_info->dev_ik.expiration);
+
+		WPA_PUT_LE16(dika_len,
+			     (u8 *) wpabuf_put(p2p2_ie, 0) - dika_len - 2);
+	}
+
+	if (dev->req_bootstrap_method == P2P_PBMA_OPPORTUNISTIC) {
+		if (!p2p->dev_sae_password[0]) {
+			int password_len;
+
+			/* SAE password is not available as the request is not
+			 * for an existing GO. Pick a random SAE password of
+			 * length between 10 and 20. */
+			password_len = 10 + os_random() % 10;
+			if (p2p_random(p2p->dev_sae_password,
+				       password_len) < 0) {
+				wpabuf_free(p2p2_ie);
+				return -1;
+			}
+			p2p->dev_sae_password[password_len] = '\0';
+		}
+
+		wpabuf_put_u8(p2p2_ie, P2P_ATTR_PASSWORD);
+		wpabuf_put_le16(p2p2_ie, os_strlen(p2p->dev_sae_password));
+		wpabuf_put_str(p2p2_ie, p2p->dev_sae_password);
+	}
+
+	p2p_buf_update_ie_hdr(p2p2_ie, p2p2_ie_len);
+
+	ret = pasn_add_encrypted_data(pasn, buf, wpabuf_mhead_u8(p2p2_ie),
+				      wpabuf_len(p2p2_ie));
+	wpabuf_free(p2p2_ie);
+	return ret;
+}
+
+
+int p2p_prepare_data_element(struct p2p_data *p2p, const u8 *peer_addr)
+{
+	int ret = -1;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	struct wpabuf *extra_ies;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "PASN: Peer not found " MACSTR,
+			MAC2STR(peer_addr));
+		return -1;
+	}
+	pasn = dev->pasn;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies ||
+	    p2p_prepare_pasn_extra_ie(p2p, extra_ies,
+				      dev->action_frame_wrapper, false)) {
+		p2p_dbg(p2p, "Failed to prepare PASN extra elements");
+		goto out;
+	}
+
+	if (p2p_pasn_add_encrypted_data(p2p, dev, extra_ies) < 0)
+		p2p_dbg(p2p, "Failed to add PASN encrypted elements");
+
+	pasn->extra_ies = os_memdup(wpabuf_head_u8(extra_ies),
+				    wpabuf_len(extra_ies));
+	if (!pasn->extra_ies)
+		goto out;
+	pasn->extra_ies_len = wpabuf_len(extra_ies);
+	ret = 0;
+
+out:
+	wpabuf_free(extra_ies);
+	wpabuf_free(dev->action_frame_wrapper);
+	dev->action_frame_wrapper = NULL;
+
+	return ret;
+}
+
+
+int p2p_parse_data_element(struct p2p_data *p2p, const u8 *data, size_t len)
+{
+	u8 attr_id;
+	const u8 *pos, *next;
+	u16 rem_len, attr_len;
+
+	if (!p2p || !data || !len)
+		return -1;
+
+	pos = data;
+	rem_len = len;
+
+	if (rem_len < 6 ||
+	    pos[0] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[1] < 4 ||
+	    rem_len < 2 + pos[1] ||
+	    WPA_GET_BE32(&pos[2]) != P2P2_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p,
+			"P2P: P2P2 IE not present in PASN Encrypted Data element");
+		return -1;
+	}
+
+	pos += 6;
+	rem_len -= 6;
+
+	while (rem_len >= 3) {
+		attr_id = *pos++;
+		attr_len = WPA_GET_LE16(pos);
+		pos += 2;
+		rem_len -= 3;
+		if (rem_len < attr_len)
+			return -1;
+		next = pos + attr_len;
+		rem_len -= attr_len;
+
+		switch (attr_id) {
+		case P2P_ATTR_DEVICE_IDENTITY_KEY:
+			if (attr_len < 1) {
+				p2p_dbg(p2p,
+					"Too short Device Identity Key attribute");
+				return -1;
+			}
+			p2p->dik_cipher_version = *pos++;
+			attr_len--;
+			if (p2p->dik_cipher_version ==
+			    DIRA_CIPHER_VERSION_128) {
+				if (attr_len < DEVICE_IDENTITY_KEY_LEN) {
+					p2p_dbg(p2p, "Too short DevIK");
+					return -1;
+				}
+				os_memcpy(p2p->peer_dik_data, pos,
+					  DEVICE_IDENTITY_KEY_LEN);
+				p2p->peer_dik_len = DEVICE_IDENTITY_KEY_LEN;
+				pos += DEVICE_IDENTITY_KEY_LEN;
+				attr_len -= DEVICE_IDENTITY_KEY_LEN;
+			} else {
+				p2p_dbg(p2p,
+					"Unsupported cipher version %u in Device Identity Key attribute",
+					p2p->dik_cipher_version);
+				return -1;
+			}
+			if (attr_len < 4) {
+				p2p_dbg(p2p,
+					"Not enough room for DevIK lifetime");
+				return -1;
+			}
+			p2p->peer_dik_lifetime = WPA_GET_BE32(pos);
+			p2p_dbg(p2p,
+				"Received peer DevIK of length %zu octets and lifetime %u",
+				p2p->peer_dik_len, p2p->peer_dik_lifetime);
+			break;
+		case P2P_ATTR_PASSWORD:
+			if (attr_len < 1 ||
+			    attr_len > sizeof(p2p->peer_sae_password) - 1) {
+				p2p_dbg(p2p,
+					"P2P: Invalid password length %d",
+					attr_len);
+				return -1;
+			}
+			os_memset(p2p->peer_sae_password, 0,
+				  sizeof(p2p->peer_sae_password));
+			os_memcpy(p2p->peer_sae_password, pos, attr_len);
+			break;
+		default:
+			p2p_dbg(p2p,
+				"Unsupported Attribute ID %u in P2P2 IE in PASN Encrypted Data element",
+				attr_id);
+			break;
+		}
+		pos = next;
+	}
+
+	return 0;
+}
+
+
+static int p2p_validate_custom_pmkid(struct p2p_data *p2p,
+				     struct p2p_device *dev, const u8 *pmkid)
+{
+	if (dev->pasn->custom_pmkid_valid &&
+	    os_memcmp(dev->pasn->custom_pmkid, pmkid, PMKID_LEN) == 0) {
+		p2p_dbg(p2p, "Customized PMKID valid");
+		return 0;
+	}
+	return -1;
+}
+
+
+static int p2p_pasn_pmksa_get_pmk(struct p2p_data *p2p, const u8 *addr,
+				  u8 *pmkid, u8 *pmk, size_t *pmk_len)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, addr);
+	if (!dev) {
+		p2p_dbg(p2p, "PASN: Peer not found " MACSTR, MAC2STR(addr));
+		return -1;
+	}
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR)
+		return pasn_initiator_pmksa_cache_get(p2p->initiator_pmksa,
+						      addr, pmkid, pmk,
+						      pmk_len);
+	else
+		return pasn_responder_pmksa_cache_get(p2p->responder_pmksa,
+						      addr, pmkid, pmk,
+						      pmk_len);
+}
+
+
+int p2p_pasn_validate_and_update_pmkid(struct p2p_data *p2p, const u8 *addr,
+				       const u8 *rsn_pmkid)
+{
+	size_t pmk_len;
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	struct p2p_device *dev;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, addr);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "P2P PASN: Peer not found " MACSTR,
+			MAC2STR(addr));
+		return -1;
+	}
+
+	if (p2p_validate_custom_pmkid(p2p, dev, rsn_pmkid))
+		return -1;
+
+	if (p2p_pasn_pmksa_get_pmk(p2p, addr, pmkid, pmk, &pmk_len)) {
+		p2p_dbg(p2p, "P2P PASN: Failed to get PMK from cache");
+		return -1;
+	}
+
+	p2p_pasn_pmksa_set_pmk(p2p, p2p->cfg->dev_addr, addr, pmk, pmk_len,
+			       rsn_pmkid);
+	return 0;
+}
+
+
+int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data,
+			    size_t data_len, bool acked, bool verify)
+{
+	int ret = 0;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, mgmt->da);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "P2P PASN: Peer not found " MACSTR,
+			MAC2STR(mgmt->da));
+		return -1;
+	}
+
+	pasn = dev->pasn;
+
+	ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
+	if (ret != 1 && !acked && pasn->frame)
+		return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame),
+				       wpabuf_len(pasn->frame), 0, pasn->freq,
+				       1000);
+
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
+	if (ret != 1)
+		return ret;
+
+	if (verify && dev == p2p->invite_peer)
+		p2p_start_invitation_connect(p2p, dev);
+	else if (dev == p2p->go_neg_peer)
+		p2p_go_complete(p2p, dev);
+
+	return 0;
+}
+
+
+static int p2p_handle_pasn_auth(struct p2p_data *p2p, struct p2p_device *dev,
+				const struct ieee80211_mgmt *mgmt, size_t len,
+				int freq)
+{
+	struct pasn_data *pasn;
+	u8 pasn_type;
+	int pasn_groups[4] = { 0 };
+	u16 auth_alg, auth_transaction, status_code;
+
+	if (!p2p || !dev || !dev->pasn)
+		return -1;
+
+	if (os_memcmp(mgmt->da, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "PASN Responder: Not our frame");
+		return -1;
+	}
+
+	if (len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+		return -1;
+
+	pasn = dev->pasn;
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (status_code != WLAN_STATUS_SUCCESS &&
+	    status_code != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		p2p_dbg(p2p, "PASN: Authentication rejected - status=%u",
+			status_code);
+		return -1;
+	}
+
+	if (auth_alg != WLAN_AUTH_PASN || auth_transaction == 2) {
+		p2p_dbg(p2p,
+			"PASN Responder: Not a PASN frame or unexpected Authentication frame, auth_alg=%d",
+			auth_alg);
+		return -1;
+	}
+	if (auth_transaction == 1) {
+		pasn_type = p2p->cfg->pairing_config.pasn_type;
+		if (pasn_type & 0xc && pasn_type & 0x3) {
+			pasn_groups[0] = 20;
+			pasn_groups[1] = 19;
+		} else if (pasn_type & 0xc) {
+			pasn_groups[0] = 20;
+		} else {
+			pasn_groups[0] = 19;
+		}
+		pasn->pasn_groups = pasn_groups;
+
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 1 action wrapper failed");
+			return -1;
+		}
+		if (handle_auth_pasn_1(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt,
+				       len, false) < 0) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 1 failed");
+			return -1;
+		}
+	} else if (auth_transaction == 3) {
+		if (handle_auth_pasn_3(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt,
+				       len) < 0) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 3 failed");
+			return -1;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		p2p_pasn_store_ptk(p2p, &pasn->ptk);
+#endif /* CONFIG_TESTING_OPTIONS */
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 3 action wrapper failed");
+			/* Drop keying material from a failed pairing attempt */
+			os_memset(p2p->peer_dik_data, 0,
+				  sizeof(p2p->peer_dik_data));
+			os_memset(p2p->peer_sae_password, 0,
+				  sizeof(p2p->peer_sae_password));
+			return -1;
+		}
+		forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
+	}
+	return 0;
+}
+
+
+int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt,
+		     size_t len, int freq)
+{
+	int ret = 0;
+	u8 auth_transaction;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	struct wpa_pasn_params_data pasn_data;
+
+	dev = p2p_get_device(p2p, mgmt->sa);
+	if (!dev) {
+		p2p_dbg(p2p, "PASN: Peer not found " MACSTR,
+			MAC2STR(mgmt->sa));
+		return -1;
+	}
+
+	if (!dev->pasn) {
+		p2p_dbg(p2p, "PASN: Uninitialized");
+		return -1;
+	}
+
+	pasn = dev->pasn;
+
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
+	pasn_register_callbacks(pasn, p2p->cfg->cb_ctx,
+				p2p->cfg->pasn_send_mgmt,
+				p2p->cfg->pasn_validate_pmkid);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR && auth_transaction == 2) {
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p,
+				"PASN Initiator: Handle Auth 2 action wrapper failed");
+			return -1;
+		}
+		ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len,
+				       &pasn_data);
+		if (ret < 0) {
+			p2p_dbg(p2p, "PASN: wpa_pasn_auth_rx() failed");
+			dev->role = P2P_ROLE_IDLE;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		p2p_pasn_store_ptk(p2p, &pasn->ptk);
+#endif /* CONFIG_TESTING_OPTIONS */
+		forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
+	} else {
+		ret = p2p_handle_pasn_auth(p2p, dev, mgmt, len, freq);
+	}
+	return ret;
+}
+
+
+void p2p_pasn_pmksa_set_pmk(struct p2p_data *p2p, const u8 *src, const u8 *dst,
+			    const u8 *pmk, size_t pmk_len, const u8 *pmkid)
+{
+	pasn_initiator_pmksa_cache_add(p2p->initiator_pmksa, src, dst, pmk,
+				       pmk_len, pmkid);
+	pasn_responder_pmksa_cache_add(p2p->responder_pmksa, src, dst, pmk,
+				       pmk_len, pmkid);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+void p2p_pasn_store_ptk(struct p2p_data *p2p, struct wpa_ptk *ptk)
+{
+	u8 *pos;
+
+	if (ptk->ptk_len > sizeof(p2p->pasn_ptk)) {
+		p2p_dbg(p2p, "P2P PASN PTK exceeds: (len=%ld)", ptk->ptk_len);
+		return;
+	}
+
+	pos = p2p->pasn_ptk;
+	p2p->pasn_ptk_len = ptk->ptk_len;
+	if (ptk->kck_len) {
+		os_memcpy(pos, ptk->kck, ptk->kck_len);
+		pos += ptk->kck_len;
+	}
+	if (ptk->kek_len) {
+		os_memcpy(pos, ptk->kek, ptk->kek_len);
+		pos += ptk->kek_len;
+	}
+	if (ptk->tk_len) {
+		os_memcpy(pos, ptk->tk, ptk->tk_len);
+		pos += ptk->tk_len;
+	}
+	if (ptk->kdk_len) {
+		os_memcpy(pos, ptk->kdk, ptk->kdk_len);
+		pos += ptk->kdk_len;
+	}
+}
+
+
+int p2p_pasn_get_ptk(struct p2p_data *p2p, const u8 **buf, size_t *buf_len)
+{
+	if (!p2p || !p2p->pasn_ptk_len)
+		return -1;
+
+	*buf_len = p2p->pasn_ptk_len;
+	*buf = p2p->pasn_ptk;
+	return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#endif /* CONFIG_PASN */
+
+
+int p2p_get_dira_info(struct p2p_data *p2p, char *buf, size_t buflen)
+{
+	int res;
+	char *pos, *end;
+	struct p2p_id_key *dev_ik;
+
+	if (!p2p->pairing_info ||
+	    !p2p->cfg->pairing_config.pairing_capable ||
+	    !p2p->cfg->pairing_config.enable_pairing_cache)
+		return 0;
+
+	if (p2p_derive_nonce_tag(p2p))
+		return 0;
+
+	pos = buf;
+	end = buf + buflen;
+	dev_ik = &p2p->pairing_info->dev_ik;
+
+	res = os_snprintf(pos, end - pos, MACSTR,
+			  MAC2STR(p2p->cfg->dev_addr));
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	res = os_snprintf(pos, end - pos, " ");
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	pos += wpa_snprintf_hex(pos, end - pos, dev_ik->dira_nonce,
+				dev_ik->dira_nonce_len);
+
+	res = os_snprintf(pos, end - pos, " ");
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	pos += wpa_snprintf_hex(pos, end - pos, dev_ik->dira_tag,
+				dev_ik->dira_tag_len);
+
+	res = os_snprintf(pos, end - pos, "\n");
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	return pos - buf;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 5b5c7dd..db70fd6 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -11,6 +11,7 @@
 
 #include "common/ieee802_11_defs.h"
 #include "wps/wps.h"
+#include "common/wpa_common.h"
 
 #define DEVICE_IDENTITY_KEY_MAX_LEN 64
 #define DEVICE_IDENTITY_KEY_LEN 16
@@ -181,6 +182,41 @@
 	 * peer_config_timeout - Peer configuration timeout (in 10 msec units)
 	 */
 	unsigned int peer_config_timeout;
+
+	/**
+	 * p2p2 - Whether this group uses P2P2
+	 */
+	bool p2p2;
+
+	/**
+	 * akmp - The negotiated PASN AKMP for P2P2
+	 */
+	int akmp;
+
+	/**
+	 * cipher - Pairwise cipher(s) for the group for P2P2
+	 */
+	int cipher;
+
+	/**
+	 * pmkid - PMKID for P2P2 when PMK is derived as part of pairing
+	 */
+	u8 pmkid[PMKID_LEN];
+
+	/**
+	 * pmk - PMK for P2P2 when PMK is derived as part of pairing
+	 */
+	u8 pmk[PMK_LEN_MAX];
+
+	/**
+	 * pmk_len - Length of @pmk in octets
+	 */
+	size_t pmk_len;
+
+	/**
+	 * sae_password - SAE password for the group (P2P2)
+	 */
+	char sae_password[100];
 };
 
 struct p2ps_provision {
@@ -350,11 +386,6 @@
 	bool enable_pairing_cache;
 
 	/**
-	 * Enable P2P pairing verification with cached NIK/NPK
-	 */
-	bool enable_pairing_verification;
-
-	/**
 	 * P2P bootstrapping methods supported
 	 */
 	u16 bootstrap_methods;
@@ -480,6 +511,26 @@
 	 * p2p_pairing_config - P2P pairing configuration
 	 */
 	struct p2p_pairing_config pairing_config;
+
+	/**
+	 * dik_id - For paired peers Identity block ID with PMK
+	 */
+	int dik_id;
+
+	/**
+	 * nonce_tag_valid - Whether nonce and tag are valid
+	 */
+	bool nonce_tag_valid;
+
+	/**
+	 * nonce - Valid nonce received in a recent discovery frame
+	 */
+	u8 nonce[DEVICE_IDENTITY_NONCE_LEN];
+
+	/**
+	 * tag - Valid tag received in a recent discovery frame
+	 */
+	u8 tag[DEVICE_IDENTITY_TAG_LEN];
 };
 
 enum p2p_prov_disc_status {
@@ -691,6 +742,23 @@
 	u16 comeback_after;
 
 	/**
+	 * chan_switch_req_enable - Enable P2P client channel switch request
+	 */
+	bool chan_switch_req_enable;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/**
+	 * Operating class for own operational channel in Invitation Response
+	 */
+	u8 inv_op_class;
+
+	/**
+	 * inv_op_channel - Own operational channel in Invitation Response
+	 */
+	u8 inv_op_channel;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -918,6 +986,20 @@
 	void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
 
 	/**
+	 * set_go_security_config - Set security configuration for P2P GO
+	 * @ctx: Callback context from cb_ctx
+	 * @res: GO Negotiation results
+	 *
+	 * This callback is used to set PMK/passphrase derived during PASN
+	 * authentication with a P2P client. This will fetch an active P2P group
+	 * owner instance and configure PMKSA in case of password based PASN, or
+	 * configures the passphrase and derive PT in case of unauthenticated
+	 * PASN.
+	 */
+	void (*set_go_security_config)(void *ctx,
+				       struct p2p_go_neg_results *res);
+
+	/**
 	 * sd_request - Callback on Service Discovery Request
 	 * @ctx: Callback context from cb_ctx
 	 * @freq: Frequency (in MHz) of the channel
@@ -1027,6 +1109,10 @@
 	 * @channels: Available operating channels for the group
 	 * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
 	 *	used
+	 * @p2p2: Whether invitation request was wrapped in PASN authentication
+	 * received from a P2P2 device
+	 * @new_ssid: Pointer to hold new SSID
+	 * @new_ssid_len: Length of new SSID buffer in octets
 	 * Returns: Status code (P2P_SC_*)
 	 *
 	 * This optional callback can be used to implement persistent reconnect
@@ -1049,7 +1135,8 @@
 				 size_t ssid_len, int *go, u8 *group_bssid,
 				 int *force_freq, int persistent_group,
 				 const struct p2p_channels *channels,
-				 int dev_pw_id);
+				 int dev_pw_id, bool p2p2, const u8 **new_ssid,
+				 size_t *new_ssid_len);
 
 	/**
 	 * invitation_received - Callback on Invitation Request RX
@@ -1071,18 +1158,25 @@
 	void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
 				    const u8 *ssid, size_t ssid_len,
 				    const u8 *go_dev_addr, u8 status,
-				    int op_freq);
+				    int op_freq, const u8 *pmkid, const u8 *pmk,
+				    size_t pmk_len);
 
 	/**
 	 * invitation_result - Callback on Invitation result
 	 * @ctx: Callback context from cb_ctx
 	 * @status: Negotiation result (Status Code)
+	 * @new_ssid: New SSID received in invitation response
+	 * @new_ssid_len: Length of new SSID received
 	 * @bssid: P2P Group BSSID or %NULL if not received
 	 * @channels: Available operating channels for the group
 	 * @addr: Peer address
 	 * @freq: Frequency (in MHz) indicated during invitation or 0
 	 * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer
 	 * during invitation or 0
+	 * @pmkid: PMKID used during invitation handshake
+	 * @pmk: The derived PMK
+	 * @pmk_len: PMK length in octets
+	 * @go_dev_addr: The P2P Device Address of the GO
 	 *
 	 * This callback is used to indicate result of an Invitation procedure
 	 * started with a call to p2p_invite(). The indicated status code is
@@ -1090,9 +1184,12 @@
 	 * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
 	 * local failure in transmitting the Invitation Request.
 	 */
-	void (*invitation_result)(void *ctx, int status, const u8 *bssid,
+	void (*invitation_result)(void *ctx, int status, const u8 *new_ssid,
+				  size_t new_ssid_len, const u8 *bssid,
 				  const struct p2p_channels *channels,
-				  const u8 *addr, int freq, int peer_oper_freq);
+				  const u8 *addr, int freq, int peer_oper_freq,
+				  const u8 *pmkid, const u8 *pmk,
+				  size_t pmk_len, const u8 *go_dev_addr);
 
 	/**
 	 * go_connected - Check whether we are connected to a GO
@@ -1265,17 +1362,76 @@
 				 u16 bootstrap_method);
 
 	/**
-	 * bootstrap_completed - Indicate bootstrapping completed with P2P peer
+	 * bootstrap_rsp_rx - Indicate bootstrapping response from a P2P peer
 	 * @ctx: Callback context from cb_ctx
 	 * @addr: P2P device address with which bootstrapping is completed
 	 * @status: P2P Status Code of bootstrapping handshake
 	 * @freq: Frequency in which bootstrapping is done
+	 * @bootstrap_method: Bootstrapping method by the peer device
 	 *
 	 * This function can be used to notify the status of bootstrapping
 	 * handshake.
 	 */
-	void (*bootstrap_completed)(void *ctx, const u8 *addr,
-				    enum p2p_status_code status, int freq);
+	void (*bootstrap_rsp_rx)(void *ctx, const u8 *addr,
+				 enum p2p_status_code status, int freq,
+				 u16 bootstrap_method);
+
+	/**
+	 * validate_dira - Indicate reception of DIRA to be validated against a
+	 *	list of available device identity keys
+	 * @ctx: Callback context from cb_ctx
+	 * @peer_addr: P2P Device address of the peer
+	 * @dira_nonce: DIRA Nonce
+	 * @dira_tag: DIRA Tag
+	 * Returns: Identity block ID on success, 0 on failure
+	 *
+	 * This function can be used to validate DIRA and configure PMK of a
+	 * paired/persistent peer from configuration. The handler function is
+	 * expected to call p2p_pasn_pmksa_set_pmk() to set the PMK/PMKID in
+	 * case a matching entry is found.
+	 */
+	int (*validate_dira)(void *ctx, const u8 *peer_addr,
+			     const u8 *dira_nonce, const u8 *dira_tag);
+
+	/**
+	 * pasn_send_mgmt - Function handler to transmit a Management frame
+	 * @ctx: Callback context from cb_ctx
+	 * @data: Frame to transmit
+	 * @data_len: Length of frame to transmit
+	 * @noack: No ack flag
+	 * @freq: Frequency in MHz for the channel on which to transmit
+	 * @wait: How many milliseconds to wait for a response frame
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len,
+			      int noack, unsigned int freq, unsigned int wait);
+
+	/**
+	 * prepare_data_element - Function handler to update protocol specific
+	 *	elements in PASN authentication frames
+	 * @ctx: Callback context from cb_ctx
+	 * @peer_addr: Peer MAC address
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*prepare_data_element)(void *ctx, const u8 *peer_addr);
+
+	/**
+	 * parse_data_element - Function handler to parse P2P data element
+	 * @ctx: Callback context from cb_ctx
+	 * @data: Data to be parsed
+	 * @len: Length of data
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*parse_data_element)(void *ctx, const u8 *data, size_t len);
+
+	/**
+	 * pasn_validate_pmkid - Function handler to validate RSN PMKID
+	 * @ctx: Callback context from cb_ctx
+	 * @addr: Peer MAC address
+	 * @pmkid: PMKID in the PASN frame
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*pasn_validate_pmkid)(void *ctx, const u8 *addr, const u8 *pmkid);
 };
 
 
@@ -1514,6 +1670,17 @@
 int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
 
 /**
+ * p2p_set_req_bootstrap_method - Send Provision Discovery Request to initiate
+ *	 bootstrapping
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @boostrap: Bootstrapping method
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_req_bootstrap_method(struct p2p_data *p2p, const u8 *peer_addr,
+				 u16 bootstrap);
+
+/**
  * p2p_prov_disc_req - Send Provision Discovery Request
  * @p2p: P2P module context from p2p_init()
  * @peer_addr: MAC address of the peer P2P client
@@ -1609,12 +1776,14 @@
  *	force_freq == 0)
  * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
  *	case or -1 if not used
+ * @p2p2: Operating in P2P2 mode
  * Returns: 0 on success, -1 on failure
  */
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq, int dev_pw_id);
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id,
+	       bool p2p2);
 
 /**
  * p2p_presence_req - Request GO presence
@@ -1836,6 +2005,8 @@
  */
 int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
 
+void p2p_listen_failed(struct p2p_data *p2p, unsigned int freq);
+
 void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
 		      const u8 *ie, size_t ie_len);
 
@@ -1855,6 +2026,11 @@
  */
 struct p2p_group_config {
 	/**
+	 * p2p2 - Whether the group was formed using P2P2
+	 */
+	bool p2p2;
+
+	/**
 	 * persistent_group - Whether the group is persistent
 	 * 0 = not a persistent group
 	 * 1 = persistent group without persistent reconnect
@@ -2093,6 +2269,14 @@
 size_t p2p_scan_ie_buf_len(struct p2p_data *p2p);
 
 /**
+ * p2p_build_ssid - Generate a random P2P SSID
+ * @p2p: P2P module context from p2p_init()
+ * @ssid: Buffer for SSID
+ * @ssid_len: Pointer to hold SSID length
+ */
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
+
+/**
  * p2p_go_params - Generate random P2P group parameters
  * @p2p: P2P module context from p2p_init()
  * @params: Buffer for parameters
@@ -2101,6 +2285,18 @@
 int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
 
 /**
+ * p2p_set_go_role - Set the current role of P2P device
+ * @p2p: P2P module context from p2p_init()
+ * @val: 1 if P2P GO, 0 to reset the role variable
+ *
+ * This role is configured as P2P GO when authorizing a P2P Client to join the
+ * group. Once PASN authentication with GO negotiation with predefined GO intent
+ * values (15 for P2P GO) is completed, the role helps to configure PMK derived
+ * during the PASN authentication.
+ */
+void p2p_set_go_role(struct p2p_data *p2p, bool val);
+
+/**
  * p2p_get_group_capab - Get Group Capability from P2P IE data
  * @p2p_ie: P2P IE(s) contents
  * Returns: Group Capability
@@ -2200,6 +2396,8 @@
 			   u8 *iface_addr);
 int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
 			   u8 *dev_addr);
+int p2p_get_dev_identity_key(struct p2p_data *p2p, const u8 *dev_addr,
+			     const u8 **dik_data, size_t *dik_len, u8 *cipher);
 
 void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
 
@@ -2582,5 +2780,41 @@
 struct wpabuf * p2p_usd_elems(struct p2p_data *p2p);
 void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len,
 			   const u8 *peer_addr, unsigned int freq);
+int p2p_get_dik_id(struct p2p_data *p2p, const u8 *peer);
+
+void p2p_set_pairing_setup(struct p2p_data *p2p, int pairing_setup);
+void p2p_set_pairing_cache(struct p2p_data *p2p, int pairing_cache);
+void p2p_set_bootstrapmethods(struct p2p_data *p2p, int bootstrap_methods);
+void p2p_set_pasn_type(struct p2p_data *p2p, u8 pasn_type);
+void p2p_set_comeback_after(struct p2p_data *p2p, int comeback_after);
+void p2p_set_reg_info(struct p2p_data *p2p, u8 val);
+void p2p_set_twt_power_mgmt(struct p2p_data *p2p, int val);
+void p2p_set_dev_addr(struct p2p_data *p2p, const u8 *addr);
+void p2p_set_chan_switch_req_enable(struct p2p_data *p2p, bool val);
+void p2p_set_invitation_op_freq(struct p2p_data *p2p, int freq);
+
+int p2p_get_listen_freq(struct p2p_data *p2p, const u8 *peer_addr);
+int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq);
+int p2p_initiate_pasn_verify(struct p2p_data *p2p, const u8 *peer_addr,
+			     int freq, enum p2p_invite_role role,
+			     const u8 *bssid, const u8 *ssid, size_t ssid_len,
+			     unsigned int force_freq, const u8 *go_dev_addr,
+			     unsigned int pref_freq);
+int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt,
+		     size_t len, int freq);
+int p2p_prepare_data_element(struct p2p_data *p2p, const u8 *peer_addr);
+int p2p_parse_data_element(struct p2p_data *p2p, const u8 *data, size_t len);
+int p2p_pasn_validate_and_update_pmkid(struct p2p_data *p2p, const u8 *addr,
+				       const u8 *pmkid);
+int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data,
+			    size_t data_len, bool acked, bool verify);
+int p2p_config_sae_password(struct p2p_data *p2p, const char *pw);
+void p2p_pasn_pmksa_set_pmk(struct p2p_data *p2p, const u8 *src, const u8 *dst,
+			    const u8 *pmk, size_t pmk_len, const u8 *pmkid);
+void p2p_set_store_pasn_ptk(struct p2p_data *p2p, u8 val);
+void p2p_pasn_store_ptk(struct p2p_data *p2p, struct wpa_ptk *ptk);
+int p2p_pasn_get_ptk(struct p2p_data *p2p, const u8 **buf, size_t *buf_len);
+void p2p_usd_service_hash(struct p2p_data *p2p, const char *service_name);
+int p2p_get_dira_info(struct p2p_data *p2p, char *buf, size_t buflen);
 
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index ddadd34..bc67ec2 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -418,6 +418,20 @@
 }
 
 
+void p2p_buf_add_usd_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
+{
+	if (!p2p)
+		return;
+
+	/* USD Service Hash */
+	wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
+	wpabuf_put_le16(buf, P2PS_HASH_LEN);
+	wpabuf_put_data(buf, p2p->p2p_service_hash, P2PS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
+		    p2p->p2p_service_hash, P2PS_HASH_LEN);
+}
+
+
 void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
 {
 	size_t info_len = 0;
@@ -741,6 +755,9 @@
 	if (p2p->cfg->dfs_owner)
 		capability_info |= P2P_PCEA_DFS_OWNER;
 
+	if (p2p->cfg->chan_switch_req_enable)
+		capability_info |= P2P_PCEA_CLI_REQ_CS;
+
 	if (p2p->cfg->pairing_config.pairing_capable)
 		capability_info |= P2P_PCEA_PAIRING_CAPABLE;
 
@@ -806,8 +823,7 @@
 	struct p2p_id_key *dev_ik;
 
 	if (!p2p->cfg->pairing_config.pairing_capable ||
-	    !p2p->cfg->pairing_config.enable_pairing_cache ||
-	    !p2p->cfg->pairing_config.enable_pairing_verification)
+	    !p2p->cfg->pairing_config.enable_pairing_cache)
 		return;
 
 	dev_ik = &p2p->pairing_info->dev_ik;
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index ac6bbf7..4b787a5 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -260,6 +260,8 @@
 			return -1;
 		return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
 					 NULL, config_method, 0, 0, 1);
+	} else if (dev->p2p2) {
+		return 0;
 	}
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index c036f92..2659787 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -205,11 +205,28 @@
 }
 
 
+struct wpabuf * p2p_group_build_p2p2_ie(struct p2p_data *p2p,
+					struct wpabuf *p2p2_ie, int freq)
+{
+	u8 *len;
+
+	wpabuf_put_u8(p2p2_ie, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(p2p2_ie, 1);
+	wpabuf_put_be32(p2p2_ie, P2P2_IE_VENDOR_TYPE);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P2 IE header");
+	p2p_buf_add_pcea(p2p2_ie, p2p);
+	*len = (u8 *) wpabuf_put(p2p2_ie, 0) - len - 1;
+
+	return p2p2_ie;
+}
+
+
 static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 {
 	struct wpabuf *ie;
 	u8 *len;
 	size_t extra = 0;
+	struct wpabuf *p2p2_ie;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (group->p2p->wfd_ie_beacon)
@@ -220,7 +237,7 @@
 	    group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
 		extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
 
-	ie = wpabuf_alloc(257 + extra);
+	ie = wpabuf_alloc(500 + extra);
 	if (ie == NULL)
 		return NULL;
 
@@ -240,6 +257,17 @@
 	p2p_group_add_noa(ie, group->noa);
 	p2p_buf_update_ie_hdr(ie, len);
 
+	if (group->cfg->p2p2) {
+		p2p2_ie = wpabuf_alloc(255);
+		if (!p2p2_ie) {
+			wpabuf_free(ie);
+			return NULL;
+		}
+
+		p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq);
+		ie = wpabuf_concat(p2p2_ie, ie);
+	}
+
 	return ie;
 }
 
@@ -443,6 +471,7 @@
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	struct wpabuf *p2p_subelems, *ie;
+	struct wpabuf *p2p2_ie;
 
 	p2p_subelems = wpabuf_alloc(500);
 	if (p2p_subelems == NULL)
@@ -474,7 +503,16 @@
 		ie = wpabuf_concat(wfd, ie);
 	}
 #endif /* CONFIG_WIFI_DISPLAY */
+	if (group->cfg->p2p2) {
+		p2p2_ie = wpabuf_alloc(255);
+		if (!p2p2_ie) {
+			wpabuf_free(ie);
+			return NULL;
+		}
 
+		p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq);
+		ie = wpabuf_concat(p2p2_ie, ie);
+	}
 	return ie;
 }
 
@@ -648,6 +686,7 @@
 	struct wpabuf *resp;
 	u8 *rlen;
 	size_t extra = 0;
+	struct wpabuf *p2p2_ie;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (group->wfd_ie)
@@ -683,6 +722,17 @@
 		p2p_buf_add_status(resp, status);
 	p2p_buf_update_ie_hdr(resp, rlen);
 
+	if (group->cfg->p2p2) {
+		p2p2_ie = wpabuf_alloc(255);
+		if (!p2p2_ie) {
+			wpabuf_free(resp);
+			return NULL;
+		}
+
+		p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq);
+		resp = wpabuf_concat(p2p2_ie, resp);
+	}
+
 	return resp;
 }
 
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 808bb96..1353652 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -37,6 +37,13 @@
 	REMOTE_GO
 };
 
+/* Enumeration for P2P device current role */
+enum p2p_role {
+	P2P_ROLE_IDLE = 0,
+	P2P_ROLE_PAIRING_INITIATOR,
+	P2P_ROLE_PAIRING_RESPONDER,
+};
+
 /**
  * struct bootstrap_params - P2P Device bootstrap request parameters
  */
@@ -183,6 +190,23 @@
 
 	/* Password for P2P2 GO negotiation */
 	char password[100];
+
+	/* PASN data structure */
+	struct pasn_data *pasn;
+	struct wpabuf *action_frame_wrapper;
+
+	/* Device role */
+	enum p2p_role role;
+
+	/* Invitation parameters for P2P2 */
+	bool inv_reject;
+	u8 inv_status;
+	int inv_freq;
+	int inv_peer_oper_freq;
+	u8 inv_bssid[ETH_ALEN];
+	u8 inv_ssid[SSID_MAX_LEN];
+	size_t inv_ssid_len;
+	bool inv_all_channels;
 };
 
 struct p2p_sd_query {
@@ -199,6 +223,8 @@
 	int akmp;
 	/* Cipher version type */
 	int cipher_version;
+	/* DevIK expiration time in hours */
+	u32 expiration;
 	/* Buffer to hold the DevIK */
 	u8 dik_data[DEVICE_IDENTITY_KEY_MAX_LEN];
 	/* Length of DevIK */
@@ -637,6 +663,49 @@
 	struct rsn_pmksa_cache *initiator_pmksa;
 	/* Pairing responder PMKSA cache */
 	struct rsn_pmksa_cache *responder_pmksa;
+
+	/* DevIK variables: Cipher version, DevIK, and its lifetime
+	 * These are fetched from the P2P2 included in the PASN Encrypted Data
+	 * element during P2P2 group negotiation with PASN Authentication
+	 * frames. These values are stored in struct p2p_data for an ongoing GO
+	 * negotiation or join-a-group operation with the assumption that these
+	 * operations cannot happen in parallel with multiple peers. After
+	 * successful group formation and connection, these are moved to
+	 * wpa_supplicant configuration if the connection is persistent. */
+	u8 dik_cipher_version;
+	u8 peer_dik_data[DEVICE_IDENTITY_KEY_MAX_LEN];
+	size_t peer_dik_len;
+	unsigned int peer_dik_lifetime;
+
+	/* Password used during an ongoing group formation after opportunistic
+	 * PASN authentication or while joining an existing group. This will be
+	 * moved to a more permanent location from struct p2p_data at the
+	 * conclusion of a successful pairing. */
+	char dev_sae_password[100];
+	char peer_sae_password[100];
+
+	/* Variable used to know the role of the device in a given instance.
+	 * go_role variable is set while authorizing a P2P Client for PASN
+	 * authentication with predefined GO intent value for GO (15 for
+	 * P2P-GO). Once the authentication is completed and security
+	 * configuration is done, this variable is reset to false.
+	 */
+	bool go_role;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/**
+	 * PASN PTK of recent auth
+	 */
+	u8 pasn_ptk[128];
+
+	/**
+	 * PASN PTK length
+	 */
+	size_t pasn_ptk_len;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	bool usd_service;
+	u8 p2p_service_hash[P2PS_HASH_LEN];
 };
 
 /**
@@ -898,6 +967,8 @@
 				   const struct weighted_pcl *pref_freq_list,
 				   unsigned int size);
 struct wpabuf * p2p_encaps_ie(const struct wpabuf *subelems, u32 ie_type);
+struct wpabuf * p2p_group_build_p2p2_ie(struct p2p_data *p2p,
+					struct wpabuf *p2p2_ie, int freq);
 
 /* p2p_sd.c */
 struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -951,19 +1022,23 @@
 		      struct p2p_device *dev);
 
 /* p2p_invitation.c */
+struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+					 struct p2p_device *peer,
+					 const u8 *go_dev_addr, int dev_pw_id);
 void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa,
 			       const u8 *data, size_t len, int rx_freq);
 void p2p_handle_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 				const u8 *data, size_t len);
 struct wpabuf * p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 					   const u8 *data, size_t len,
-					   int rx_freq);
+					   int rx_freq, bool p2p2);
 void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 				 const u8 *data, size_t len);
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
 		    const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
-void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
+void p2p_invitation_resp_cb(struct p2p_data *p2p, const u8 *dst, int success);
+void p2p_start_invitation_connect(struct p2p_data *p2p, struct p2p_device *dev);
 
 /* p2p_dev_disc.c */
 void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
@@ -1004,7 +1079,6 @@
 struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
 					 const u8 *query_hash,
 					 u8 query_count);
-void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
 int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 		    const u8 *src, const u8 *bssid, const u8 *buf,
 		    size_t len, unsigned int wait_time);
@@ -1021,6 +1095,10 @@
 			     struct p2p_channels *res, bool go);
 
 void p2p_sd_query_cb(struct p2p_data *p2p, int success);
+void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev,
+			 const u8 *addr, int freq, bool verify,
+			 bool derive_kek);
+void p2p_buf_add_usd_service_hash(struct wpabuf *buf, struct p2p_data *p2p);
 
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 3fd66c2..6d112ee 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -11,14 +11,17 @@
 #include "common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "crypto/sha384.h"
+#include "common/wpa_common.h"
+#include "pasn/pasn_common.h"
 #include "p2p_i.h"
 #include "p2p.h"
 
 
-static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
-						struct p2p_device *peer,
-						const u8 *go_dev_addr,
-						int dev_pw_id)
+struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+					 struct p2p_device *peer,
+					 const u8 *go_dev_addr, int dev_pw_id)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -100,7 +103,7 @@
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
 		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
 
-	if (dev_pw_id >= 0) {
+	if (dev_pw_id >= 0 && !peer->p2p2) {
 		/* WSC IE in Invitation Request for NFC static handover */
 		p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
 	}
@@ -114,7 +117,9 @@
 						 u8 dialog_token, u8 status,
 						 const u8 *group_bssid,
 						 u8 reg_class, u8 channel,
-						 struct p2p_channels *channels)
+						 struct p2p_channels *channels,
+						 const u8 *ssid,
+						 size_t ssid_len)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -159,6 +164,18 @@
 					      reg_class, channel);
 	if (group_bssid)
 		p2p_buf_add_group_bssid(buf, group_bssid);
+
+	if (ssid_len && ssid) {
+		const u8 *dev_addr;
+
+		if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
+			dev_addr = peer->info.p2p_device_addr;
+		else
+			dev_addr = p2p->cfg->dev_addr;
+
+		p2p_buf_add_group_id(buf, dev_addr, ssid, ssid_len);
+	}
+
 	if (channels) {
 		bool is_6ghz_capab;
 
@@ -183,7 +200,7 @@
 
 struct wpabuf * p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 					   const u8 *data, size_t len,
-					   int rx_freq)
+					   int rx_freq, bool p2p2)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
@@ -193,6 +210,8 @@
 	u8 group_bssid[ETH_ALEN], *bssid;
 	int op_freq = 0;
 	u8 reg_class = 0, channel = 0;
+	const u8 *new_ssid = NULL;
+	size_t new_ssid_len = 0;
 	struct p2p_channels all_channels, intersection, *channels = NULL;
 	int persistent;
 
@@ -268,7 +287,8 @@
 			p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
 			msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
 			&go, group_bssid, &op_freq, persistent, &intersection,
-			msg.dev_password_id_present ? msg.dev_password_id : -1);
+			msg.dev_password_id_present ? msg.dev_password_id : -1,
+			p2p2, &new_ssid, &new_ssid_len);
 	}
 
 	if (go) {
@@ -311,6 +331,17 @@
 		p2p_dbg(p2p, "Own default op_class %d channel %d",
 			p2p->op_reg_class, p2p->op_channel);
 
+#ifdef CONFIG_TESTING_OPTIONS
+		if (p2p->cfg->inv_op_class) {
+			/* Override configuration as a starting point */
+			p2p->op_reg_class = p2p->cfg->inv_op_class;
+			p2p->op_channel = p2p->cfg->inv_op_channel;
+			p2p_dbg(p2p,
+				"Override Invitation op_class %d channel %d",
+				p2p->op_reg_class, p2p->op_channel);
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 		/* Use peer preference if specified and compatible */
 		if (msg.operating_channel) {
 			int req_freq;
@@ -386,7 +417,8 @@
 	else
 		bssid = NULL;
 	resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
-					 bssid, reg_class, channel, channels);
+					 bssid, reg_class, channel, channels,
+					 new_ssid, new_ssid_len);
 
 	/*
 	 * Store copy of invitation data to be used when processing TX status
@@ -398,7 +430,12 @@
 		p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
 	} else
 		p2p->inv_group_bssid_ptr = NULL;
-	if (msg.group_id) {
+
+	if (p2p2 && new_ssid_len) {
+		os_memcpy(p2p->inv_ssid, new_ssid, new_ssid_len);
+		p2p->inv_ssid_len = new_ssid_len;
+		os_memcpy(p2p->inv_go_dev_addr, p2p->cfg->dev_addr, ETH_ALEN);
+	} else if (msg.group_id) {
 		if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) {
 			os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
 				  msg.group_id_len - ETH_ALEN);
@@ -422,7 +459,7 @@
 	int freq;
 	struct wpabuf *resp;
 
-	resp = p2p_process_invitation_req(p2p, sa, data, len, rx_freq);
+	resp = p2p_process_invitation_req(p2p, sa, data, len, rx_freq, false);
 	if (!resp)
 		return;
 
@@ -451,6 +488,8 @@
 	struct p2p_device *dev;
 	struct p2p_message msg;
 	struct p2p_channels intersection, *channels = NULL;
+	bool all_channels = false;
+	const u8 *go_dev_addr = NULL;
 
 	p2p_dbg(p2p, "Received Invitation Response from " MACSTR,
 		MAC2STR(sa));
@@ -530,14 +569,17 @@
 #endif /* CONFIG_P2P_STRICT */
 		/* Try to survive without peer channel list */
 		channels = &p2p->channels;
+		all_channels = true;
 	} else if (!msg.channel_list) {
 		/* Non-success cases are not required to include Channel List */
 		channels = &p2p->channels;
+		all_channels = true;
 	} else if (p2p_peer_channels_check(p2p, &p2p->channels, dev,
 					   msg.channel_list,
 					   msg.channel_list_len) < 0) {
 		p2p_dbg(p2p, "No common channels found");
 		p2p_parse_free(&msg);
+		dev->inv_reject = true;
 		return;
 	} else {
 		p2p_channels_intersect(&p2p->channels, &dev->channels,
@@ -566,19 +608,93 @@
 		 */
 		p2p_check_pref_chan(p2p, 0, dev, &msg);
 
-		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
-					    msg.group_bssid, channels, sa,
-					    freq, peer_oper_freq);
-	}
+		if (dev->p2p2) {
+			dev->inv_freq = freq;
+			dev->inv_status = *msg.status;
+			dev->inv_all_channels = all_channels;
+			dev->inv_peer_oper_freq = peer_oper_freq;
+			if (msg.group_bssid)
+				os_memcpy(dev->inv_bssid, msg.group_bssid,
+					  ETH_ALEN);
+			if (msg.group_id) {
+				dev->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+				os_memcpy(dev->inv_ssid,
+					  msg.group_id + ETH_ALEN,
+					  dev->inv_ssid_len);
 
-	p2p_parse_free(&msg);
+				os_memcpy(p2p->invite_go_dev_addr_buf,
+					  msg.group_id, ETH_ALEN);
+				p2p->invite_go_dev_addr =
+					p2p->invite_go_dev_addr_buf;
+				go_dev_addr = p2p->invite_go_dev_addr;
+			}
+			goto out;
+		}
+
+		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+					    NULL, 0,
+					    msg.group_bssid, channels, sa,
+					    freq, peer_oper_freq, NULL, NULL,
+					    0, go_dev_addr);
+	}
 
 	p2p_clear_timeout(p2p);
 	p2p_set_state(p2p, P2P_IDLE);
 	p2p->invite_peer = NULL;
+
+out:
+	p2p_parse_free(&msg);
 }
 
 
+#ifdef CONFIG_PASN
+void p2p_start_invitation_connect(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	size_t pmk_len = 0;
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	struct p2p_channels intersection;
+	const struct p2p_channels *inv_channels;
+
+	if (!p2p || !dev || dev->inv_reject || !dev->pasn)
+		return;
+
+	if (dev->inv_all_channels) {
+		inv_channels = &p2p->channels;
+	} else {
+		p2p_channels_intersect(&p2p->channels, &dev->channels,
+				       &intersection);
+		inv_channels = &intersection;
+	}
+
+	pasn_initiator_pmksa_cache_get(dev->pasn->pmksa, dev->pasn->peer_addr,
+				       pmkid, pmk, &pmk_len);
+
+	wpa_pasn_reset(dev->pasn);
+	p2p_dbg(p2p, "Invitation connect: msg status %d", dev->inv_status);
+	if (p2p->cfg->invitation_result)
+		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, dev->inv_status,
+					    dev->inv_ssid, dev->inv_ssid_len,
+					    dev->inv_bssid, inv_channels,
+					    dev->info.p2p_device_addr,
+					    dev->inv_freq,
+					    dev->inv_peer_oper_freq, pmkid,
+					    pmk, pmk_len,
+					    p2p->invite_go_dev_addr);
+
+	/* Reset PMK and PMKID from stack */
+	forced_memzero(pmkid, sizeof(pmkid));
+	forced_memzero(pmk, sizeof(pmk));
+
+	p2p_clear_timeout(p2p);
+	p2p_set_state(p2p, P2P_IDLE);
+	os_memset(dev->inv_ssid, 0, sizeof(dev->inv_ssid));
+	dev->inv_ssid_len = 0;
+	p2p->invite_peer = NULL;
+}
+#endif /* CONFIG_PASN */
+
+
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
 		    const u8 *go_dev_addr, int dev_pw_id)
 {
@@ -647,8 +763,26 @@
 }
 
 
-void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+void p2p_invitation_resp_cb(struct p2p_data *p2p, const u8 *peer, int success)
 {
+	size_t pmk_len = 0;
+	const u8 *pmkid = NULL, *pmk = NULL;
+
+#ifdef CONFIG_PASN
+	u8 _pmkid[PMKID_LEN];
+	u8 _pmk[PMK_LEN_MAX];
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer);
+	if (dev && dev->pasn) {
+		pasn_responder_pmksa_cache_get(dev->pasn->pmksa,
+					       dev->pasn->peer_addr, _pmkid,
+					       _pmk, &pmk_len);
+		pmkid = _pmkid;
+		pmk = _pmk;
+	}
+#endif /* CONFIG_PASN */
+
 	p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success);
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 
@@ -662,15 +796,23 @@
 					      p2p->inv_ssid, p2p->inv_ssid_len,
 					      p2p->inv_go_dev_addr,
 					      p2p->inv_status,
-					      p2p->inv_op_freq);
+					      p2p->inv_op_freq, pmkid, pmk,
+					      pmk_len);
 	}
+
+#ifdef CONFIG_PASN
+	/* Reset PMK and PMKID from stack */
+	forced_memzero(_pmkid, sizeof(_pmkid));
+	forced_memzero(_pmk, sizeof(_pmk));
+#endif /* CONFIG_PASN */
 }
 
 
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq, int dev_pw_id)
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id,
+	       bool p2p2)
 {
 	struct p2p_device *dev;
 
@@ -738,5 +880,8 @@
 	os_memcpy(p2p->inv_ssid, ssid, ssid_len);
 	p2p->inv_ssid_len = ssid_len;
 	p2p->inv_persistent = persistent_group;
+	if (p2p2)
+		return 0;
+
 	return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
 }
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index fb20313..f08fa0e 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -703,7 +703,6 @@
 
 	if (dev->info.pcea_cap_info & P2P_PCEA_PMK_CACHING) {
 		dev->info.pairing_config.enable_pairing_cache = true;
-		dev->info.pairing_config.enable_pairing_verification = true;
 	}
 }
 
@@ -781,9 +780,6 @@
 
 		if (!dev->req_bootstrap_method) {
 			status = P2P_SC_COMEBACK;
-			if (p2p->cfg->bootstrap_req_rx)
-				p2p->cfg->bootstrap_req_rx(p2p->cfg->cb_ctx,
-							   sa, bootstrap);
 			goto out;
 		}
 	} else {
@@ -849,6 +845,12 @@
 
 	wpa_printf(MSG_ERROR, "Bootstrap received %d", bootstrap);
 
+	if (status == P2P_SC_SUCCESS) {
+		dev->role = P2P_ROLE_PAIRING_RESPONDER;
+#ifdef CONFIG_PASN
+		p2p_pasn_initialize(p2p, dev, sa, rx_freq, false, true);
+#endif /* CONFIG_PASN */
+	}
 out:
 	/* Send PD Bootstrapping Response for the PD Request */
 	resp = p2p_build_prov_disc_bootstrap_resp(p2p, dev, msg->dialog_token,
@@ -1641,6 +1643,7 @@
 	size_t cookie_len = 0;
 	const u8 *pos, *cookie;
 	u16 comeback_after;
+	u16 bootstrap = 0;
 
 	/* Parse the P2P status present */
 	if (msg->status)
@@ -1707,16 +1710,24 @@
 		p2p->cfg->register_bootstrap_comeback(p2p->cfg->cb_ctx, sa,
 						      comeback_after);
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+		if (p2p->cfg->bootstrap_rsp_rx)
+			p2p->cfg->bootstrap_rsp_rx(p2p->cfg->cb_ctx, sa, status,
+						   rx_freq, bootstrap);
 		return;
 	}
 
+	/* PBMA response */
+	if (msg->pbma_info_len >= 2)
+		bootstrap = WPA_GET_LE16(msg->pbma_info);
+
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG)
 		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
 
-	if (p2p->cfg->bootstrap_completed)
-		p2p->cfg->bootstrap_completed(p2p->cfg->cb_ctx, sa, status,
-					      rx_freq);
+	if (p2p->cfg->bootstrap_rsp_rx)
+		p2p->cfg->bootstrap_rsp_rx(p2p->cfg->cb_ctx, sa, status,
+					   rx_freq, bootstrap);
 }
 
 
@@ -2112,6 +2123,24 @@
 }
 
 
+int p2p_set_req_bootstrap_method(struct p2p_data *p2p, const u8 *peer_addr,
+				 u16 bootstrap)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Bootstrap request for peer " MACSTR
+			" not yet known", MAC2STR(peer_addr));
+		return -1;
+	}
+
+	dev->p2p2 = 1;
+	dev->req_bootstrap_method = bootstrap;
+	return 0;
+}
+
+
 int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
 		      struct p2ps_provision *p2ps_prov,
 		      u16 config_methods, int join, int force_freq,
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index bc33a25..61a234b 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -1892,7 +1892,7 @@
 
 	/* Determine if we need space for the ICV Indicator */
 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
-	    DEFAULT_ICV_LEN)
+	    DEFAULT_ICV_LEN || participant->kay->include_icv_indicator)
 		length = sizeof(struct ieee802_1x_mka_icv_body);
 	else
 		length = 0;
@@ -1915,7 +1915,7 @@
 
 	length = ieee802_1x_mka_get_icv_length(participant);
 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
-	    DEFAULT_ICV_LEN)  {
+	    DEFAULT_ICV_LEN || participant->kay->include_icv_indicator)  {
 		wpa_printf(MSG_DEBUG, "KaY: ICV Indicator");
 		body = wpabuf_put(buf, MKA_HDR_LEN);
 		body->type = MKA_ICV_INDICATOR;
@@ -3161,9 +3161,9 @@
 		   be_to_host16(eth_hdr->ethertype));
 
 	/* the destination address shall not be an individual address */
-	if (!ether_addr_equal(eth_hdr->dest, pae_group_addr)) {
+	if (!is_multicast_ether_addr(eth_hdr->dest)) {
 		wpa_printf(MSG_DEBUG,
-			   "KaY: ethernet destination address is not PAE group address");
+			   "KaY: ethernet destination address is not a multicast adddress");
 		return -1;
 	}
 
@@ -3538,7 +3538,8 @@
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
 		    u8 macsec_offload, u16 port, u8 priority,
-		    u32 macsec_csindex, const char *ifname, const u8 *addr)
+		    u32 macsec_csindex, bool include_icv_indicator,
+		    const char *ifname, const u8 *addr)
 {
 	struct ieee802_1x_kay *kay;
 
@@ -3576,6 +3577,7 @@
 
 	kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
 	kay->macsec_csindex = macsec_csindex;
+	kay->include_icv_indicator = include_icv_indicator;
 	kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
 	kay->mka_version = MKA_VERSION_ID;
 
@@ -3929,33 +3931,28 @@
 	dl_list_del(&participant->list);
 
 	/* remove live peer */
-	while (!dl_list_empty(&participant->live_peers)) {
-		peer = dl_list_entry(participant->live_peers.next,
-				     struct ieee802_1x_kay_peer, list);
+	while ((peer = dl_list_first(&participant->live_peers,
+				     struct ieee802_1x_kay_peer, list))) {
 		dl_list_del(&peer->list);
 		os_free(peer);
 	}
 
 	/* remove potential peer */
-	while (!dl_list_empty(&participant->potential_peers)) {
-		peer = dl_list_entry(participant->potential_peers.next,
-				     struct ieee802_1x_kay_peer, list);
+	while ((peer = dl_list_first(&participant->potential_peers,
+				     struct ieee802_1x_kay_peer, list))) {
 		dl_list_del(&peer->list);
 		os_free(peer);
 	}
 
 	/* remove sak */
-	while (!dl_list_empty(&participant->sak_list)) {
-		sak = dl_list_entry(participant->sak_list.next,
-				    struct data_key, list);
+	while ((sak = dl_list_first(&participant->sak_list,
+				    struct data_key, list))) {
 		dl_list_del(&sak->list);
 		ieee802_1x_kay_deinit_data_key(sak);
 	}
-	while (!dl_list_empty(&participant->rxsc_list)) {
-		rxsc = dl_list_entry(participant->rxsc_list.next,
-				     struct receive_sc, list);
+	while ((rxsc = dl_list_first(&participant->rxsc_list,
+				     struct receive_sc, list)))
 		ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
-	}
 	ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
 
 	os_memset(&participant->cak, 0, sizeof(participant->cak));
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 545a99b..280f8d4 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -206,6 +206,7 @@
 	struct ieee802_1x_kay_ctx *ctx;
 	bool is_key_server;
 	bool is_obliged_key_server;
+	bool include_icv_indicator; /* Always include ICV Indicator */
 	char if_name[IFNAMSIZ];
 	u8 macsec_offload;
 
@@ -243,7 +244,8 @@
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
 		    u8 macsec_offload, u16 port, u8 priority,
-		    u32 macsec_csindex, const char *ifname, const u8 *addr);
+		    u32 macsec_csindex, bool include_icv_indicator,
+		    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/pasn/pasn_common.c b/src/pasn/pasn_common.c
index 25e44a1..654656e 100644
--- a/src/pasn/pasn_common.c
+++ b/src/pasn/pasn_common.c
@@ -15,6 +15,8 @@
 #include "crypto/sha384.h"
 #include "crypto/crypto.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
 #include "pasn_common.h"
 
 
@@ -31,6 +33,7 @@
 	if (!pasn)
 		return;
 	os_free(pasn->rsnxe_ie);
+	wpabuf_free(pasn->frame);
 	bin_clear_free(pasn, sizeof(struct pasn_data));
 }
 
@@ -241,3 +244,107 @@
 		return NULL;
 	return &pasn->ptk;
 }
+
+
+int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf,
+			    const u8 *data, size_t data_len)
+{
+	int ret;
+	u8 *encrypted_data, *padded_data = NULL;
+	u8 *len;
+	size_t pad_len = 0;
+
+	if (!pasn->ptk.kek_len) {
+		wpa_printf(MSG_DEBUG, "PASN: KEK not available");
+		return -2;
+	}
+
+	pad_len = data_len % 8;
+	if (pad_len) {
+		pad_len = 8 - pad_len;
+		padded_data = os_zalloc(data_len + pad_len);
+		if (!padded_data)
+			return -1;
+		os_memcpy(padded_data, data, data_len);
+		data = padded_data;
+		padded_data[data_len] = 0xdd;
+	}
+	data_len += pad_len + 8;
+
+	encrypted_data = os_malloc(data_len);
+	if (!encrypted_data) {
+		os_free(padded_data);
+		return -1;
+	}
+
+	ret = aes_wrap(pasn->ptk.kek, pasn->ptk.kek_len,
+		       (data_len - 8) / 8, data, encrypted_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: AES wrap failed, ret=%d", ret);
+		goto out;
+	}
+
+	if (wpabuf_tailroom(buf) < 1 + 1 + 1 + data_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Not enough room in the buffer for PASN Encrypred Data element");
+		ret = -1;
+		goto out;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	len = wpabuf_put(buf, 1);
+
+	wpabuf_put_u8(buf, WLAN_EID_EXT_PASN_ENCRYPTED_DATA);
+
+	wpabuf_put_data(buf, encrypted_data, data_len);
+	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+
+out:
+	os_free(padded_data);
+	os_free(encrypted_data);
+	return ret;
+}
+
+
+int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data,
+			      size_t len)
+{
+	int ret = -1;
+	u8 *buf;
+	u16 buf_len;
+	struct ieee802_11_elems elems;
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+
+	if (len < 24 + 6 ||
+	    ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return -1;
+	}
+
+	if (!elems.pasn_encrypted_data || elems.pasn_encrypted_data_len < 8 ||
+	    elems.pasn_encrypted_data_len % 8) {
+		wpa_printf(MSG_DEBUG, "PASN: No encrypted elements");
+		return 0;
+	}
+
+	buf_len = elems.pasn_encrypted_data_len - 8;
+
+	buf = os_malloc(buf_len);
+	if (!buf)
+		return -1;
+
+	ret = aes_unwrap(pasn->ptk.kek, pasn->ptk.kek_len, buf_len / 8,
+			 elems.pasn_encrypted_data, buf);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "PASN: AES unwrap failed, ret=%d", ret);
+	else if (pasn->parse_data_element && pasn->cb_ctx)
+		ret = pasn->parse_data_element(pasn->cb_ctx, buf, buf_len);
+
+	os_free(buf);
+	return ret;
+}
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
index 7b7c737..cc3abf6 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -66,6 +66,7 @@
 	size_t extra_ies_len;
 
 	/* External modules do not access below variables */
+	bool derive_kek;
 	size_t kek_len;
 	u16 group;
 	bool secure_ltf;
@@ -130,6 +131,7 @@
 	struct os_reltime last_comeback_key_update;
 	u16 comeback_idx;
 	u16 *comeback_pending_idx;
+	struct wpabuf *frame;
 
 	/**
 	 * send_mgmt - Function handler to transmit a Management frame
@@ -151,6 +153,10 @@
 	 */
 	int (*validate_custom_pmkid)(void *ctx, const u8 *addr,
 				     const u8 *pmkid);
+
+	int (*prepare_data_element)(void *ctx, const u8 *peer_addr);
+
+	int (*parse_data_element)(void *ctx, const u8 *data, size_t len);
 };
 
 /* Initiator */
@@ -210,8 +216,9 @@
 struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void);
 void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
 int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid);
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *pmkid);
 int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
 				   const u8 *bssid, u8 *pmkid, u8 *pmk,
 				   size_t *pmk_len);
@@ -232,8 +239,9 @@
 struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void);
 void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
 int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid);
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *pmkid);
 int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
 				   const u8 *bssid, u8 *pmkid, u8 *pmk,
 				   size_t *pmk_len);
@@ -246,6 +254,10 @@
 size_t pasn_get_pmk_len(struct pasn_data *pasn);
 u8 * pasn_get_pmk(struct pasn_data *pasn);
 struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn);
+int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf,
+			    const u8 *data, size_t data_len);
+int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data,
+			      size_t len);
 
 #ifdef __cplusplus
 }
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
index ce1055b..bee7e58 100644
--- a/src/pasn/pasn_initiator.c
+++ b/src/pasn/pasn_initiator.c
@@ -39,11 +39,12 @@
 
 
 int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid)
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk,
+				   size_t pmk_len, const u8 *pmkid)
 {
 	if (pmksa_cache_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid,
-			    own_addr, NULL, WPA_KEY_MGMT_SAE, 0))
+			    own_addr, NULL, WPA_KEY_MGMT_SAE, NULL))
 		return 0;
 	return -1;
 }
@@ -125,7 +126,7 @@
 	wpabuf_put_le16(buf, 1);
 	wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
 
-	sae_write_commit(&pasn->sae, buf, NULL, 0);
+	sae_write_commit(&pasn->sae, buf, NULL, NULL);
 	pasn->sae.state = SAE_COMMITTED;
 
 	return buf;
@@ -174,8 +175,8 @@
 		return -1;
 	}
 
-	res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
-			       1, NULL);
+	res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, NULL,
+			       groups, 1, NULL);
 	if (res != WLAN_STATUS_SUCCESS) {
 		wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
 		return -1;
@@ -498,7 +499,7 @@
 					    pasn->pmk_len, pasn->fils.erp_pmkid,
 					    NULL, 0, pasn->peer_addr,
 					    pasn->own_addr, NULL,
-					    pasn->akmp, 0);
+					    pasn->akmp, NULL);
 
 	pasn->fils.completed = true;
 	return 0;
@@ -682,7 +683,8 @@
 {
 	struct wpabuf *buf, *wrapped_data_buf = NULL;
 	u8 mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len, data_len;
+	u8 mic_len;
+	size_t data_len;
 	const u8 *data;
 	u8 *ptr;
 	u8 wrapped_data;
@@ -716,6 +718,11 @@
 	wpabuf_free(wrapped_data_buf);
 	wrapped_data_buf = NULL;
 
+	if (pasn->prepare_data_element && pasn->cb_ctx)
+		pasn->prepare_data_element(pasn->cb_ctx, pasn->peer_addr);
+
+	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
 	/* Add the MIC */
 	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
 	wpabuf_put_u8(buf, WLAN_EID_MIC);
@@ -817,6 +824,9 @@
 		os_free((u8 *) pasn->extra_ies);
 		pasn->extra_ies = NULL;
 	}
+
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
 }
 
 
@@ -905,7 +915,7 @@
 						    pasn->sae.pmkid,
 						    NULL, 0, pasn->peer_addr,
 						    pasn->own_addr, NULL,
-						    pasn->akmp, 0);
+						    pasn->akmp, NULL);
 		return 0;
 	}
 #endif /* CONFIG_SAE */
@@ -987,16 +997,20 @@
 		goto fail;
 	}
 
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
 	ret = pasn->send_mgmt(pasn->cb_ctx,
 			      wpabuf_head(frame), wpabuf_len(frame), 0,
 			      pasn->freq, 1000);
 
-	wpabuf_free(frame);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+		wpabuf_free(frame);
 		goto fail;
 	}
 
+	pasn->frame = frame;
 	return 0;
 
 fail:
@@ -1386,21 +1400,30 @@
 
 	wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
 
+	if (pasn_parse_encrypted_data(pasn, data, len) < 0) {
+		wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed");
+		goto fail;
+	}
+
 	frame = wpas_pasn_build_auth_3(pasn);
 	if (!frame) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
 		goto fail;
 	}
 
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
 	ret = pasn->send_mgmt(pasn->cb_ctx,
 			      wpabuf_head(frame), wpabuf_len(frame), 0,
 			      pasn->freq, 100);
-	wpabuf_free(frame);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+		wpabuf_free(frame);
 		goto fail;
 	}
 
+	pasn->frame = frame;
 	wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
 
 	pasn->status = WLAN_STATUS_SUCCESS;
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
index e344898..b4137b4 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -39,8 +39,9 @@
 
 
 int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid)
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *pmkid)
 {
 	if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, own_addr,
 				 bssid, 0, NULL, WPA_KEY_MGMT_SAE))
@@ -152,7 +153,7 @@
 		return -1;
 	}
 
-	res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
+	res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, NULL,
 			       groups, 0, NULL);
 	if (res != WLAN_STATUS_SUCCESS) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit");
@@ -251,7 +252,7 @@
 	wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
 
 	/* Write the actual commit and update the length accordingly */
-	sae_write_commit(&pasn->sae, buf, NULL, 0);
+	sae_write_commit(&pasn->sae, buf, NULL, NULL);
 	len = wpabuf_len(buf);
 	WPA_PUT_LE16(len_ptr, len - 2);
 
@@ -561,6 +562,9 @@
 	if (rsnxe_ie)
 		wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
 
+	if (pasn->prepare_data_element && pasn->cb_ctx)
+		pasn->prepare_data_element(pasn->cb_ctx, peer_addr);
+
 	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
 
 	/* Add the mic */
@@ -636,6 +640,8 @@
 	wpa_printf(MSG_DEBUG,
 		   "PASN: Building frame 2: success; resp STA=" MACSTR,
 		   MAC2STR(peer_addr));
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
 
 	ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
 			      wpabuf_len(buf), 0, pasn->freq, 0);
@@ -643,7 +649,7 @@
 		wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
 
 	wpabuf_free(rsn_buf);
-	wpabuf_free(buf);
+	pasn->frame = buf;
 	return ret;
 fail:
 	wpabuf_free(wrapped_data_buf);
@@ -735,6 +741,14 @@
 
 	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
 
+	if (!ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+				       WLAN_RSNX_CAPAB_KEK_IN_PASN)) {
+		pasn->kek_len = 0;
+		pasn->derive_kek = false;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: kek_len=%zu", pasn->kek_len);
+
 	if (!elems.pasn_params || !elems.pasn_params_len) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: No PASN Parameters element found");
@@ -1086,6 +1100,11 @@
 		wpabuf_free(wrapped_data);
 	}
 
+	if (pasn_parse_encrypted_data(pasn, (const u8 *) mgmt, len) < 0) {
+		wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed");
+		goto fail;
+	}
+
 	wpa_printf(MSG_INFO,
 		   "PASN: Success handling transaction == 3. Store PTK");
 	return 0;
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 37aa216..029e622 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -469,8 +469,10 @@
 			return -1;
 		msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 		if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
-			     wpabuf_len(msg->buf), pos) < 0)
+			     wpabuf_len(msg->buf), pos) < 0) {
+			wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
 			return -1;
+		}
 	} else
 		msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 
@@ -497,8 +499,10 @@
 	os_memcpy(msg->hdr->authenticator, req_authenticator,
 		  sizeof(msg->hdr->authenticator));
 	if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
-		     wpabuf_len(msg->buf), pos) < 0)
+		     wpabuf_len(msg->buf), pos) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
 		return -1;
+	}
 
 	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
 	addr[0] = (u8 *) msg->hdr;
@@ -509,7 +513,10 @@
 	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 	addr[3] = secret;
 	len[3] = secret_len;
-	md5_vector(4, addr, len, msg->hdr->authenticator);
+	if (md5_vector(4, addr, len, msg->hdr->authenticator) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		return -1;
+	}
 
 	if (wpabuf_len(msg->buf) > 0xffff) {
 		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
@@ -535,16 +542,20 @@
 	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 	os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
 	if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
-		     wpabuf_len(msg->buf), pos) < 0)
+		     wpabuf_len(msg->buf), pos) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
 		return -1;
+	}
 
 	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
 	addr[0] = wpabuf_head_u8(msg->buf);
 	len[0] = wpabuf_len(msg->buf);
 	addr[1] = secret;
 	len[1] = secret_len;
-	if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
+	if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
 		return -1;
+	}
 
 	if (wpabuf_len(msg->buf) > 0xffff) {
 		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
@@ -555,8 +566,8 @@
 }
 
 
-void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
-			    size_t secret_len)
+int radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+			   size_t secret_len)
 {
 	const u8 *addr[2];
 	size_t len[2];
@@ -567,17 +578,22 @@
 	len[0] = wpabuf_len(msg->buf);
 	addr[1] = secret;
 	len[1] = secret_len;
-	md5_vector(2, addr, len, msg->hdr->authenticator);
+	if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		return -1;
+	}
 
 	if (wpabuf_len(msg->buf) > 0xffff) {
 		wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
 			   (unsigned long) wpabuf_len(msg->buf));
+		return -1;
 	}
+	return 0;
 }
 
 
-void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
-				 size_t secret_len, const u8 *req_authenticator)
+int radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+				size_t secret_len, const u8 *req_authenticator)
 {
 	const u8 *addr[2];
 	size_t len[2];
@@ -588,12 +604,17 @@
 	len[0] = wpabuf_len(msg->buf);
 	addr[1] = secret;
 	len[1] = secret_len;
-	md5_vector(2, addr, len, msg->hdr->authenticator);
+	if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		return -1;
+	}
 
 	if (wpabuf_len(msg->buf) > 0xffff) {
 		wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
 			   (unsigned long) wpabuf_len(msg->buf));
+		return -1;
 	}
+	return 0;
 }
 
 
@@ -614,7 +635,10 @@
 	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 	addr[3] = secret;
 	len[3] = secret_len;
-	md5_vector(4, addr, len, hash);
+	if (md5_vector(4, addr, len, hash) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		return 1;
+	}
 	return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
 }
 
@@ -642,7 +666,10 @@
 	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 	addr[3] = secret;
 	len[3] = secret_len;
-	md5_vector(4, addr, len, hash);
+	if (md5_vector(4, addr, len, hash) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		return 1;
+	}
 	if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
 		return 1;
 
@@ -674,8 +701,11 @@
 		  sizeof(orig_authenticator));
 	os_memset(msg->hdr->authenticator, 0,
 		  sizeof(msg->hdr->authenticator));
-	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
-		 wpabuf_len(msg->buf), auth);
+	if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		     wpabuf_len(msg->buf), auth) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		return 1;
+	}
 	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
 	os_memcpy(msg->hdr->authenticator, orig_authenticator,
 		  sizeof(orig_authenticator));
@@ -972,8 +1002,10 @@
 			  sizeof(msg->hdr->authenticator));
 	}
 	if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
-		     wpabuf_len(msg->buf), auth) < 0)
+		     wpabuf_len(msg->buf), auth) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
 		return 1;
+	}
 	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
 	if (req_auth) {
 		os_memcpy(msg->hdr->authenticator, orig_authenticator,
@@ -1185,6 +1217,7 @@
 			elen[1] = MD5_MAC_LEN;
 		}
 		if (md5_vector(first ? 3 : 2, addr, elen, hash) < 0) {
+			wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
 			os_free(plain);
 			return NULL;
 		}
@@ -1213,10 +1246,10 @@
 }
 
 
-static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
-			   const u8 *req_authenticator,
-			   const u8 *secret, size_t secret_len,
-			   u8 *ebuf, size_t *elen)
+static int encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
+			  const u8 *req_authenticator,
+			  const u8 *secret, size_t secret_len,
+			  u8 *ebuf, size_t *elen)
 {
 	int i, len, first = 1;
 	u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
@@ -1250,7 +1283,10 @@
 			addr[1] = pos - MD5_MAC_LEN;
 			_len[1] = MD5_MAC_LEN;
 		}
-		md5_vector(first ? 3 : 2, addr, _len, hash);
+		if (md5_vector(first ? 3 : 2, addr, _len, hash) < 0) {
+			wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+			return -1;
+		}
 		first = 0;
 
 		for (i = 0; i < MD5_MAC_LEN; i++)
@@ -1258,6 +1294,8 @@
 
 		len -= MD5_MAC_LEN;
 	}
+
+	return 0;
 }
 
 
@@ -1375,8 +1413,9 @@
 	salt |= 0x8000;
 	WPA_PUT_BE16(pos, salt);
 	pos += 2;
-	encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
-		       secret_len, pos, &elen);
+	if (encrypt_ms_key(send_key, send_key_len, salt, req_authenticator,
+			   secret, secret_len, pos, &elen) < 0)
+		return 0;
 	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
 
 	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
@@ -1400,8 +1439,9 @@
 	salt ^= 1;
 	WPA_PUT_BE16(pos, salt);
 	pos += 2;
-	encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
-		       secret_len, pos, &elen);
+	if (encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator,
+			   secret, secret_len, pos, &elen) < 0)
+		return 0;
 	vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
 
 	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
@@ -1492,7 +1532,10 @@
 	len[0] = secret_len;
 	addr[1] = msg->hdr->authenticator;
 	len[1] = 16;
-	md5_vector(2, addr, len, hash);
+	if (md5_vector(2, addr, len, hash) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		return -1;
+	}
 
 	for (i = 0; i < 16; i++)
 		buf[i] ^= hash[i];
@@ -1503,7 +1546,10 @@
 		len[0] = secret_len;
 		addr[1] = &buf[pos - 16];
 		len[1] = 16;
-		md5_vector(2, addr, len, hash);
+		if (md5_vector(2, addr, len, hash) < 0) {
+			wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+			return -1;
+		}
 
 		for (i = 0; i < 16; i++)
 			buf[pos + i] ^= hash[i];
@@ -1792,7 +1838,10 @@
 		len[0] = secret_len;
 		addr[1] = pos - 16;
 		len[1] = 16;
-		md5_vector(2, addr, len, hash);
+		if (md5_vector(2, addr, len, hash) < 0) {
+			wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+			goto out;
+		}
 
 		for (i = 0; i < 16; i++)
 			pos[i] ^= hash[i];
@@ -1809,7 +1858,10 @@
 	len[1] = 16;
 	addr[2] = salt;
 	len[2] = 2;
-	md5_vector(3, addr, len, hash);
+	if (md5_vector(3, addr, len, hash) < 0) {
+		wpa_printf(MSG_INFO, "RADIUS: MD5 not available");
+		goto out;
+	}
 
 	for (i = 0; i < 16; i++)
 		pos[i] ^= hash[i];
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 05fddba..09d3591 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -221,7 +221,6 @@
 #define RADIUS_VENDOR_ID_WFA 40808
 
 enum {
-	RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1,
 	RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2,
 	RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
 	RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
@@ -276,11 +275,10 @@
 int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
 			       size_t secret_len,
 			       const struct radius_hdr *req_hdr);
-void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
-			    size_t secret_len);
-void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
-				 size_t secret_len,
-				 const u8 *req_authenticator);
+int radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+			   size_t secret_len);
+int radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+				size_t secret_len, const u8 *req_authenticator);
 int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
 			       size_t secret_len);
 int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 2a7f361..705aaef 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -482,8 +482,11 @@
 		wpa_printf(MSG_DEBUG,
 			   "RADIUS: Updated Acct-Delay-Time to %u for retransmission",
 			   delay_time);
-		radius_msg_finish_acct(entry->msg, entry->shared_secret,
-				       entry->shared_secret_len);
+		if (radius_msg_finish_acct(entry->msg, entry->shared_secret,
+					   entry->shared_secret_len) < 0) {
+			wpa_printf(MSG_INFO, "Failed to build RADIUS message");
+			return -1;
+		}
 		if (radius->conf->msg_dumps)
 			radius_msg_dump(entry->msg);
 	}
@@ -878,7 +881,14 @@
 		}
 		shared_secret = conf->acct_server->shared_secret;
 		shared_secret_len = conf->acct_server->shared_secret_len;
-		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
+		if (radius_msg_finish_acct(msg, shared_secret,
+					   shared_secret_len) < 0) {
+			hostapd_logger(radius->ctx, NULL,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "Failed to build RADIUS accounting message");
+			return -1;
+		}
 		name = "accounting";
 		s = radius->acct_sock;
 		conf->acct_server->requests++;
@@ -900,7 +910,14 @@
 		}
 		shared_secret = conf->auth_server->shared_secret;
 		shared_secret_len = conf->auth_server->shared_secret_len;
-		radius_msg_finish(msg, shared_secret, shared_secret_len);
+		if (radius_msg_finish(msg, shared_secret, shared_secret_len) <
+		    0) {
+			hostapd_logger(radius->ctx, NULL,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "Failed to build RADIUS authentication message");
+			return -1;
+		}
 		name = "authentication";
 		s = radius->auth_sock;
 		conf->auth_server->requests++;
@@ -1099,7 +1116,7 @@
 	struct radius_hdr *hdr;
 	struct radius_rx_handler *handlers;
 	size_t num_handlers, i;
-	struct radius_msg_list *req, *prev_req;
+	struct radius_msg_list *req, *prev_req, *r;
 	struct os_reltime now;
 	struct hostapd_radius_server *rconf;
 	int invalid_authenticator = 0;
@@ -1224,7 +1241,6 @@
 		break;
 	}
 
-	prev_req = NULL;
 	req = radius->msgs;
 	while (req) {
 		/* TODO: also match by src addr:port of the packet when using
@@ -1236,7 +1252,6 @@
 		    hdr->identifier)
 			break;
 
-		prev_req = req;
 		req = req->next;
 	}
 
@@ -1259,13 +1274,6 @@
 		       roundtrip / 100, roundtrip % 100);
 	rconf->round_trip_time = roundtrip;
 
-	/* Remove ACKed RADIUS packet from retransmit list */
-	if (prev_req)
-		prev_req->next = req->next;
-	else
-		radius->msgs = req->next;
-	radius->num_msgs--;
-
 	for (i = 0; i < num_handlers; i++) {
 		RadiusRxResult res;
 		res = handlers[i].handler(msg, req->msg, req->shared_secret,
@@ -1276,6 +1284,19 @@
 			radius_msg_free(msg);
 			/* fall through */
 		case RADIUS_RX_QUEUED:
+			/* Remove ACKed RADIUS packet from retransmit list */
+			prev_req = NULL;
+			for (r = radius->msgs; r; r = r->next) {
+				if (r == req)
+					break;
+				prev_req = r;
+			}
+			if (prev_req)
+				prev_req->next = req->next;
+			else
+				radius->msgs = req->next;
+			radius->num_msgs--;
+
 			radius_client_msg_free(req);
 			return;
 		case RADIUS_RX_INVALID_AUTHENTICATOR:
@@ -1297,7 +1318,6 @@
 		       msg_type, hdr->code, hdr->identifier,
 		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
 		       "");
-	radius_client_msg_free(req);
 
  fail:
 	radius_msg_free(msg);
@@ -1509,8 +1529,10 @@
 		if (entry->msg_type == RADIUS_ACCT) {
 			entry->shared_secret = shared_secret;
 			entry->shared_secret_len = shared_secret_len;
-			radius_msg_finish_acct(entry->msg, shared_secret,
-					       shared_secret_len);
+			if (radius_msg_finish_acct(entry->msg, shared_secret,
+						   shared_secret_len) < 0)
+				wpa_printf(MSG_INFO,
+					   "RADIUS: Failed to update accounting message");
 		}
 	}
 }
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index fa36915..c9497c0 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -86,7 +86,6 @@
 	struct radius_msg *last_reply;
 	u8 last_authenticator[16];
 
-	unsigned int remediation:1;
 	unsigned int macacl:1;
 	unsigned int t_c_filtering:1;
 
@@ -147,7 +146,8 @@
 	/**
 	 * conf_ctx - Context pointer for callbacks
 	 *
-	 * This is used as the ctx argument in get_eap_user() calls.
+	 * This is used as the ctx argument in get_eap_user() and acct_req_cb()
+	 * calls.
 	 */
 	void *conf_ctx;
 
@@ -195,6 +195,27 @@
 			    int phase2, struct eap_user *user);
 
 	/**
+	 * acct_req_cb - Callback for processing received RADIUS accounting
+	 * requests
+	 * @ctx: Context data from conf_ctx
+	 * @msg: Received RADIUS accounting request
+	 * @status_type: Status type from the message (parsed Acct-Status-Type
+	 * attribute)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This can be used to log accounting information into file, database,
+	 * syslog server, etc.
+	 * Callback should not modify the message.
+	 * If 0 is returned, response is automatically created. Otherwise,
+	 * no response is created.
+	 *
+	 * acct_req_cb can be set to null to omit any custom processing of
+	 * account requests. Statistics counters will be incremented in any
+	 * case.
+	 */
+	int (*acct_req_cb)(void *ctx, struct radius_msg *msg, u32 status_type);
+
+	/**
 	 * eap_req_id_text - Optional data for EAP-Request/Identity
 	 *
 	 * This can be used to configure an optional, displayable message that
@@ -215,10 +236,6 @@
 	char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
 
-	char *subscr_remediation_url;
-	u8 subscr_remediation_method;
-	char *hs20_sim_provisioning_url;
-
 	char *t_c_server_url;
 
 #ifdef CONFIG_SQLITE
@@ -243,44 +260,6 @@
 static void radius_server_session_remove_timeout(void *eloop_ctx,
 						 void *timeout_ctx);
 
-#ifdef CONFIG_SQLITE
-#ifdef CONFIG_HS20
-
-static int db_table_exists(sqlite3 *db, const char *name)
-{
-	char cmd[128];
-
-	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
-	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
-}
-
-
-static int db_table_create_sim_provisioning(sqlite3 *db)
-{
-	char *err = NULL;
-	const char *sql =
-		"CREATE TABLE sim_provisioning("
-		" mobile_identifier_hash TEXT PRIMARY KEY,"
-		" imsi TEXT,"
-		" mac_addr TEXT,"
-		" eap_method TEXT,"
-		" timestamp TEXT"
-		");";
-
-	RADIUS_DEBUG("Adding database table for SIM provisioning information");
-	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
-		RADIUS_ERROR("SQLite error: %s", err);
-		sqlite3_free(err);
-		return -1;
-	}
-
-	return 0;
-}
-
-#endif /* CONFIG_HS20 */
-#endif /* CONFIG_SQLITE */
-
-
 void srv_log(struct radius_session *sess, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
 
@@ -780,117 +759,6 @@
 }
 
 
-#ifdef CONFIG_HS20
-
-static int radius_server_is_sim_method(struct radius_session *sess)
-{
-	const char *name;
-
-	name = eap_get_method(sess->eap);
-	return name &&
-		(os_strcmp(name, "SIM") == 0 ||
-		 os_strcmp(name, "AKA") == 0 ||
-		 os_strcmp(name, "AKA'") == 0);
-}
-
-
-static int radius_server_hs20_missing_sim_pps(struct radius_msg *request)
-{
-	u8 *buf, *pos, *end, type, sublen;
-	size_t len;
-
-	buf = NULL;
-	for (;;) {
-		if (radius_msg_get_attr_ptr(request,
-					    RADIUS_ATTR_VENDOR_SPECIFIC,
-					    &buf, &len, buf) < 0)
-			return 0;
-		if (len < 6)
-			continue;
-		pos = buf;
-		end = buf + len;
-		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
-			continue;
-		pos += 4;
-
-		type = *pos++;
-		sublen = *pos++;
-		if (sublen < 2)
-			continue; /* invalid length */
-		sublen -= 2; /* skip header */
-		if (pos + sublen > end)
-			continue; /* invalid WFA VSA */
-
-		if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION)
-			continue;
-
-		RADIUS_DUMP("HS2.0 mobile device version", pos, sublen);
-		if (sublen < 1 + 2)
-			continue;
-		if (pos[0] == 0)
-			continue; /* Release 1 STA does not support provisioning
-
-				   */
-		/* UpdateIdentifier 0 indicates no PPS MO */
-		return WPA_GET_BE16(pos + 1) == 0;
-	}
-}
-
-
-#define HS20_MOBILE_ID_HASH_LEN 16
-
-static int radius_server_sim_provisioning_session(struct radius_session *sess,
-						  const u8 *hash)
-{
-#ifdef CONFIG_SQLITE
-	char *sql;
-	char addr_txt[ETH_ALEN * 3];
-	char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1];
-	struct os_time now;
-	int res;
-	const char *imsi, *eap_method;
-
-	if (!sess->server->db ||
-	    (!db_table_exists(sess->server->db, "sim_provisioning") &&
-	     db_table_create_sim_provisioning(sess->server->db) < 0))
-		return -1;
-
-	imsi = eap_get_imsi(sess->eap);
-	if (!imsi)
-		return -1;
-
-	eap_method = eap_get_method(sess->eap);
-	if (!eap_method)
-		return -1;
-
-	os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
-		    MAC2STR(sess->mac_addr));
-	wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash,
-			 HS20_MOBILE_ID_HASH_LEN);
-
-	os_get_time(&now);
-	sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)",
-			      hash_txt, imsi, addr_txt, eap_method, now.sec);
-	if (!sql)
-		return -1;
-
-	if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
-	    SQLITE_OK) {
-		RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s",
-			     sqlite3_errmsg(sess->server->db));
-		res = -1;
-	} else {
-		res = 0;
-	}
-	sqlite3_free(sql);
-	return res;
-#endif /* CONFIG_SQLITE */
-	return -1;
-}
-
-#endif /* CONFIG_HS20 */
-
-
 static struct radius_msg *
 radius_server_encapsulate_eap(struct radius_server_data *data,
 			      struct radius_client *client,
@@ -992,74 +860,6 @@
 	}
 
 #ifdef CONFIG_HS20
-	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
-	    data->subscr_remediation_url) {
-		u8 *buf;
-		size_t url_len = os_strlen(data->subscr_remediation_url);
-		buf = os_malloc(1 + url_len);
-		if (buf == NULL) {
-			radius_msg_free(msg);
-			return NULL;
-		}
-		buf[0] = data->subscr_remediation_method;
-		os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
-		if (!radius_msg_add_wfa(
-			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
-			    buf, 1 + url_len)) {
-			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
-		}
-		os_free(buf);
-	} else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
-		u8 buf[1];
-		if (!radius_msg_add_wfa(
-			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
-			    buf, 0)) {
-			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
-		}
-	} else if (code == RADIUS_CODE_ACCESS_ACCEPT &&
-		   data->hs20_sim_provisioning_url &&
-		   radius_server_is_sim_method(sess) &&
-		   radius_server_hs20_missing_sim_pps(request)) {
-		u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN];
-		size_t prefix_len, url_len;
-
-		RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning");
-
-		if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) {
-			radius_msg_free(msg);
-			return NULL;
-		}
-		RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash",
-			    hash, HS20_MOBILE_ID_HASH_LEN);
-
-		if (radius_server_sim_provisioning_session(sess, hash) < 0) {
-			radius_msg_free(msg);
-			return NULL;
-		}
-
-		prefix_len = os_strlen(data->hs20_sim_provisioning_url);
-		url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN;
-		buf = os_malloc(1 + url_len + 1);
-		if (!buf) {
-			radius_msg_free(msg);
-			return NULL;
-		}
-		pos = buf;
-		*pos++ = data->subscr_remediation_method;
-		os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len);
-		pos += prefix_len;
-		wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1,
-				 hash, HS20_MOBILE_ID_HASH_LEN);
-		RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s",
-			     (char *) &buf[1]);
-		if (!radius_msg_add_wfa(
-			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
-			    buf, 1 + url_len)) {
-			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
-		}
-		os_free(buf);
-	}
-
 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
 		u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */
 		const char *url = data->t_c_server_url, *pos;
@@ -1148,6 +948,8 @@
 				  client->shared_secret_len,
 				  hdr->authenticator) < 0) {
 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+		radius_msg_free(msg);
+		return NULL;
 	}
 
 	if (code == RADIUS_CODE_ACCESS_ACCEPT)
@@ -1237,6 +1039,8 @@
 				  client->shared_secret_len,
 				  hdr->authenticator) < 0) {
 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+		radius_msg_free(msg);
+		return NULL;
 	}
 
 	return msg;
@@ -1288,6 +1092,8 @@
 				  hdr->authenticator) <
 	    0) {
 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+		radius_msg_free(msg);
+		return -1;
 	}
 
 	if (wpa_debug_level <= MSG_MSGDUMP) {
@@ -1815,6 +1621,7 @@
 	int from_port = 0;
 	struct radius_hdr *hdr;
 	struct wpabuf *rbuf;
+	u32 status_type;
 
 	buf = os_malloc(RADIUS_MAX_MSG_LEN);
 	if (buf == NULL) {
@@ -1896,7 +1703,20 @@
 		goto fail;
 	}
 
-	/* TODO: Write accounting information to a file or database */
+	/* Parse Acct-Status-Type from Accounting-Request */
+	if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+				      &status_type) != 0) {
+		RADIUS_DEBUG("Unable to parse Acct-Status-Type from %s", abuf);
+		goto fail;
+	}
+
+	/* Process accounting information by configured callback */
+	if (data->acct_req_cb &&
+	    data->acct_req_cb(data->conf_ctx, msg, status_type) != 0) {
+		RADIUS_DEBUG("Accounting request callback returned non-zero code indicating processing failure (from %s)",
+			     abuf);
+		goto fail;
+	}
 
 	hdr = radius_msg_get_hdr(msg);
 
@@ -1904,9 +1724,12 @@
 	if (resp == NULL)
 		goto fail;
 
-	radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
-				    client->shared_secret_len,
-				    hdr->authenticator);
+	if (radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
+					client->shared_secret_len,
+					hdr->authenticator) < 0) {
+		RADIUS_ERROR("Failed to add Message-Authenticator attribute");
+		goto fail;
+	}
 
 	RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
 	if (wpa_debug_level <= MSG_MSGDUMP) {
@@ -2221,6 +2044,7 @@
 	conf->eap_cfg->eap_server = 1;
 	data->ipv6 = conf->ipv6;
 	data->get_eap_user = conf->get_eap_user;
+	data->acct_req_cb = conf->acct_req_cb;
 	if (conf->eap_req_id_text) {
 		data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
 		if (!data->eap_req_id_text)
@@ -2231,20 +2055,6 @@
 	}
 	data->erp_domain = conf->erp_domain;
 
-	if (conf->subscr_remediation_url) {
-		data->subscr_remediation_url =
-			os_strdup(conf->subscr_remediation_url);
-		if (!data->subscr_remediation_url)
-			goto fail;
-	}
-	data->subscr_remediation_method = conf->subscr_remediation_method;
-	if (conf->hs20_sim_provisioning_url) {
-		data->hs20_sim_provisioning_url =
-			os_strdup(conf->hs20_sim_provisioning_url);
-		if (!data->hs20_sim_provisioning_url)
-			goto fail;
-	}
-
 	if (conf->t_c_server_url) {
 		data->t_c_server_url = os_strdup(conf->t_c_server_url);
 		if (!data->t_c_server_url)
@@ -2359,8 +2169,6 @@
 #ifdef CONFIG_RADIUS_TEST
 	os_free(data->dump_msk_file);
 #endif /* CONFIG_RADIUS_TEST */
-	os_free(data->subscr_remediation_url);
-	os_free(data->hs20_sim_provisioning_url);
 	os_free(data->t_c_server_url);
 
 #ifdef CONFIG_SQLITE
@@ -2528,7 +2336,6 @@
 				 phase2, user);
 	if (ret == 0 && user) {
 		sess->accept_attr = user->accept_attr;
-		sess->remediation = user->remediation;
 		sess->macacl = user->macacl;
 		sess->t_c_timestamp = user->t_c_timestamp;
 	}
@@ -2827,8 +2634,12 @@
 		return -1;
 	}
 
-	radius_msg_finish_acct(msg, (u8 *) client->shared_secret,
-			       client->shared_secret_len);
+	if (radius_msg_finish_acct(msg, (u8 *) client->shared_secret,
+				   client->shared_secret_len) < 0) {
+		RADIUS_ERROR("Failed to add Message-Authenticator attribute");
+		radius_msg_free(msg);
+		return -1;
+	}
 
 	if (wpa_debug_level <= MSG_MSGDUMP)
 		radius_msg_dump(msg);
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 43192e5..5440558 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -10,6 +10,7 @@
 #define RADIUS_SERVER_H
 
 struct radius_server_data;
+struct radius_msg;
 struct eap_user;
 
 /**
@@ -47,7 +48,8 @@
 	/**
 	 * conf_ctx - Context pointer for callbacks
 	 *
-	 * This is used as the ctx argument in get_eap_user() calls.
+	 * This is used as the ctx argument in get_eap_user() and acct_req_cb()
+	 * calls.
 	 */
 	void *conf_ctx;
 
@@ -76,6 +78,27 @@
 			    int phase2, struct eap_user *user);
 
 	/**
+	 * acct_req_cb - Callback for processing received RADIUS accounting
+	 * requests
+	 * @ctx: Context data from conf_ctx
+	 * @msg: Received RADIUS accounting request
+	 * @status_type: Status type from the message (parsed Acct-Status-Type
+	 * attribute)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This can be used to log accounting information into file, database,
+	 * syslog server, etc.
+	 * Callback should not modify the message.
+	 * If 0 is returned, response is automatically created. Otherwise,
+	 * no response is created.
+	 *
+	 * acct_req_cb can be set to NULL to omit any custom processing of
+	 * accounting requests. Statistics counters will be incremented in any
+	 * case.
+	 */
+	int (*acct_req_cb)(void *ctx, struct radius_msg *msg, u32 status_type);
+
+	/**
 	 * eap_req_id_text - Optional data for EAP-Request/Identity
 	 *
 	 * This can be used to configure an optional, displayable message that
@@ -96,10 +119,6 @@
 	const char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
 
-	char *subscr_remediation_url;
-	u8 subscr_remediation_method;
-	char *hs20_sim_provisioning_url;
-
 	char *t_c_server_url;
 
 	struct eap_config *eap_cfg;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index d145da0..264013c 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -256,8 +256,7 @@
 	if (rbuf == NULL)
 		return;
 
-	reply->type = (sm->proto == WPA_PROTO_RSN ||
-		       sm->proto == WPA_PROTO_OSEN) ?
+	reply->type = (sm->proto == WPA_PROTO_RSN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info = WPA_KEY_INFO_REQUEST | ver;
 	key_info |= WPA_KEY_INFO_SECURE;
@@ -482,8 +481,7 @@
 
 	if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
-	    !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
-	{
+	    !wpa_key_mgmt_ft(sm->key_mgmt)) {
 		/* Send EAPOL-Start to trigger full EAP authentication. */
 		u8 *buf;
 		size_t buflen;
@@ -637,8 +635,7 @@
 		return -1;
 	}
 
-	reply->type = (sm->proto == WPA_PROTO_RSN ||
-		       sm->proto == WPA_PROTO_OSEN) ?
+	reply->type = (sm->proto == WPA_PROTO_RSN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info = ver | WPA_KEY_INFO_KEY_TYPE;
 	if (sm->ptk_set && sm->proto != WPA_PROTO_WPA)
@@ -650,9 +647,11 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	if (sm->encrypt_eapol_m2)
 		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+	if (sm->eapol_2_key_info_set_mask)
+		key_info |= sm->eapol_2_key_info_set_mask;
 #endif /* CONFIG_TESTING_OPTIONS */
 	WPA_PUT_BE16(reply->key_info, key_info);
-	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
+	if (sm->proto == WPA_PROTO_RSN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
 		os_memcpy(reply->key_length, key->key_length, 2);
@@ -1226,14 +1225,16 @@
 	enum wpa_alg alg;
 	const u8 *key_rsc;
 
-	if (sm->ptk.installed) {
+	if (sm->ptk.installed ||
+	    (sm->ptk.installed_rx && (key_flag & KEY_FLAG_NEXT))) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"WPA: Do not re-install same PTK to the driver");
 		return 0;
 	}
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-		"WPA: Installing PTK to the driver");
+		"WPA: Installing %sTK to the driver",
+		(key_flag & KEY_FLAG_NEXT) ? "next " : "");
 
 	if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
@@ -1257,7 +1258,7 @@
 	}
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 
-	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+	if (sm->proto == WPA_PROTO_RSN) {
 		key_rsc = null_rsc;
 	} else {
 		key_rsc = key->key_rsc;
@@ -1267,6 +1268,9 @@
 	if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm),
 			   sm->keyidx_active, 1, key_rsc, rsclen, sm->ptk.tk,
 			   keylen, KEY_FLAG_PAIRWISE | key_flag) < 0) {
+		if (key_flag & KEY_FLAG_NEXT)
+			return 0; /* Not all drivers support this, so do not
+				   * report failures on the RX-only set_key */
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr="
 			MACSTR " idx=%d key_flag=0x%x)",
@@ -1292,11 +1296,15 @@
 	wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher,
 			 sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
 
-	/* TK is not needed anymore in supplicant */
-	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
-	sm->ptk.tk_len = 0;
-	sm->ptk.installed = 1;
-	sm->tk_set = true;
+	if (key_flag & KEY_FLAG_NEXT) {
+		sm->ptk.installed_rx = true;
+	} else {
+		/* TK is not needed anymore in supplicant */
+		os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+		sm->ptk.tk_len = 0;
+		sm->ptk.installed = 1;
+		sm->tk_set = true;
+	}
 
 	if (sm->wpa_ptk_rekey) {
 		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
@@ -2361,8 +2369,7 @@
 		return -1;
 	}
 
-	reply->type = (sm->proto == WPA_PROTO_RSN ||
-		       sm->proto == WPA_PROTO_OSEN) ?
+	reply->type = (sm->proto == WPA_PROTO_RSN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_SECURE;
 	key_info |= ver | WPA_KEY_INFO_KEY_TYPE;
@@ -2375,7 +2382,7 @@
 		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
 #endif /* CONFIG_TESTING_OPTIONS */
 	WPA_PUT_BE16(reply->key_info, key_info);
-	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
+	if (sm->proto == WPA_PROTO_RSN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
 		os_memcpy(reply->key_length, key->key_length, 2);
@@ -2898,6 +2905,16 @@
 	    wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
 		goto failed;
 
+	if (!sm->use_ext_key_id &&
+	    wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX | KEY_FLAG_NEXT)) {
+		/* Continue anyway since the many drivers do not support
+		 * configuration of the TK for RX-only purposes for cases where
+		 * multiple keys might be in use in parallel and this being an
+		 * optional optimization to avoid race condition during TK
+		 * changes that could result in some protected frames getting
+		 * discarded. */
+	}
+
 	if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
 				       key_info, &sm->ptk) < 0)
 		goto failed;
@@ -3017,8 +3034,7 @@
 	if (rbuf == NULL)
 		return -1;
 
-	reply->type = (sm->proto == WPA_PROTO_RSN ||
-		       sm->proto == WPA_PROTO_OSEN) ?
+	reply->type = (sm->proto == WPA_PROTO_RSN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
 	key_info |= ver | WPA_KEY_INFO_SECURE;
@@ -3027,7 +3043,7 @@
 	else
 		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
 	WPA_PUT_BE16(reply->key_info, key_info);
-	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
+	if (sm->proto == WPA_PROTO_RSN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
 		os_memcpy(reply->key_length, key->key_length, 2);
@@ -3426,6 +3442,28 @@
 }
 
 
+static void wpa_sm_tptk_to_ptk(struct wpa_sm *sm)
+{
+	sm->tptk_set = 0;
+	sm->ptk_set = 1;
+	os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+
+	if (wpa_sm_pmf_enabled(sm)) {
+		os_memcpy(sm->last_kck, sm->ptk.kck, sm->ptk.kck_len);
+		sm->last_kck_len = sm->ptk.kck_len;
+		sm->last_kck_pmk_len = sm->pmk_len;
+		sm->last_kck_key_mgmt = sm->key_mgmt;
+		sm->last_kck_eapol_key_ver = sm->last_eapol_key_ver;
+		os_memcpy(sm->last_kck_aa, wpa_sm_get_auth_addr(sm), ETH_ALEN);
+	} else {
+		os_memset(sm->last_kck, 0, sizeof(sm->last_kck));
+		sm->last_kck_len = 0;
+		os_memset(sm->last_kck_aa, 0, ETH_ALEN);
+	}
+}
+
+
 static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
 					       struct wpa_eapol_key *key,
 					       u16 ver,
@@ -3455,10 +3493,7 @@
 		continue_fuzz:
 #endif /* TEST_FUZZ */
 			ok = 1;
-			sm->tptk_set = 0;
-			sm->ptk_set = 1;
-			os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
-			os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+			wpa_sm_tptk_to_ptk(sm);
 			/*
 			 * This assures the same TPTK in sm->tptk can never be
 			 * copied twice to sm->ptk as the new PTK. In
@@ -3711,12 +3746,8 @@
 	WPA_PUT_BE16(pos, *key_data_len);
 	bin_clear_free(tmp, *key_data_len);
 
-	if (sm->tptk_set) {
-		sm->tptk_set = 0;
-		sm->ptk_set = 1;
-		os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
-		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
-	}
+	if (sm->tptk_set)
+		wpa_sm_tptk_to_ptk(sm);
 
 	os_memcpy(sm->rx_replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
@@ -3816,7 +3847,8 @@
  * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
  * @len: Length of the EAPOL frame
  * @encrypted: Whether the frame was encrypted
- * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
+ * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure,
+ *	    -2 = reply counter did not increase.
  *
  * This function is called for each received EAPOL frame. Other than EAPOL-Key
  * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
@@ -3925,6 +3957,7 @@
 		      WPA_REPLAY_COUNTER_LEN) <= 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: EAPOL-Key Replay Counter did not increase - dropping packet");
+		ret = -2;
 		goto out;
 	}
 
@@ -4039,6 +4072,8 @@
 		goto out;
 	}
 
+	sm->last_eapol_key_ver = ver;
+
 	if ((key_info & WPA_KEY_INFO_MIC) &&
 	    wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
 		goto out;
@@ -4050,7 +4085,7 @@
 	}
 #endif /* CONFIG_FILS */
 
-	if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
+	if (sm->proto == WPA_PROTO_RSN &&
 	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
 		/*
 		 * Only decrypt the Key Data field if the frame's authenticity
@@ -4120,8 +4155,7 @@
 {
 	switch (sm->key_mgmt) {
 	case WPA_KEY_MGMT_IEEE8021X:
-		return ((sm->proto == WPA_PROTO_RSN ||
-			 sm->proto == WPA_PROTO_OSEN) ?
+		return ((sm->proto == WPA_PROTO_RSN) ?
 			RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
 			WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
 	case WPA_KEY_MGMT_PSK:
@@ -4374,6 +4408,8 @@
 	wpabuf_free(sm->test_assoc_ie);
 	wpabuf_free(sm->test_eapol_m2_elems);
 	wpabuf_free(sm->test_eapol_m4_elems);
+	wpabuf_free(sm->test_rsnxe_data);
+	wpabuf_free(sm->test_rsnxe_mask);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_FILS_SK_PFS
 	crypto_ecdh_deinit(sm->fils_ecdh);
@@ -4387,6 +4423,7 @@
 #ifdef CONFIG_DPP2
 	wpabuf_clear_free(sm->dpp_z);
 #endif /* CONFIG_DPP2 */
+	os_memset(sm->last_kck, 0, sizeof(sm->last_kck));
 	os_free(sm);
 }
 
@@ -4948,6 +4985,9 @@
 	case WPA_PARAM_USE_EXT_KEY_ID:
 		sm->use_ext_key_id = value;
 		break;
+	case WPA_PARAM_SPP_AMSDU:
+		sm->spp_amsdu = !!value;
+		break;
 #ifdef CONFIG_TESTING_OPTIONS
 	case WPA_PARAM_FT_RSNXE_USED:
 		sm->ft_rsnxe_used = value;
@@ -4973,6 +5013,9 @@
 	case WPA_PARAM_ENCRYPT_EAPOL_M4:
 		sm->encrypt_eapol_m4 = value;
 		break;
+	case WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK:
+		sm->eapol_2_key_info_set_mask = value;
+		break;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_DPP2
 	case WPA_PARAM_DPP_PFS:
@@ -5230,6 +5273,76 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+
+int wpa_sm_set_test_rsnxe_data(struct wpa_sm *sm, struct wpabuf *data,
+			       struct wpabuf *mask)
+{
+	size_t data_len = 0, mask_len = 0;
+
+	wpabuf_free(sm->test_rsnxe_data);
+	sm->test_rsnxe_data = NULL;
+	wpabuf_free(sm->test_rsnxe_mask);
+	sm->test_rsnxe_mask = NULL;
+
+	if (!data && !mask)
+		return 0;
+
+	if (data)
+		data_len = wpabuf_len(data);
+	if (mask)
+		mask_len = wpabuf_len(mask);
+
+	if (data_len != mask_len || data_len > 255)
+		return -1;
+
+	sm->test_rsnxe_data = data;
+	sm->test_rsnxe_mask = mask;
+
+	return 0;
+}
+
+
+static int wpa_set_test_rsnxe_data(struct wpa_sm *sm, u8 *rsnxe,
+				   size_t orig_len, size_t max_len)
+{
+	const u8 *data, *mask;
+	size_t i, data_len;
+
+	if (!sm->test_rsnxe_data || !sm->test_rsnxe_mask)
+		return orig_len;
+
+	mask = wpabuf_head(sm->test_rsnxe_mask);
+	data = wpabuf_head(sm->test_rsnxe_data);
+	data_len = wpabuf_len(sm->test_rsnxe_data);
+	if (max_len < data_len + 2) {
+		wpa_printf(MSG_ERROR, "Couldn't fit RSNXE test data");
+		return -1;
+	}
+
+	/* Set data after original RSNXE to zero */
+	if (orig_len < data_len + 2)
+		os_memset(&rsnxe[orig_len], 0, data_len + 2 - orig_len);
+
+	/* Set EID and length fields */
+	*rsnxe++ = WLAN_EID_RSNX;
+	*rsnxe++ = data_len;
+
+	/* Preserve original RSNXE bit value when mask bit is zero */
+	for (i = 0; i < data_len; i++) {
+		if (!mask[i])
+			continue;
+
+		rsnxe[i] &= ~mask[i];
+		rsnxe[i] |= data[i] & mask[i];
+	}
+
+	return data_len + 2;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 /**
  * wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -5248,6 +5361,11 @@
 	res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len);
 	if (res < 0)
 		return -1;
+#ifdef CONFIG_TESTING_OPTIONS
+	res = wpa_set_test_rsnxe_data(sm, rsnxe, res, *rsnxe_len);
+	if (res < 0)
+		return -1;
+#endif /* CONFIG_TESTING_OPTIONS */
 	*rsnxe_len = res;
 
 	wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len);
@@ -5565,6 +5683,25 @@
 }
 
 
+int wpa_sm_pmksa_get_pmk(struct wpa_sm *sm, const u8 *aa, const u8 **pmk,
+			 size_t *pmk_len, const u8 **pmkid)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	pmksa = wpa_sm_pmksa_cache_get(sm, aa, NULL, NULL, 0);
+	if (!pmksa) {
+		wpa_printf(MSG_DEBUG, "RSN: Failed to get PMKSA for " MACSTR,
+			   MAC2STR(aa));
+		return -1;
+	}
+
+	*pmk = pmksa->pmk;
+	*pmk_len = pmksa->pmk_len;
+	*pmkid = pmksa->pmkid;
+	return 0;
+}
+
+
 void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
 			       struct rsn_pmksa_cache_entry *entry)
 {
@@ -6670,6 +6807,29 @@
 		goto fail;
 	}
 
+	if ((sm->ap_rsnxe && !elems.rsnxe) ||
+	    (!sm->ap_rsnxe && elems.rsnxe) ||
+	    (sm->ap_rsnxe && elems.rsnxe && sm->ap_rsnxe_len >= 2 &&
+	     (sm->ap_rsnxe_len != 2U + elems.rsnxe_len ||
+	      os_memcmp(sm->ap_rsnxe + 2, elems.rsnxe, sm->ap_rsnxe_len - 2) !=
+	      0))) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"FILS: RSNXE mismatch between Beacon/Probe Response and (Re)Association Response");
+		wpa_hexdump(MSG_INFO, "FILS: RSNXE in Beacon/Probe Response",
+			    sm->ap_rsnxe, sm->ap_rsnxe_len);
+		wpa_hexdump(MSG_INFO, "RSNXE in (Re)Association Response",
+			    elems.rsnxe, elems.rsnxe_len);
+		/* As an interop workaround, allow this for now if we did not
+		 * include the RSNXE in (Re)Association Request frame since
+		 * IEEE Std 802.11-2020 does not say anything about verifying
+		 * the RSNXE in FILS cases and there have been hostapd releases
+		 * that might omit the RSNXE in cases where the STA did not
+		 * include it in the Association Request frame. This workaround
+		 * might eventually be removed. */
+		if (sm->assoc_rsnxe && sm->assoc_rsnxe_len)
+			goto fail;
+	}
+
 	/* TODO: FILS Public Key */
 
 	if (!elems.fils_key_confirm) {
@@ -7100,6 +7260,12 @@
 }
 
 
+bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm)
+{
+	return sm ? sm->spp_amsdu : false;
+}
+
+
 struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm)
 {
 	return sm ? sm->pmksa : NULL;
@@ -7120,3 +7286,41 @@
 	if (sm)
 		sm->driver_bss_selection = driver_bss_selection;
 }
+
+
+struct wpabuf * wpa_sm_known_sta_identification(struct wpa_sm *sm, const u8 *aa,
+						u64 timestamp)
+{
+	struct wpabuf *ie;
+	unsigned int mic_len;
+	const u8 *start;
+	u8 *mic;
+
+	if (!sm || sm->last_kck_len == 0)
+		return NULL;
+
+	if (!ether_addr_equal(aa, sm->last_kck_aa))
+		return NULL;
+
+	mic_len = wpa_mic_len(sm->last_kck_key_mgmt, sm->last_kck_pmk_len);
+
+	ie = wpabuf_alloc(3 + 8 + 1 + mic_len);
+	if (!ie)
+		return NULL;
+
+	wpabuf_put_u8(ie, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(ie, 1 + 8 + 1 + mic_len);
+	wpabuf_put_u8(ie, WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION);
+	start = wpabuf_put(ie, 0);
+	wpabuf_put_le64(ie, timestamp);
+	wpabuf_put_u8(ie, mic_len);
+	mic = wpabuf_put(ie, mic_len);
+	if (wpa_eapol_key_mic(sm->last_kck, sm->last_kck_len,
+			      sm->last_kck_key_mgmt, sm->last_kck_eapol_key_ver,
+			      start, 8, mic) < 0) {
+		wpabuf_free(ie);
+		return NULL;
+	}
+
+	return ie;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index ca64d8f..55a22e5 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -139,6 +139,8 @@
 	WPA_PARAM_SSID_PROTECTION,
 	WPA_PARAM_RSN_OVERRIDE,
 	WPA_PARAM_RSN_OVERRIDE_SUPPORT,
+	WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK,
+	WPA_PARAM_SPP_AMSDU,
 };
 
 enum wpa_rsn_override {
@@ -255,6 +257,8 @@
 						      const u8 *pmkid,
 						      const void *network_ctx,
 						      int akmp);
+int wpa_sm_pmksa_get_pmk(struct wpa_sm *sm, const u8 *aa, const u8 **pmk,
+			 size_t *pmk_len, const u8 **pmkid);
 void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
 			       struct rsn_pmksa_cache_entry *entry);
 bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md);
@@ -279,6 +283,9 @@
 int wpa_fils_is_completed(struct wpa_sm *sm);
 void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm);
 int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo);
+void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
+				     bool driver_bss_selection);
+bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm);
 
 #else /* CONFIG_NO_WPA */
 
@@ -322,6 +329,11 @@
 {
 }
 
+static inline void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid,
+				   size_t ssid_len)
+{
+}
+
 static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
 {
 }
@@ -471,7 +483,7 @@
 	return NULL;
 }
 
-static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
+static inline int wpa_sm_has_ptk_installed(struct wpa_sm *sm)
 {
 	return 0;
 }
@@ -517,6 +529,16 @@
 	return 0;
 }
 
+static inline void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
+						   bool driver_bss_selection)
+{
+}
+
+static inline bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm)
+{
+	return false;
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_IEEE80211R
@@ -645,6 +667,8 @@
 void wpa_sm_set_test_eapol_m2_elems(struct wpa_sm *sm, struct wpabuf *buf);
 void wpa_sm_set_test_eapol_m4_elems(struct wpa_sm *sm, struct wpabuf *buf);
 const u8 * wpa_sm_get_anonce(struct wpa_sm *sm);
+int wpa_sm_set_test_rsnxe_data(struct wpa_sm *sm, struct wpabuf *data,
+			       struct wpabuf *mask);
 unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm);
 
 struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md);
@@ -670,7 +694,7 @@
 void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
 			  struct rsn_pmksa_cache_entry *entry);
 const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm);
-void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
-				     bool driver_bss_selection);
+struct wpabuf * wpa_sm_known_sta_identification(struct wpa_sm *sm, const u8 *aa,
+						u64 timestamp);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 9a39749..544e349 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -215,7 +215,7 @@
 	struct rsn_mdie *mdie;
 	struct rsn_ie_hdr *rsnie;
 	int mdie_len;
-	u8 rsnxe[10];
+	u8 rsnxe[257];
 	size_t rsnxe_len;
 	int rsnxe_used;
 	int res;
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index ef26b24..9315268 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -69,7 +69,7 @@
 	u8 ssid[32];
 	size_t ssid_len;
 	int wpa_ptk_rekey;
-	int wpa_deny_ptk0_rekey:1;
+	unsigned int wpa_deny_ptk0_rekey:1;
 	int p2p;
 	int wpa_rsc_relaxation;
 	int owe_ptk_workaround;
@@ -113,6 +113,7 @@
 	unsigned int secure_rtt:1;
 	unsigned int prot_range_neg:1;
 	unsigned int ssid_protection:1;
+	unsigned int spp_amsdu:1;
 
 	u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
 	size_t assoc_wpa_ie_len;
@@ -188,12 +189,15 @@
 	struct wpabuf *test_assoc_ie;
 	struct wpabuf *test_eapol_m2_elems;
 	struct wpabuf *test_eapol_m4_elems;
+	struct wpabuf *test_rsnxe_data;
+	struct wpabuf *test_rsnxe_mask;
 	int ft_rsnxe_used;
 	unsigned int oci_freq_override_eapol;
 	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;
+	unsigned int eapol_2_key_info_set_mask;
 	bool encrypt_eapol_m2;
 	bool encrypt_eapol_m4;
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -235,6 +239,14 @@
 
 	bool rsn_override_support;
 	enum wpa_rsn_override rsn_override;
+
+	u8 last_kck[WPA_KCK_MAX_LEN];
+	size_t last_kck_len;
+	size_t last_kck_pmk_len;
+	unsigned int last_kck_key_mgmt;
+	int last_kck_eapol_key_ver;
+	u8 last_kck_aa[ETH_ALEN];
+	int last_eapol_key_ver;
 };
 
 
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 515f1b0..d27bcf9 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -31,9 +31,6 @@
 	if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
 		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
 	if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
-	    wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
-		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
-	if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
 	    wpa_ie[1] >= 4 &&
 	    WPA_GET_BE32(&wpa_ie[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE)
 		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
@@ -233,10 +230,6 @@
 	} else if (key_mgmt & WPA_KEY_MGMT_DPP) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-	} else if (key_mgmt & WPA_KEY_MGMT_OSEN) {
-		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_SHA384
 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA384) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384);
@@ -282,64 +275,6 @@
 }
 
 
-#ifdef CONFIG_HS20
-static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
-			       int pairwise_cipher, int group_cipher,
-			       int key_mgmt)
-{
-	u8 *pos, *len;
-	u32 suite;
-
-	if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
-	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
-		return -1;
-
-	pos = wpa_ie;
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	len = pos++; /* to be filled */
-	WPA_PUT_BE24(pos, OUI_WFA);
-	pos += 3;
-	*pos++ = HS20_OSEN_OUI_TYPE;
-
-	/* Group Data Cipher Suite */
-	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
-	if (suite == 0) {
-		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
-			   group_cipher);
-		return -1;
-	}
-	RSN_SELECTOR_PUT(pos, suite);
-	pos += RSN_SELECTOR_LEN;
-
-	/* Pairwise Cipher Suite Count and List */
-	WPA_PUT_LE16(pos, 1);
-	pos += 2;
-	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
-	if (suite == 0 ||
-	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
-	     pairwise_cipher != WPA_CIPHER_NONE)) {
-		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
-			   pairwise_cipher);
-		return -1;
-	}
-	RSN_SELECTOR_PUT(pos, suite);
-	pos += RSN_SELECTOR_LEN;
-
-	/* AKM Suite Count and List */
-	WPA_PUT_LE16(pos, 1);
-	pos += 2;
-	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
-	pos += RSN_SELECTOR_LEN;
-
-	*len = pos - len - 1;
-
-	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
-
-	return pos - wpa_ie;
-}
-#endif /* CONFIG_HS20 */
-
-
 /**
  * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -355,13 +290,6 @@
 					  sm->group_cipher,
 					  sm->key_mgmt, sm->mgmt_group_cipher,
 					  sm);
-#ifdef CONFIG_HS20
-	else if (sm->proto == WPA_PROTO_OSEN)
-		return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
-					   sm->pairwise_cipher,
-					   sm->group_cipher,
-					   sm->key_mgmt);
-#endif /* CONFIG_HS20 */
 	else
 		return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
 					  sm->pairwise_cipher,
@@ -394,6 +322,8 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 	if (sm->ssid_protection)
 		capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+	if (sm->spp_amsdu)
+		capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
 
 	if (!capab)
 		return 0; /* no supported extended RSN capabilities */
diff --git a/src/utils/common.h b/src/utils/common.h
index aed93fb..3deb204 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -283,6 +283,23 @@
 	a[0] = val & 0xff;
 }
 
+static inline u64 WPA_GET_LE48(const u8 *a)
+{
+	return (((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+		(((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+		(((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void WPA_PUT_LE48(u8 *a, u64 val)
+{
+	a[5] = val >> 40;
+	a[4] = val >> 32;
+	a[3] = val >> 24;
+	a[2] = val >> 16;
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
+
 static inline u64 WPA_GET_BE64(const u8 *a)
 {
 	return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
diff --git a/src/utils/ext_password_file.c b/src/utils/ext_password_file.c
index 4bb0095..158500c 100644
--- a/src/utils/ext_password_file.c
+++ b/src/utils/ext_password_file.c
@@ -9,7 +9,6 @@
 #include "includes.h"
 
 #include "utils/common.h"
-#include "utils/config.h"
 #include "ext_password_i.h"
 
 
@@ -84,6 +83,7 @@
 	struct ext_password_file_data *data = ctx;
 	struct wpabuf *password = NULL;
 	char buf[512], *pos;
+	size_t name_len;
 	int line = 0;
 	FILE *f;
 
@@ -95,11 +95,23 @@
 		return NULL;
 	}
 
+	name_len = os_strlen(name);
+
 	wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name);
 
-	while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
-		char *sep = os_strchr(pos, '=');
+	while ((pos = fgets(buf, sizeof(buf), f))) {
+		char *sep;
 
+		line++;
+
+		/* Strip newline characters */
+		pos[strcspn(pos, "\r\n")] = 0;
+
+		/* Skip comments and empty lines */
+		if (*pos == '#' || *pos == '\0')
+			continue;
+
+		sep = os_strchr(pos, '=');
 		if (!sep) {
 			wpa_printf(MSG_ERROR, "Invalid password line %d.",
 				   line);
@@ -112,7 +124,8 @@
 
 		}
 
-		if (os_strncmp(name, pos, sep - pos) != 0)
+		if (name_len != (size_t) (sep - pos) ||
+		    os_strncmp(name, pos, sep - pos) != 0)
 			continue;
 
 		password = wpabuf_alloc_copy(sep + 1, os_strlen(sep + 1));
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
index 23e9ecd..14efaf8 100644
--- a/src/utils/http-utils.h
+++ b/src/utils/http-utils.h
@@ -11,38 +11,6 @@
 
 struct http_ctx;
 
-struct http_othername {
-	char *oid;
-	u8 *data;
-	size_t len;
-};
-
-#define HTTP_MAX_CERT_LOGO_HASH 32
-
-struct http_logo {
-	char *alg_oid;
-	u8 *hash;
-	size_t hash_len;
-	char *uri;
-};
-
-struct http_cert {
-	char **dnsname;
-	size_t num_dnsname;
-	struct http_othername *othername;
-	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,
-		     const char *ca_fname, const char *username,
-		     const char *password, const char *client_cert,
-		     const char *client_key);
-int soap_reinit_client(struct http_ctx *ctx);
-xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
-
 struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
 void http_ocsp_set(struct http_ctx *ctx, int val);
 void http_deinit_ctx(struct http_ctx *ctx);
@@ -55,10 +23,6 @@
 		 const char *username, const char *password,
 		 const char *client_cert, const char *client_key,
 		 size_t *resp_len);
-void http_set_cert_cb(struct http_ctx *ctx,
-		      int (*cb)(void *ctx, struct http_cert *cert),
-		      void *cb_ctx);
 const char * http_get_err(struct http_ctx *ctx);
-void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname);
 
 #endif /* HTTP_UTILS_H */
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index 77d5b35..1cf2f7e 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -31,31 +31,15 @@
 #endif /* EAP_TLS_OPENSSL */
 
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
-{
-	return ASN1_STRING_data((ASN1_STRING *) x);
-}
-#endif /* OpenSSL < 1.1.0 */
-
-
 struct http_ctx {
 	void *ctx;
 	struct xml_node_ctx *xml;
 	CURL *curl;
 	struct curl_slist *curl_hdr;
 	char *svc_address;
-	char *svc_ca_fname;
-	char *svc_username;
-	char *svc_password;
-	char *svc_client_cert;
-	char *svc_client_key;
 	char *curl_buf;
 	size_t curl_buf_len;
 
-	int (*cert_cb)(void *ctx, struct http_cert *cert);
-	void *cert_cb_ctx;
-
 	enum {
 		NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP
 	} ocsp;
@@ -81,16 +65,6 @@
 }
 
 
-static void clone_str(char **dst, const char *src)
-{
-	os_free(*dst);
-	if (src)
-		*dst = os_strdup(src);
-	else
-		*dst = NULL;
-}
-
-
 static void debug_dump(struct http_ctx *ctx, const char *title,
 		       const char *buf, size_t len)
 {
@@ -202,773 +176,6 @@
 }
 
 
-static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert,
-				   OTHERNAME *o)
-{
-	char txt[100];
-	int res;
-	struct http_othername *on;
-	ASN1_TYPE *val;
-
-	on = os_realloc_array(cert->othername, cert->num_othername + 1,
-			      sizeof(struct http_othername));
-	if (on == NULL)
-		return;
-	cert->othername = on;
-	on = &on[cert->num_othername];
-	os_memset(on, 0, sizeof(*on));
-
-	res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1);
-	if (res < 0 || res >= (int) sizeof(txt))
-		return;
-
-	on->oid = os_strdup(txt);
-	if (on->oid == NULL)
-		return;
-
-	val = o->value;
-	on->data = val->value.octet_string->data;
-	on->len = val->value.octet_string->length;
-
-	cert->num_othername++;
-}
-
-
-static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert,
-			     ASN1_STRING *name)
-{
-	char *buf;
-	char **n;
-
-	buf = NULL;
-	if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0)
-		return;
-
-	n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1,
-			     sizeof(char *));
-	if (n == NULL)
-		return;
-
-	cert->dnsname = n;
-	n[cert->num_dnsname] = buf;
-	cert->num_dnsname++;
-}
-
-
-static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert,
-			 const GENERAL_NAME *name)
-{
-	switch (name->type) {
-	case GEN_OTHERNAME:
-		add_alt_name_othername(ctx, cert, name->d.otherName);
-		break;
-	case GEN_DNS:
-		add_alt_name_dns(ctx, cert, name->d.dNSName);
-		break;
-	}
-}
-
-
-static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert,
-			  GENERAL_NAMES *names)
-{
-	int num, i;
-
-	num = sk_GENERAL_NAME_num(names);
-	for (i = 0; i < num; i++) {
-		const GENERAL_NAME *name;
-		name = sk_GENERAL_NAME_value(names, i);
-		add_alt_name(ctx, cert, name);
-	}
-}
-
-
-/* RFC 3709 */
-
-typedef struct {
-	X509_ALGOR *hashAlg;
-	ASN1_OCTET_STRING *hashValue;
-} HashAlgAndValue;
-
-typedef struct {
-	STACK_OF(HashAlgAndValue) *refStructHash;
-	STACK_OF(ASN1_IA5STRING) *refStructURI;
-} LogotypeReference;
-
-typedef struct {
-	ASN1_IA5STRING *mediaType;
-	STACK_OF(HashAlgAndValue) *logotypeHash;
-	STACK_OF(ASN1_IA5STRING) *logotypeURI;
-} LogotypeDetails;
-
-typedef struct {
-	int type;
-	union {
-		ASN1_INTEGER *numBits;
-		ASN1_INTEGER *tableSize;
-	} d;
-} LogotypeImageResolution;
-
-typedef struct {
-	ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */
-	ASN1_INTEGER *fileSize;
-	ASN1_INTEGER *xSize;
-	ASN1_INTEGER *ySize;
-	LogotypeImageResolution *resolution;
-	ASN1_IA5STRING *language;
-} LogotypeImageInfo;
-
-typedef struct {
-	LogotypeDetails *imageDetails;
-	LogotypeImageInfo *imageInfo;
-} LogotypeImage;
-
-typedef struct {
-	ASN1_INTEGER *fileSize;
-	ASN1_INTEGER *playTime;
-	ASN1_INTEGER *channels;
-	ASN1_INTEGER *sampleRate;
-	ASN1_IA5STRING *language;
-} LogotypeAudioInfo;
-
-typedef struct {
-	LogotypeDetails *audioDetails;
-	LogotypeAudioInfo *audioInfo;
-} LogotypeAudio;
-
-typedef struct {
-	STACK_OF(LogotypeImage) *image;
-	STACK_OF(LogotypeAudio) *audio;
-} LogotypeData;
-
-typedef struct {
-	int type;
-	union {
-		LogotypeData *direct;
-		LogotypeReference *indirect;
-	} d;
-} LogotypeInfo;
-
-typedef struct {
-	ASN1_OBJECT *logotypeType;
-	LogotypeInfo *info;
-} OtherLogotypeInfo;
-
-typedef struct {
-	STACK_OF(LogotypeInfo) *communityLogos;
-	LogotypeInfo *issuerLogo;
-	LogotypeInfo *subjectLogo;
-	STACK_OF(OtherLogotypeInfo) *otherLogos;
-} LogotypeExtn;
-
-ASN1_SEQUENCE(HashAlgAndValue) = {
-	ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR),
-	ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING)
-} ASN1_SEQUENCE_END(HashAlgAndValue);
-
-ASN1_SEQUENCE(LogotypeReference) = {
-	ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue),
-	ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING)
-} ASN1_SEQUENCE_END(LogotypeReference);
-
-ASN1_SEQUENCE(LogotypeDetails) = {
-	ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING),
-	ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue),
-	ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING)
-} ASN1_SEQUENCE_END(LogotypeDetails);
-
-ASN1_CHOICE(LogotypeImageResolution) = {
-	ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1),
-	ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2)
-} ASN1_CHOICE_END(LogotypeImageResolution);
-
-ASN1_SEQUENCE(LogotypeImageInfo) = {
-	ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0),
-	ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER),
-	ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER),
-	ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER),
-	ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution),
-	ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4),
-} ASN1_SEQUENCE_END(LogotypeImageInfo);
-
-ASN1_SEQUENCE(LogotypeImage) = {
-	ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails),
-	ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo)
-} ASN1_SEQUENCE_END(LogotypeImage);
-
-ASN1_SEQUENCE(LogotypeAudioInfo) = {
-	ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER),
-	ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER),
-	ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER),
-	ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3),
-	ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4)
-} ASN1_SEQUENCE_END(LogotypeAudioInfo);
-
-ASN1_SEQUENCE(LogotypeAudio) = {
-	ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails),
-	ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo)
-} ASN1_SEQUENCE_END(LogotypeAudio);
-
-ASN1_SEQUENCE(LogotypeData) = {
-	ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage),
-	ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1)
-} ASN1_SEQUENCE_END(LogotypeData);
-
-ASN1_CHOICE(LogotypeInfo) = {
-	ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0),
-	ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1)
-} ASN1_CHOICE_END(LogotypeInfo);
-
-ASN1_SEQUENCE(OtherLogotypeInfo) = {
-	ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT),
-	ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo)
-} ASN1_SEQUENCE_END(OtherLogotypeInfo);
-
-ASN1_SEQUENCE(LogotypeExtn) = {
-	ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0),
-	ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1),
-	ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2),
-	ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3)
-} ASN1_SEQUENCE_END(LogotypeExtn);
-
-IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
-#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
-#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
-#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i))
-#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st))
-#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i))
-#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st))
-#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
-#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
-#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
-#else
-DEFINE_STACK_OF(LogotypeInfo)
-DEFINE_STACK_OF(LogotypeImage)
-DEFINE_STACK_OF(LogotypeAudio)
-DEFINE_STACK_OF(HashAlgAndValue)
-DEFINE_STACK_OF(ASN1_IA5STRING)
-#endif
-
-
-static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
-		     HashAlgAndValue *hash, ASN1_IA5STRING *uri)
-{
-	char txt[100];
-	int res, len;
-	struct http_logo *n;
-
-	if (hash == NULL || uri == NULL)
-		return;
-
-	res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1);
-	if (res < 0 || res >= (int) sizeof(txt))
-		return;
-
-	n = os_realloc_array(hcert->logo, hcert->num_logo + 1,
-			     sizeof(struct http_logo));
-	if (n == NULL)
-		return;
-	hcert->logo = n;
-	n = &hcert->logo[hcert->num_logo];
-	os_memset(n, 0, sizeof(*n));
-
-	n->alg_oid = os_strdup(txt);
-	if (n->alg_oid == NULL)
-		return;
-
-	n->hash_len = ASN1_STRING_length(hash->hashValue);
-	n->hash = os_memdup(ASN1_STRING_get0_data(hash->hashValue),
-			    n->hash_len);
-	if (n->hash == NULL) {
-		os_free(n->alg_oid);
-		return;
-	}
-
-	len = ASN1_STRING_length(uri);
-	n->uri = os_malloc(len + 1);
-	if (n->uri == NULL) {
-		os_free(n->alg_oid);
-		os_free(n->hash);
-		return;
-	}
-	os_memcpy(n->uri, ASN1_STRING_get0_data(uri), len);
-	n->uri[len] = '\0';
-
-	hcert->num_logo++;
-}
-
-
-static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert,
-			    LogotypeData *data)
-{
-	int i, num;
-
-	if (data->image == NULL)
-		return;
-
-	num = sk_LogotypeImage_num(data->image);
-	for (i = 0; i < num; i++) {
-		LogotypeImage *image;
-		LogotypeDetails *details;
-		int j, hash_num, uri_num;
-		HashAlgAndValue *found_hash = NULL;
-
-		image = sk_LogotypeImage_value(data->image, i);
-		if (image == NULL)
-			continue;
-
-		details = image->imageDetails;
-		if (details == NULL)
-			continue;
-
-		hash_num = sk_HashAlgAndValue_num(details->logotypeHash);
-		for (j = 0; j < hash_num; j++) {
-			HashAlgAndValue *hash;
-			char txt[100];
-			int res;
-			hash = sk_HashAlgAndValue_value(details->logotypeHash,
-							j);
-			if (hash == NULL)
-				continue;
-			res = OBJ_obj2txt(txt, sizeof(txt),
-					  hash->hashAlg->algorithm, 1);
-			if (res < 0 || res >= (int) sizeof(txt))
-				continue;
-			if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) {
-				found_hash = hash;
-				break;
-			}
-		}
-
-		if (!found_hash) {
-			wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo");
-			continue;
-		}
-
-		uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI);
-		for (j = 0; j < uri_num; j++) {
-			ASN1_IA5STRING *uri;
-			uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j);
-			add_logo(ctx, hcert, found_hash, uri);
-		}
-	}
-}
-
-
-static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert,
-			      LogotypeReference *ref)
-{
-	int j, hash_num, uri_num;
-
-	hash_num = sk_HashAlgAndValue_num(ref->refStructHash);
-	uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI);
-	if (hash_num != uri_num) {
-		wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d",
-			   hash_num, uri_num);
-		return;
-	}
-
-	for (j = 0; j < hash_num; j++) {
-		HashAlgAndValue *hash;
-		ASN1_IA5STRING *uri;
-		hash = sk_HashAlgAndValue_value(ref->refStructHash, j);
-		uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j);
-		add_logo(ctx, hcert, hash, uri);
-	}
-}
-
-
-static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent)
-{
-	int i;
-	const unsigned char *data;
-
-	BIO_printf(out, "%*shashAlg: ", indent, "");
-	i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm);
-	BIO_printf(out, "\n");
-
-	BIO_printf(out, "%*shashValue: ", indent, "");
-	data = hash->hashValue->data;
-	for (i = 0; i < hash->hashValue->length; i++)
-		BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]);
-	BIO_printf(out, "\n");
-}
-
-static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent)
-{
-	int i, num;
-
-	BIO_printf(out, "%*sLogotypeDetails\n", indent, "");
-	if (details->mediaType) {
-		BIO_printf(out, "%*smediaType: ", indent, "");
-		ASN1_STRING_print(out, details->mediaType);
-		BIO_printf(out, "\n");
-	}
-
-	num = details->logotypeHash ?
-		sk_HashAlgAndValue_num(details->logotypeHash) : 0;
-	for (i = 0; i < num; i++) {
-		HashAlgAndValue *hash;
-		hash = sk_HashAlgAndValue_value(details->logotypeHash, i);
-		i2r_HashAlgAndValue(hash, out, indent);
-	}
-
-	num = details->logotypeURI ?
-		sk_ASN1_IA5STRING_num(details->logotypeURI) : 0;
-	for (i = 0; i < num; i++) {
-		ASN1_IA5STRING *uri;
-		uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i);
-		BIO_printf(out, "%*slogotypeURI: ", indent, "");
-		ASN1_STRING_print(out, uri);
-		BIO_printf(out, "\n");
-	}
-}
-
-static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent)
-{
-	long val;
-
-	BIO_printf(out, "%*sLogotypeImageInfo\n", indent, "");
-	if (info->type) {
-		val = ASN1_INTEGER_get(info->type);
-		BIO_printf(out, "%*stype: %ld\n", indent, "", val);
-	} else {
-		BIO_printf(out, "%*stype: default (1)\n", indent, "");
-	}
-	val = ASN1_INTEGER_get(info->fileSize);
-	BIO_printf(out, "%*sfileSize: %ld\n", indent, "", val);
-	val = ASN1_INTEGER_get(info->xSize);
-	BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
-	val = ASN1_INTEGER_get(info->ySize);
-	BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
-	if (info->resolution) {
-		BIO_printf(out, "%*sresolution [%d]\n", indent, "",
-			   info->resolution->type);
-		switch (info->resolution->type) {
-		case 0:
-			val = ASN1_INTEGER_get(info->resolution->d.numBits);
-			BIO_printf(out, "%*snumBits: %ld\n", indent, "", val);
-			break;
-		case 1:
-			val = ASN1_INTEGER_get(info->resolution->d.tableSize);
-			BIO_printf(out, "%*stableSize: %ld\n", indent, "", val);
-			break;
-		}
-	}
-	if (info->language) {
-		BIO_printf(out, "%*slanguage: ", indent, "");
-		ASN1_STRING_print(out, info->language);
-		BIO_printf(out, "\n");
-	}
-}
-
-static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent)
-{
-	BIO_printf(out, "%*sLogotypeImage\n", indent, "");
-	if (image->imageDetails) {
-		i2r_LogotypeDetails(image->imageDetails, out, indent + 4);
-	}
-	if (image->imageInfo) {
-		i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4);
-	}
-}
-
-static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out,
-			     int indent)
-{
-	int i, num;
-
-	BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title);
-
-	num = data->image ? sk_LogotypeImage_num(data->image) : 0;
-	for (i = 0; i < num; i++) {
-		LogotypeImage *image = sk_LogotypeImage_value(data->image, i);
-		i2r_LogotypeImage(image, out, indent + 4);
-	}
-
-	num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0;
-	for (i = 0; i < num; i++) {
-		BIO_printf(out, "%*saudio: TODO\n", indent, "");
-	}
-}
-
-static void i2r_LogotypeReference(LogotypeReference *ref, const char *title,
-				  BIO *out, int indent)
-{
-	int i, hash_num, uri_num;
-
-	BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title);
-
-	hash_num = ref->refStructHash ?
-		sk_HashAlgAndValue_num(ref->refStructHash) : 0;
-	uri_num = ref->refStructURI ?
-		sk_ASN1_IA5STRING_num(ref->refStructURI) : 0;
-	if (hash_num != uri_num) {
-		BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n",
-			   indent, "", hash_num, uri_num);
-		return;
-	}
-
-	for (i = 0; i < hash_num; i++) {
-		HashAlgAndValue *hash;
-		ASN1_IA5STRING *uri;
-
-		hash = sk_HashAlgAndValue_value(ref->refStructHash, i);
-		i2r_HashAlgAndValue(hash, out, indent);
-
-		uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i);
-		BIO_printf(out, "%*srefStructURI: ", indent, "");
-		ASN1_STRING_print(out, uri);
-		BIO_printf(out, "\n");
-	}
-}
-
-static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out,
-			     int indent)
-{
-	switch (info->type) {
-	case 0:
-		i2r_LogotypeData(info->d.direct, title, out, indent);
-		break;
-	case 1:
-		i2r_LogotypeReference(info->d.indirect, title, out, indent);
-		break;
-	}
-}
-
-static void debug_print_logotypeext(LogotypeExtn *logo)
-{
-	BIO *out;
-	int i, num;
-	int indent = 0;
-
-	out = BIO_new_fp(stdout, BIO_NOCLOSE);
-	if (out == NULL)
-		return;
-
-	if (logo->communityLogos) {
-		num = sk_LogotypeInfo_num(logo->communityLogos);
-		for (i = 0; i < num; i++) {
-			LogotypeInfo *info;
-			info = sk_LogotypeInfo_value(logo->communityLogos, i);
-			i2r_LogotypeInfo(info, "communityLogo", out, indent);
-		}
-	}
-
-	if (logo->issuerLogo) {
-		i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent );
-	}
-
-	if (logo->subjectLogo) {
-		i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent);
-	}
-
-	if (logo->otherLogos) {
-		BIO_printf(out, "%*sotherLogos - TODO\n", indent, "");
-	}
-
-	BIO_free(out);
-}
-
-
-static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
-			     X509 *cert)
-{
-	ASN1_OBJECT *obj;
-	int pos;
-	X509_EXTENSION *ext;
-	ASN1_OCTET_STRING *os;
-	LogotypeExtn *logo;
-	const unsigned char *data;
-	int i, num;
-
-	obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0);
-	if (obj == NULL)
-		return;
-
-	pos = X509_get_ext_by_OBJ(cert, obj, -1);
-	if (pos < 0) {
-		wpa_printf(MSG_INFO, "No logotype extension included");
-		return;
-	}
-
-	wpa_printf(MSG_INFO, "Parsing logotype extension");
-	ext = X509_get_ext(cert, pos);
-	if (!ext) {
-		wpa_printf(MSG_INFO, "Could not get logotype extension");
-		return;
-	}
-
-	os = X509_EXTENSION_get_data(ext);
-	if (os == NULL) {
-		wpa_printf(MSG_INFO, "Could not get logotype extension data");
-		return;
-	}
-
-	wpa_hexdump(MSG_DEBUG, "logotypeExtn",
-		    ASN1_STRING_get0_data(os), ASN1_STRING_length(os));
-
-	data = ASN1_STRING_get0_data(os);
-	logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os));
-	if (logo == NULL) {
-		wpa_printf(MSG_INFO, "Failed to parse logotypeExtn");
-		return;
-	}
-
-	if (wpa_debug_level < MSG_INFO)
-		debug_print_logotypeext(logo);
-
-	if (!logo->communityLogos) {
-		wpa_printf(MSG_INFO, "No communityLogos included");
-		LogotypeExtn_free(logo);
-		return;
-	}
-
-	num = sk_LogotypeInfo_num(logo->communityLogos);
-	for (i = 0; i < num; i++) {
-		LogotypeInfo *info;
-		info = sk_LogotypeInfo_value(logo->communityLogos, i);
-		switch (info->type) {
-		case 0:
-			add_logo_direct(ctx, hcert, info->d.direct);
-			break;
-		case 1:
-			add_logo_indirect(ctx, hcert, info->d.indirect);
-			break;
-		}
-	}
-
-	LogotypeExtn_free(logo);
-}
-
-
-static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert,
-		       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)
-		add_alt_names(ctx, hcert, *names);
-
-	add_logotype_ext(ctx, hcert, cert);
-}
-
-
-static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names)
-{
-	unsigned int i;
-
-	for (i = 0; i < hcert->num_dnsname; i++)
-		OPENSSL_free(hcert->dnsname[i]);
-	os_free(hcert->dnsname);
-
-	for (i = 0; i < hcert->num_othername; i++)
-		os_free(hcert->othername[i].oid);
-	os_free(hcert->othername);
-
-	for (i = 0; i < hcert->num_logo; i++) {
-		os_free(hcert->logo[i].alg_oid);
-		os_free(hcert->logo[i].hash);
-		os_free(hcert->logo[i].uri);
-	}
-	os_free(hcert->logo);
-
-	sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
-}
-
-
-static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
-{
-	GENERAL_NAMES *names;
-	struct http_cert hcert;
-	int ret;
-
-	if (ctx->cert_cb == NULL) {
-		wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__);
-		return 0;
-	}
-
-	if (0) {
-		BIO *out;
-		out = BIO_new_fp(stdout, BIO_NOCLOSE);
-		X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
-		BIO_free(out);
-	}
-
-	parse_cert(ctx, &hcert, cert, &names);
-	ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
-	parse_cert_free(&hcert, names);
-
-	return ret;
-}
-
-
-void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
-{
-	BIO *in, *out;
-	X509 *cert;
-	GENERAL_NAMES *names;
-	struct http_cert hcert;
-	unsigned int i;
-
-	in = BIO_new_file(fname, "r");
-	if (in == NULL) {
-		wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
-		return;
-	}
-
-	cert = d2i_X509_bio(in, NULL);
-	BIO_free(in);
-
-	if (cert == NULL) {
-		wpa_printf(MSG_ERROR, "Could not parse certificate");
-		return;
-	}
-
-	out = BIO_new_fp(stdout, BIO_NOCLOSE);
-	if (out) {
-		X509_print_ex(out, cert, XN_FLAG_COMPAT,
-			      X509_FLAG_COMPAT);
-		BIO_free(out);
-	}
-
-	wpa_printf(MSG_INFO, "Additional parsing information:");
-	parse_cert(ctx, &hcert, cert, &names);
-	for (i = 0; i < hcert.num_othername; i++) {
-		if (os_strcmp(hcert.othername[i].oid,
-			      "1.3.6.1.4.1.40808.1.1.1") == 0) {
-			char *name = os_zalloc(hcert.othername[i].len + 1);
-			if (name) {
-				os_memcpy(name, hcert.othername[i].data,
-					  hcert.othername[i].len);
-				wpa_printf(MSG_INFO,
-					   "id-wfa-hotspot-friendlyName: %s",
-					   name);
-				os_free(name);
-			}
-			wpa_hexdump_ascii(MSG_INFO,
-					  "id-wfa-hotspot-friendlyName",
-					  hcert.othername[i].data,
-					  hcert.othername[i].len);
-		} else {
-			wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
-				   hcert.othername[i].oid);
-			wpa_hexdump_ascii(MSG_INFO, "unknown othername",
-					  hcert.othername[i].data,
-					  hcert.othername[i].len);
-		}
-	}
-	parse_cert_free(&hcert, names);
-
-	X509_free(cert);
-}
-
-
 static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
 {
 	struct http_ctx *ctx;
@@ -1011,9 +218,6 @@
 		   depth, err, err_str, buf);
 	debug_dump_cert("Server certificate chain - certificate", cert);
 
-	if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
-		return 0;
-
 #ifdef OPENSSL_IS_BORINGSSL
 	if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) {
 		enum ocsp_result res;
@@ -1387,91 +591,6 @@
 }
 
 
-static int post_init_client(struct http_ctx *ctx, const char *address,
-			    const char *ca_fname, const char *username,
-			    const char *password, const char *client_cert,
-			    const char *client_key)
-{
-	char *pos;
-	int count;
-
-	clone_str(&ctx->svc_address, address);
-	clone_str(&ctx->svc_ca_fname, ca_fname);
-	clone_str(&ctx->svc_username, username);
-	clone_str(&ctx->svc_password, password);
-	clone_str(&ctx->svc_client_cert, client_cert);
-	clone_str(&ctx->svc_client_key, client_key);
-
-	/*
-	 * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
-	 * 'foo' provided via HTTP are different.
-	 */
-	for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos;
-	     pos++) {
-		if (*pos == '/')
-			count++;
-		*pos = tolower(*pos);
-	}
-
-	ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
-				    password, client_cert, client_key);
-	if (ctx->curl == NULL)
-		return -1;
-
-	return 0;
-}
-
-
-int soap_init_client(struct http_ctx *ctx, const char *address,
-		     const char *ca_fname, const char *username,
-		     const char *password, const char *client_cert,
-		     const char *client_key)
-{
-	if (post_init_client(ctx, address, ca_fname, username, password,
-			     client_cert, client_key) < 0)
-		return -1;
-
-	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
-					  "Content-Type: application/soap+xml");
-	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: ");
-	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:");
-	curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr);
-
-	return 0;
-}
-
-
-int soap_reinit_client(struct http_ctx *ctx)
-{
-	char *address = NULL;
-	char *ca_fname = NULL;
-	char *username = NULL;
-	char *password = NULL;
-	char *client_cert = NULL;
-	char *client_key = NULL;
-	int ret;
-
-	clear_curl(ctx);
-
-	clone_str(&address, ctx->svc_address);
-	clone_str(&ca_fname, ctx->svc_ca_fname);
-	clone_str(&username, ctx->svc_username);
-	clone_str(&password, ctx->svc_password);
-	clone_str(&client_cert, ctx->svc_client_cert);
-	clone_str(&client_key, ctx->svc_client_key);
-
-	ret = soap_init_client(ctx, address, ca_fname, username, password,
-			       client_cert, client_key);
-	os_free(address);
-	os_free(ca_fname);
-	str_clear_free(username);
-	str_clear_free(password);
-	os_free(client_cert);
-	os_free(client_key);
-	return ret;
-}
-
-
 static void free_curl_buf(struct http_ctx *ctx)
 {
 	os_free(ctx->curl_buf);
@@ -1480,73 +599,6 @@
 }
 
 
-xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node)
-{
-	char *str;
-	xml_node_t *envelope, *ret, *resp, *n;
-	CURLcode res;
-	long http = 0;
-
-	ctx->last_err = NULL;
-
-	wpa_printf(MSG_DEBUG, "SOAP: Sending message");
-	envelope = soap_build_envelope(ctx->xml, node);
-	str = xml_node_to_str(ctx->xml, envelope);
-	xml_node_free(ctx->xml, envelope);
-	wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str);
-
-	curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str);
-	free_curl_buf(ctx);
-
-	res = curl_easy_perform(ctx->curl);
-	if (res != CURLE_OK) {
-		if (!ctx->last_err)
-			ctx->last_err = curl_easy_strerror(res);
-		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
-			   ctx->last_err);
-		os_free(str);
-		free_curl_buf(ctx);
-		return NULL;
-	}
-	os_free(str);
-
-	curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http);
-	wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http);
-	if (http != 200) {
-		ctx->last_err = "HTTP download failed";
-		wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
-		free_curl_buf(ctx);
-		return NULL;
-	}
-
-	if (ctx->curl_buf == NULL)
-		return NULL;
-
-	wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf);
-	resp = xml_node_from_buf(ctx->xml, ctx->curl_buf);
-	free_curl_buf(ctx);
-	if (resp == NULL) {
-		wpa_printf(MSG_INFO, "Could not parse SOAP response");
-		ctx->last_err = "Could not parse SOAP response";
-		return NULL;
-	}
-
-	ret = soap_get_body(ctx->xml, resp);
-	if (ret == NULL) {
-		wpa_printf(MSG_INFO, "Could not get SOAP body");
-		ctx->last_err = "Could not get SOAP body";
-		return NULL;
-	}
-
-	wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'",
-		   xml_node_get_localname(ctx->xml, ret));
-	n = xml_node_copy(ctx->xml, ret);
-	xml_node_free(ctx->xml, resp);
-
-	return n;
-}
-
-
 struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx)
 {
 	struct http_ctx *ctx;
@@ -1582,11 +634,6 @@
 	curl_global_cleanup();
 
 	os_free(ctx->svc_address);
-	os_free(ctx->svc_ca_fname);
-	str_clear_free(ctx->svc_username);
-	str_clear_free(ctx->svc_password);
-	os_free(ctx->svc_client_cert);
-	os_free(ctx->svc_client_key);
 
 	os_free(ctx);
 }
@@ -1726,15 +773,6 @@
 }
 
 
-void http_set_cert_cb(struct http_ctx *ctx,
-		      int (*cb)(void *ctx, struct http_cert *cert),
-		      void *cb_ctx)
-{
-	ctx->cert_cb = cb;
-	ctx->cert_cb_ctx = cb_ctx;
-}
-
-
 const char * http_get_err(struct http_ctx *ctx)
 {
 	return ctx->last_err;
diff --git a/src/utils/json.c b/src/utils/json.c
index dd12f1b..5523f28 100644
--- a/src/utils/json.c
+++ b/src/utils/json.c
@@ -269,7 +269,8 @@
 		case ']': /* end array */
 		case '}': /* end object */
 			if (!curr_token || !curr_token->parent ||
-			    curr_token->parent->state != JSON_STARTED) {
+			    curr_token->parent->state != JSON_STARTED ||
+			    depth == 0) {
 				wpa_printf(MSG_DEBUG,
 					   "JSON: Invalid state for end array/object");
 				goto fail;
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index e190645..45c4ea8 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -566,10 +566,10 @@
 #ifdef WPA_TRACE
 
 #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
-struct wpa_trace_test_fail {
+static struct wpa_trace_test_fail {
 	unsigned int fail_after;
 	char pattern[256];
-} wpa_trace_test_fail[5][2];
+} wpa_trace_test_fail[5][4];
 
 int testing_test_fail(const char *tag, bool is_alloc)
 {
diff --git a/src/utils/trace.c b/src/utils/trace.c
index 7c9a17f..1ec2265 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -197,6 +197,8 @@
 	if (abfd == NULL)
 		return;
 
+	if (start_offset > (uintptr_t) pc)
+		return;
 	data.pc = (uintptr_t) ((u8 *) pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
@@ -238,6 +240,8 @@
 	if (abfd == NULL)
 		return NULL;
 
+	if (start_offset > (uintptr_t) pc)
+		return NULL;
 	data.pc = (uintptr_t) ((u8 *) pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
@@ -310,6 +314,8 @@
 	for (i = 0; i < btrace_num; i++) {
 		struct bfd_data data;
 
+		if (start_offset > (uintptr_t) btrace_res[i])
+			continue;
 		data.pc = (uintptr_t) ((u8 *) btrace_res[i] - start_offset);
 		data.found = FALSE;
 		bfd_map_over_sections(abfd, find_addr_sect, &data);
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 291aa07..b3876c5 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -36,6 +36,7 @@
 #define wpa_debug_open_file(p) do { } while (0)
 #define wpa_debug_close_file() do { } while (0)
 #define wpa_debug_setup_stdout() do { } while (0)
+#define wpa_debug_stop_log() do { } while (0)
 #define wpa_dbg(args...) do { } while (0)
 
 static inline int wpa_debug_reopen_file(void)
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
index dae91fe..5280382 100644
--- a/src/utils/xml-utils.c
+++ b/src/utils/xml-utils.c
@@ -438,34 +438,3 @@
 		return NULL;
 	return tnds_to_mo_iter(ctx, NULL, node, NULL);
 }
-
-
-xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node)
-{
-	xml_node_t *envelope, *body;
-	xml_namespace_t *ns;
-
-	envelope = xml_node_create_root(
-		ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns,
-		"Envelope");
-	if (envelope == NULL)
-		return NULL;
-	body = xml_node_create(ctx, envelope, ns, "Body");
-	xml_node_add_child(ctx, body, node);
-	return envelope;
-}
-
-
-xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap)
-{
-	xml_node_t *body, *child;
-
-	body = get_node_uri(ctx, soap, "Envelope/Body");
-	if (body == NULL)
-		return NULL;
-	xml_node_for_each_child(ctx, child, body) {
-		xml_node_for_each_check(ctx, child);
-		return child;
-	}
-	return NULL;
-}
diff --git a/src/utils/xml-utils.h b/src/utils/xml-utils.h
index fb6208c..eb83bd4 100644
--- a/src/utils/xml-utils.h
+++ b/src/utils/xml-utils.h
@@ -15,19 +15,11 @@
 
 /* XML library wrappers */
 
-int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
-		 const char *xml_schema_fname, char **ret_err);
-int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
-		     const char *dtd_fname, char **ret_err);
 void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node);
-xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node);
 xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf);
 const char * xml_node_get_localname(struct xml_node_ctx *ctx,
 				    xml_node_t *node);
 char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node);
-void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node);
-void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
-			xml_node_t *child);
 xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
 				  const char *ns_prefix,
 				  xml_namespace_t **ret_ns, const char *name);
@@ -41,13 +33,6 @@
 				     const char *name, const char *value);
 void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
 		       const char *value);
-int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
-		      xml_namespace_t *ns, const char *name, const char *value);
-char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
-			       char *name);
-char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
-				  const char *ns_uri, char *name);
-void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val);
 xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
 				  xml_node_t *parent);
 xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
@@ -57,7 +42,6 @@
 void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val);
 char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
 				int *ret_len);
-xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node);
 
 #define xml_node_for_each_child(ctx, child, parent) \
 for (child = xml_node_first_child(ctx, parent); \
@@ -91,7 +75,4 @@
 			int use_path, const char *urn, const char *ns_uri);
 xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds);
 
-xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node);
-xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap);
-
 #endif /* XML_UTILS_H */
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
index 7b7aeb7..26ad748 100644
--- a/src/utils/xml_libxml2.c
+++ b/src/utils/xml_libxml2.c
@@ -21,161 +21,12 @@
 };
 
 
-struct str_buf {
-	char *buf;
-	size_t len;
-};
-
-#define MAX_STR 1000
-
-static void add_str(void *ctx_ptr, const char *fmt, ...)
-{
-	struct str_buf *str = ctx_ptr;
-	va_list ap;
-	char *n;
-	int len;
-
-	n = os_realloc(str->buf, str->len + MAX_STR + 2);
-	if (n == NULL)
-		return;
-	str->buf = n;
-
-	va_start(ap, fmt);
-	len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap);
-	va_end(ap);
-	if (len >= MAX_STR)
-		len = MAX_STR - 1;
-	str->len += len;
-	str->buf[str->len] = '\0';
-}
-
-
-int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
-		 const char *xml_schema_fname, char **ret_err)
-{
-	xmlDocPtr doc;
-	xmlNodePtr n;
-	xmlSchemaParserCtxtPtr pctx;
-	xmlSchemaValidCtxtPtr vctx;
-	xmlSchemaPtr schema;
-	int ret;
-	struct str_buf errors;
-
-	if (ret_err)
-		*ret_err = NULL;
-
-	doc = xmlNewDoc((xmlChar *) "1.0");
-	if (doc == NULL)
-		return -1;
-	n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
-	if (n == NULL) {
-		xmlFreeDoc(doc);
-		return -1;
-	}
-	xmlDocSetRootElement(doc, n);
-
-	os_memset(&errors, 0, sizeof(errors));
-
-	pctx = xmlSchemaNewParserCtxt(xml_schema_fname);
-	xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str,
-				 (xmlSchemaValidityWarningFunc) add_str,
-				 &errors);
-	schema = xmlSchemaParse(pctx);
-	xmlSchemaFreeParserCtxt(pctx);
-
-	vctx = xmlSchemaNewValidCtxt(schema);
-	xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str,
-				(xmlSchemaValidityWarningFunc) add_str,
-				&errors);
-
-	ret = xmlSchemaValidateDoc(vctx, doc);
-	xmlSchemaFreeValidCtxt(vctx);
-	xmlFreeDoc(doc);
-	xmlSchemaFree(schema);
-
-	if (ret == 0) {
-		os_free(errors.buf);
-		return 0;
-	} else if (ret > 0) {
-		if (ret_err)
-			*ret_err = errors.buf;
-		else
-			os_free(errors.buf);
-		return -1;
-	} else {
-		if (ret_err)
-			*ret_err = errors.buf;
-		else
-			os_free(errors.buf);
-		return -1;
-	}
-}
-
-
-int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
-		     const char *dtd_fname, char **ret_err)
-{
-	xmlDocPtr doc;
-	xmlNodePtr n;
-	xmlValidCtxt vctx;
-	xmlDtdPtr dtd;
-	int ret;
-	struct str_buf errors;
-
-	if (ret_err)
-		*ret_err = NULL;
-
-	doc = xmlNewDoc((xmlChar *) "1.0");
-	if (doc == NULL)
-		return -1;
-	n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
-	if (n == NULL) {
-		xmlFreeDoc(doc);
-		return -1;
-	}
-	xmlDocSetRootElement(doc, n);
-
-	os_memset(&errors, 0, sizeof(errors));
-
-	dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname);
-	if (dtd == NULL) {
-		xmlFreeDoc(doc);
-		return -1;
-	}
-
-	os_memset(&vctx, 0, sizeof(vctx));
-	vctx.userData = &errors;
-	vctx.error = add_str;
-	vctx.warning = add_str;
-	ret = xmlValidateDtd(&vctx, doc, dtd);
-	xmlFreeDoc(doc);
-	xmlFreeDtd(dtd);
-
-	if (ret == 1) {
-		os_free(errors.buf);
-		return 0;
-	} else {
-		if (ret_err)
-			*ret_err = errors.buf;
-		else
-			os_free(errors.buf);
-		return -1;
-	}
-}
-
-
 void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node)
 {
 	xmlFreeNode((xmlNodePtr) node);
 }
 
 
-xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node)
-{
-	return (xml_node_t *) ((xmlNodePtr) node)->parent;
-}
-
-
 xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf)
 {
 	xmlDocPtr doc;
@@ -242,19 +93,6 @@
 }
 
 
-void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node)
-{
-	xmlUnlinkNode((xmlNodePtr) node);
-}
-
-
-void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
-			xml_node_t *child)
-{
-	xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child);
-}
-
-
 xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
 				  const char *ns_prefix,
 				  xml_namespace_t **ret_ns, const char *name)
@@ -322,47 +160,6 @@
 }
 
 
-int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
-		      xml_namespace_t *ns, const char *name, const char *value)
-{
-	xmlAttrPtr attr;
-
-	if (ns) {
-		attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns,
-				    (const xmlChar *) name,
-				    (const xmlChar *) value);
-	} else {
-		attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name,
-				  (const xmlChar *) value);
-	}
-
-	return attr ? 0 : -1;
-}
-
-
-char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
-			       char *name)
-{
-	return (char *) xmlGetNoNsProp((xmlNodePtr) node,
-				       (const xmlChar *) name);
-}
-
-
-char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
-				  const char *ns_uri, char *name)
-{
-	return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name,
-				     (const xmlChar *) ns_uri);
-}
-
-
-void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val)
-{
-	if (val)
-		xmlFree((xmlChar *) val);
-}
-
-
 xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
 				  xml_node_t *parent)
 {
@@ -426,14 +223,6 @@
 }
 
 
-xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node)
-{
-	if (node == NULL)
-		return NULL;
-	return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1);
-}
-
-
 struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
 					const void *env)
 {
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index b6c9478..573eb59 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -51,7 +51,7 @@
 		s->dev_password_id = attr.dev_password_id ?
 			WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
 		s->config_methods = attr.sel_reg_config_methods ?
-			WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+			WPA_GET_BE16(attr.sel_reg_config_methods) : 0xffff;
 		if (attr.authorized_macs) {
 			int count = attr.authorized_macs_len / ETH_ALEN;
 			if (count > WPS_MAX_AUTHORIZED_MACS)
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index 5361289..cf902a1 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -12,6 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// The wpa_supplicant related modules are split into 2 sections:
+// 1. For cuttlefish series products, start from `wpa_supplicant_headers` to `libpasn`.
+// 2. For non-cuttlefish series products, ex: physical devices created by oems,
+//    the section starts from `wpa_supplicant_driver_srcs_default` to the end of this file.
+
 package {
     default_applicable_licenses: [
         // Inherits SPDX-license-identifier-BSD-3-Clause
@@ -36,6 +41,9 @@
     ],
 }
 
+// The section below is for cuttlefish series products. For non-cuttlefish
+// products please update the section starting at `wpa_supplicant_driver_srcs_default`.
+// Start of cuttlefish section
 cc_library_headers {
     name: "wpa_supplicant_headers",
     export_include_dirs: [
@@ -150,14 +158,17 @@
         "-DCONFIG_IPV6",
         "-DCONFIG_JSON",
         "-DCONFIG_MBO",
+        "-DCONFIG_NAN_USD",
         "-DCONFIG_NO_ACCOUNTING",
         "-DCONFIG_NO_RADIUS",
         "-DCONFIG_NO_RADIUS",
         "-DCONFIG_NO_RANDOM_POOL",
+        "-DCONFIG_NO_ROAMING",
         "-DCONFIG_NO_VLAN",
         "-DCONFIG_OFFCHANNEL",
         "-DCONFIG_OWE",
         "-DCONFIG_P2P",
+        "-DCONFIG_PASN",
         "-DCONFIG_SAE",
         "-DCONFIG_SAE_PK",
         "-DCONFIG_SHA256",
@@ -224,21 +235,6 @@
             any @ driver: ["-D" + driver],
             // Flag is optional, so no default value provided.
             default: [],
-        }) +
-        select(soong_config_variable("wpa_supplicant", "roaming"), {
-            true: [],
-            default: ["-DCONFIG_NO_ROAMING"],
-        }) +
-        select(soong_config_variable("wpa_supplicant", "pasn"), {
-            false: [],
-            default: ["-DCONFIG_PASN"],
-        }) +
-        select(soong_config_variable("wpa_supplicant", "bgscan_simple"), {
-            true: [
-                "-DCONFIG_BGSCAN",
-                "-DCONFIG_BGSCAN_SIMPLE",
-            ],
-            default: [],
         }),
     // Similar to suppressing clang compiler warnings, here we
     // suppress clang-tidy warnings to reduce noises in Android build.log.
@@ -290,11 +286,13 @@
         "interworking.c",
         "main.c",
         "mbo.c",
+        "nan_usd.c",
         "notify.c",
         "offchannel.c",
         "op_classes.c",
         "p2p_supplicant.c",
         "p2p_supplicant_sd.c",
+        "pasn_supplicant.c",
         "robust_av.c",
         "rrm.c",
         "scan.c",
@@ -326,6 +324,7 @@
         "src/ap/ieee802_11_vht.c",
         "src/ap/ieee802_1x.c",
         "src/ap/mbo_ap.c",
+        "src/ap/nan_usd_ap.c",
         "src/ap/neighbor_db.c",
         "src/ap/p2p_hostapd.c",
         "src/ap/pmksa_cache_auth.c",
@@ -351,6 +350,7 @@
         "src/common/gas_server.c",
         "src/common/hw_features_common.c",
         "src/common/ieee802_11_common.c",
+        "src/common/nan_de.c",
         "src/common/sae.c",
         "src/common/sae_pk.c",
         "src/common/wpa_common.c",
@@ -424,6 +424,9 @@
         "src/p2p/p2p_pd.c",
         "src/p2p/p2p_sd.c",
         "src/p2p/p2p_utils.c",
+        "src/pasn/pasn_initiator.c",
+        "src/pasn/pasn_responder.c",
+        "src/pasn/pasn_common.c",
         "src/rsn_supp/pmksa_cache.c",
         "src/rsn_supp/preauth.c",
         "src/rsn_supp/tdls.c",
@@ -471,24 +474,7 @@
         "wpas_glue.c",
         "wpa_supplicant.c",
         "wps_supplicant.c",
-    ] +
-        select(soong_config_variable("wpa_supplicant", "bgscan_simple"), {
-            true: [
-                "bgscan.c",
-                "bgscan_simple.c",
-            ],
-            default: [],
-        }) +
-        select(soong_config_variable("wpa_supplicant", "pasn"), {
-            false: [],
-            default: [
-                "pasn_supplicant.c",
-                "src/pasn/pasn_initiator.c",
-                "src/pasn/pasn_responder.c",
-                "src/pasn/pasn_common.c",
-            ],
-        }),
-
+    ],
 }
 
 // Generated by building wpa_cli and printing LOCAL_SRC_FILES
@@ -702,7 +688,29 @@
     },
 }
 
-// For converting the default to soong
+// End of cuttlefish section
+
+// The section starting below is for non-cuttlefish products.
+// For cuttlefish series please update the section starting from `wpa_supplicant_headers`.
+
+// If you need to add a new build setting based on a product config, ex:
+// ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)
+//   L_CFLAGS += -DENABLE_PRIV_CMD_UPDATE_MBO_CELL_STATUS
+// endif
+
+// In order to export the Makefile variable to soong, you will need to use a `soong_config_set` method
+// under `build/core/board_config_wpa_supplicant.mk`. Ex:
+// ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)
+//   $(call soong_config_set_bool,wpa_supplicant_8,wifi_priv_cmd_update_mbo_cell_status,true)
+// endif
+
+// And then use the select statement in Android.bp to reflect the condition you need, ex:
+// select(soong_config_variable("wpa_supplicant_8", "wifi_priv_cmd_update_mbo_cell_status"), {
+//     true: ["-DENABLE_PRIV_CMD_UPDATE_MBO_CELL_STATUS"],
+//     default: [],
+// })
+
+// Start of non-cuttlefish section
 cc_defaults {
     name: "wpa_supplicant_driver_srcs_default",
     srcs: [
@@ -786,7 +794,6 @@
         "-DCONFIG_DPP",
         "-DCONFIG_DPP2",
         "-DCONFIG_DRIVER_NL80211",
-        "-DCONFIG_DRIVER_NL80211_QCA",
         "-DCONFIG_ECC",
         "-DCONFIG_ERP",
         "-DCONFIG_FILS",
@@ -806,12 +813,10 @@
         "-DCONFIG_NO_RADIUS",
         "-DCONFIG_NO_RADIUS",
         "-DCONFIG_NO_RANDOM_POOL",
-        "-DCONFIG_NO_ROAMING",
         "-DCONFIG_NO_VLAN",
         "-DCONFIG_OFFCHANNEL",
         "-DCONFIG_OWE",
         "-DCONFIG_P2P",
-        "-DCONFIG_PASN",
         "-DCONFIG_PTKSA_CACHE",
         "-DCONFIG_SAE",
         "-DCONFIG_SAE_PK",
@@ -870,21 +875,33 @@
     ] + select(soong_config_variable("wpa_supplicant_8", "wpa_supplicant_use_stub_lib"), {
         true: ["-DANDROID_LIB_STUB"],
         default: [],
-    }) + select(soong_config_variable("wpa_supplicant_8", "board_hostapd_config_80211w_mfp_optional"), {
-        true: ["-DENABLE_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL"],
-        default: [],
     }) + select(soong_config_variable("wpa_supplicant_8", "board_wpa_supplicant_private_lib_event"), {
         true: ["-DANDROID_LIB_EVENT"],
         default: [],
     }) + select(soong_config_variable("wpa_supplicant_8", "wifi_priv_cmd_update_mbo_cell_status"), {
         true: ["-DENABLE_PRIV_CMD_UPDATE_MBO_CELL_STATUS"],
         default: [],
-    }) + select(soong_config_variable("wpa_supplicant_8", "hostapd_11ax"), {
-        true: ["-DCONFIG_IEEE80211AX"],
-        default: [],
     }) + select(soong_config_variable("wpa_supplicant_8", "wifi_brcm_open_source_multi_akm"), {
         true: ["-DWIFI_BRCM_OPEN_SOURCE_MULTI_AKM"],
         default: [],
+    }) + select(soong_config_variable("wpa_supplicant_8", "wpa_supplicant_11ax"), {
+        true: ["-DCONFIG_IEEE80211AX"],
+        default: [],
+    }) + select(soong_config_variable("wpa_supplicant_8", "wpa_supplicant_11be"), {
+        true: ["-DCONFIG_IEEE80211BE"],
+        default: [],
+    }) + select(soong_config_variable("wpa_supplicant", "roaming"), {
+        true: [],
+        default: ["-DCONFIG_NO_ROAMING"],
+    }) + select(soong_config_variable("wpa_supplicant", "pasn"), {
+        false: [],
+        default: ["-DCONFIG_PASN"],
+    }) + select(soong_config_variable("wpa_supplicant", "bgscan_simple"), {
+        true: [
+            "-DCONFIG_BGSCAN",
+            "-DCONFIG_BGSCAN_SIMPLE",
+        ],
+        default: [],
     }),
     arch: {
         arm: {
@@ -979,7 +996,6 @@
         "op_classes.c",
         "p2p_supplicant.c",
         "p2p_supplicant_sd.c",
-        "pasn_supplicant.c",
         "robust_av.c",
         "rrm.c",
         "scan.c",
@@ -1114,9 +1130,6 @@
         "src/p2p/p2p_pd.c",
         "src/p2p/p2p_sd.c",
         "src/p2p/p2p_utils.c",
-        "src/pasn/pasn_common.c",
-        "src/pasn/pasn_initiator.c",
-        "src/pasn/pasn_responder.c",
         "src/rsn_supp/pmksa_cache.c",
         "src/rsn_supp/preauth.c",
         "src/rsn_supp/tdls.c",
@@ -1157,9 +1170,26 @@
         "src/wps/wps_upnp_event.c",
         "src/wps/wps_upnp_ssdp.c",
         "src/wps/wps_upnp_web.c",
-    ] + select(soong_config_variable("wpa_supplicant_8", "hostapd_11ax"), {
+    ] + select(soong_config_variable("wpa_supplicant_8", "wpa_supplicant_11ax"), {
         true: ["src/ap/ieee802_11_he.c"],
         default: [],
+    }) + select(soong_config_variable("wpa_supplicant_8", "wpa_supplicant_11be"), {
+        true: ["src/ap/ieee802_11_eht.c"],
+        default: [],
+    }) + select(soong_config_variable("wpa_supplicant", "bgscan_simple"), {
+        true: [
+            "bgscan.c",
+            "bgscan_simple.c",
+        ],
+        default: [],
+    }) + select(soong_config_variable("wpa_supplicant", "pasn"), {
+        false: [],
+        default: [
+            "pasn_supplicant.c",
+            "src/pasn/pasn_initiator.c",
+            "src/pasn/pasn_responder.c",
+            "src/pasn/pasn_common.c",
+        ],
     }),
     defaults: [
         "wpa_supplicant_driver_srcs_default",
@@ -1222,6 +1252,18 @@
     ],
 }
 
+cc_defaults {
+    name: "wpa_supplicant_usd_defaults",
+    cflags: [
+        "-DCONFIG_NAN_USD",
+    ],
+    srcs: [
+        "nan_usd.c",
+        "src/ap/nan_usd_ap.c",
+        "src/common/nan_de.c",
+    ],
+}
+
 cc_binary {
     name: "wpa_cli",
     proprietary: true,
@@ -1295,6 +1337,7 @@
         "wpa_supplicant_srcs_default",
         "wpa_supplicant_cflags_default",
         "wpa_supplicant_includes_default",
+        "wpa_supplicant_usd_defaults",
     ],
     soong_config_variables: {
         board_wpa_supplicant_private_lib: {
@@ -1377,6 +1420,7 @@
         "wpa_supplicant_mainline_srcs_default",
         "wpa_supplicant_includes_default",
         "wpa_supplicant_mainline_cflags_default",
+        "wpa_supplicant_usd_defaults",
     ],
     apex_available: [
         "//apex_available:platform",
@@ -1415,6 +1459,7 @@
         "wpa_supplicant_includes_default",
         "wpa_supplicant_mainline_cflags_default",
         "wpa_supplicant_mainline_srcs_default",
+        "wpa_supplicant_usd_defaults",
     ],
     shared_libs: [
         "android.system.wifi.mainline_supplicant-ndk",
@@ -1448,6 +1493,7 @@
     name: "libwpa_aidl",
     vendor: true,
     cppflags: [
+        "-DCONFIG_NAN_USD",
         "-Wall",
         "-Werror",
         "-Wno-unused-parameter",
@@ -1480,3 +1526,18 @@
         "wpa_supplicant_includes_default",
     ],
 }
+
+// End of non-cuttlefish section
+
+genrule {
+    name: "com.android.hardware.wpa_supplicant.rc-gen",
+    srcs: ["aidl/vendor/android.hardware.wifi.supplicant-service.rc"],
+    out: ["com.android.hardware.wpa_supplicant.rc"],
+    cmd: "sed -E 's@/vendor/bin@/apex/com.android.hardware.wpa_supplicant/bin@' $(in) > $(out)",
+}
+
+prebuilt_etc {
+    name: "com.android.hardware.wpa_supplicant.rc",
+    src: ":com.android.hardware.wpa_supplicant.rc-gen",
+    installable: false,
+}
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 581d907..205d8b7 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -181,7 +181,7 @@
 LDFLAGS += -rdynamic
 L_CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-L_CFLAGS += -DWPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls
 LIBS += -lbfd
 LIBS_p += -lbfd
 LIBS_c += -lbfd
@@ -329,6 +329,7 @@
 ifdef CONFIG_NAN_USD
 OBJS += src/common/nan_de.c
 OBJS += nan_usd.c
+NEED_OFFCHANNEL=y
 L_CFLAGS += -DCONFIG_NAN_USD
 endif
 
@@ -763,7 +764,7 @@
 EAPDYN += src/eap_common/eap_teap_common.c
 else
 L_CFLAGS += -DEAP_TEAP
-OBJS += src/eap_peer/eap_teap.c src/eap_peer/eap_teap_pac.c
+OBJS += src/eap_peer/eap_teap.c
 OBJS += src/eap_common/eap_teap_common.c
 endif
 TLS_FUNCS=y
@@ -819,8 +820,10 @@
 CONFIG_IEEE8021X_EAPOL=y
 NEED_ECC=y
 NEED_DRAGONFLY=y
+ifndef CONFIG_FIPS
 MS_FUNCS=y
 endif
+endif
 
 ifdef CONFIG_EAP_EKE
 # EAP-EKE
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 8bec178..7718f94 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -151,7 +151,7 @@
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD
+CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls
 LIBS += -lbfd -ldl -liberty -lz
 LIBS_p += -lbfd -ldl -liberty -lz
 LIBS_c += -lbfd -ldl -liberty -lz
@@ -320,6 +320,7 @@
 ifdef CONFIG_NAN_USD
 OBJS += ../src/common/nan_de.o
 OBJS += nan_usd.o
+NEED_OFFCHANNEL=y
 CFLAGS += -DCONFIG_NAN_USD
 endif
 
@@ -763,7 +764,7 @@
 
 ifdef CONFIG_EAP_TEAP
 # EAP-TEAP
-SRC_EAP_TEAP = ../src/eap_peer/eap_teap.c ../src/eap_peer/eap_teap_pac.c
+SRC_EAP_TEAP = ../src/eap_peer/eap_teap.c
 SRC_EAP_TEAP += ../src/eap_common/eap_teap_common.c
 ifeq ($(CONFIG_EAP_TEAP), dyn)
 CFLAGS += -DEAP_TEAP_DYNAMIC
@@ -831,8 +832,10 @@
 CONFIG_IEEE8021X_EAPOL=y
 NEED_ECC=y
 NEED_DRAGONFLY=y
+ifndef CONFIG_FIPS
 MS_FUNCS=y
 endif
+endif
 
 ifdef CONFIG_EAP_EKE
 # EAP-EKE
@@ -1193,6 +1196,7 @@
 endif
 
 ifeq ($(CONFIG_TLS), wolfssl)
+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
 ifdef TLS_FUNCS
 CFLAGS += -DWOLFSSL_DER_LOAD
 OBJS += ../src/crypto/tls_wolfssl.o
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index 7d30e23..f5488f2 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -618,28 +618,6 @@
 <3>ANQP fetch completed
 
 
-Hotspot 2.0 Rel 2 online signup and OSEN
-----------------------------------------
-
-Following parameters can be used to create a network profile for
-link-layer protected Hotspot 2.0 online signup connection with
-OSEN. Note that ssid and identify (NAI) values need to be set based on
-the information for the selected provider in the OSU Providers list
-ANQP-element.
-
-network={
-    ssid="HS 2.0 OSU"
-    proto=OSEN
-    key_mgmt=OSEN
-    pairwise=CCMP
-    group=GTK_NOT_USED
-    eap=WFA-UNAUTH-TLS
-    identity="anonymous@example.com"
-    ca_cert="osu-ca.pem"
-    ocsp=2
-}
-
-
 Hotspot 2.0 connection with external network selection
 ------------------------------------------------------
 
diff --git a/wpa_supplicant/aidl/mainline/Android.bp b/wpa_supplicant/aidl/mainline/Android.bp
index a2f5370..43c9c1a 100644
--- a/wpa_supplicant/aidl/mainline/Android.bp
+++ b/wpa_supplicant/aidl/mainline/Android.bp
@@ -31,6 +31,7 @@
         "libbinder_ndk",
     ],
     cppflags: [
+        "-DMAINLINE_SUPPLICANT",
         "-Wall",
         "-Werror",
         "-Wno-unused-parameter",
diff --git a/wpa_supplicant/aidl/mainline/callback_bridge.cpp b/wpa_supplicant/aidl/mainline/callback_bridge.cpp
new file mode 100644
index 0000000..a7b284b
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_bridge.cpp
@@ -0,0 +1,100 @@
+/*
+ * WPA Supplicant - Interface to receive callbacks from the core supplicant
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils.h"
+#include "usd_utils.h"
+
+extern "C"
+{
+#include "callback_bridge.h"
+}
+
+using ::aidl::android::system::wifi::mainline_supplicant::UsdMessageInfo;
+
+void mainline_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+        enum nan_service_protocol_type srv_proto_type,
+        int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+        bool fsd, const u8 *ssi, size_t ssi_len) {
+    if (!wpa_s || !peer_addr)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD service discovered");
+    auto serviceDiscoveryInfo = createUsdServiceDiscoveryInfo(
+        srv_proto_type, subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+    callback->onUsdServiceDiscovered(serviceDiscoveryInfo);
+}
+
+void mainline_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+        enum nan_service_protocol_type srv_proto_type,
+        int publish_id, int peer_subscribe_id,
+        const u8 *peer_addr, const u8 *ssi, size_t ssi_len) {
+    if (!wpa_s || !peer_addr)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD publish replied");
+    auto serviceDiscoveryInfo = createUsdServiceDiscoveryInfo(
+        srv_proto_type, publish_id, peer_subscribe_id, peer_addr, false /* fsd */,
+        ssi, ssi_len);
+    callback->onUsdPublishReplied(serviceDiscoveryInfo);
+}
+
+void mainline_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+        int peer_instance_id, const u8 *peer_addr,
+        const u8 *message, size_t message_len) {
+    if (!wpa_s || !peer_addr)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    UsdMessageInfo messageInfo;
+    messageInfo.ownId = id;
+    messageInfo.peerId = peer_instance_id;
+    messageInfo.peerMacAddress = macAddrBytesToArray(peer_addr);
+    messageInfo.message = message ? byteArrToVec(message, message_len) : std::vector<uint8_t>();
+
+    wpa_printf(MSG_DEBUG, "Notifying USD message received");
+    callback->onUsdMessageReceived(messageInfo);
+}
+
+void mainline_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+        int publish_id, enum nan_de_reason reason) {
+    if (!wpa_s)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD publish terminated");
+    callback->onUsdPublishTerminated(
+        publish_id, convertInternalUsdTerminateReasonCodeToAidl(reason));
+}
+
+void mainline_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+        int subscribe_id, enum nan_de_reason reason) {
+    if (!wpa_s)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD subscribe terminated");
+    callback->onUsdSubscribeTerminated(
+        subscribe_id, convertInternalUsdTerminateReasonCodeToAidl(reason));
+}
diff --git a/wpa_supplicant/aidl/mainline/callback_bridge.h b/wpa_supplicant/aidl/mainline/callback_bridge.h
new file mode 100644
index 0000000..c35bf37
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_bridge.h
@@ -0,0 +1,70 @@
+/*
+ * WPA Supplicant - Interface to receive callbacks from the core supplicant
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_CALLBACK_BRIDGE_H
+#define MAINLINE_SUPPLICANT_CALLBACK_BRIDGE_H
+
+/**
+ * Intermediary layer to receive callbacks from the core supplicant.
+ *
+ * For each callback, we provide a full implementation if the MAINLINE_SUPPLICANT
+ * flag is enabled. Otherwise, we provide an empty default implementation for builds
+ * that do not have the flag enabled (ex. the vendor supplicant).
+ */
+#ifdef _cplusplus
+extern "C"
+{
+#endif  // _cplusplus
+
+#include "utils/common.h"
+#include "src/common/nan_de.h"
+#include "wpa_supplicant_i.h"
+
+#ifdef MAINLINE_SUPPLICANT
+
+void mainline_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+    bool fsd, const u8 *ssi, size_t ssi_len);
+void mainline_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int publish_id, int peer_subscribe_id,
+    const u8 *peer_addr, const u8 *ssi, size_t ssi_len);
+void mainline_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+    int peer_instance_id, const u8 *peer_addr,
+    const u8 *message, size_t message_len);
+void mainline_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+    int publish_id, enum nan_de_reason reason);
+void mainline_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+    int subscribe_id, enum nan_de_reason reason);
+
+#else // MAINLINE_SUPPLICANT
+
+static void mainline_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+    bool fsd, const u8 *ssi, size_t ssi_len) {}
+static void mainline_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int publish_id, int peer_subscribe_id,
+    const u8 *peer_addr, const u8 *ssi, size_t ssi_len) {}
+static void mainline_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+    int peer_instance_id, const u8 *peer_addr,
+    const u8 *message, size_t message_len) {}
+static void mainline_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+    int publish_id, enum nan_de_reason reason) {}
+static void mainline_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+    int subscribe_id, enum nan_de_reason reason) {}
+
+#endif // MAINLINE_SUPPLICANT
+
+#ifdef _cplusplus
+}
+#endif  // _cplusplus
+
+#endif // MAINLINE_SUPPLICANT_CALLBACK_BRIDGE_H
diff --git a/wpa_supplicant/aidl/mainline/callback_manager.cpp b/wpa_supplicant/aidl/mainline/callback_manager.cpp
new file mode 100644
index 0000000..4922ce1
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_manager.cpp
@@ -0,0 +1,90 @@
+/*
+ * WPA Supplicant - Manager for callback objects
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "callback_manager.h"
+
+extern "C"
+{
+#include "utils/common.h"
+#include "wpa_supplicant_i.h"
+}
+
+// Raw pointer to the global structure maintained by the core
+// Declared here to be accessible to onDeath()
+struct wpa_global* wpa_global_;
+
+void onDeath(void* cookie) {
+    wpa_printf(MSG_ERROR, "Client died. Terminating...");
+    wpa_supplicant_terminate_proc(wpa_global_);
+}
+
+CallbackManager* CallbackManager::instance_ = NULL;
+
+void CallbackManager::initialize(struct wpa_global* wpa_global) {
+    wpa_printf(MSG_INFO, "Initializing the callback manager");
+    wpa_global_ = wpa_global;
+    instance_ = new CallbackManager();
+    instance_->death_notifier_ = AIBinder_DeathRecipient_new(onDeath);
+}
+
+CallbackManager* CallbackManager::getInstance() {
+    return instance_;
+}
+
+bool CallbackManager::registerStaIfaceCallback(std::string ifaceName,
+        const std::shared_ptr<IStaInterfaceCallback>& callback) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (!callback) {
+         wpa_printf(MSG_ERROR, "Attempted to register a null callback for STA iface %s",
+            ifaceName.c_str());
+        return false;
+    }
+    if (callbackRegisteredForStaIface(ifaceName)) {
+        wpa_printf(MSG_ERROR, "Callback is already registered for STA iface %s",
+            ifaceName.c_str());
+        return false;
+    }
+    binder_status_t status = AIBinder_linkToDeath(callback->asBinder().get(),
+        death_notifier_, nullptr /* cookie */);
+    if (status != STATUS_OK) {
+        wpa_printf(MSG_ERROR, "Received code %d when linking death recipient"
+            " for callback on STA iface %s", status, ifaceName.c_str());
+        return false;
+    }
+    wpa_printf(MSG_INFO, "Registered callback for STA iface %s", ifaceName.c_str());
+    sta_iface_callbacks_[ifaceName] = callback;
+    return true;
+}
+
+void CallbackManager::unregisterStaIfaceCallback(std::string ifaceName) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    wpa_printf(MSG_INFO, "Unregistering callback for STA iface %s",
+        ifaceName.c_str());
+    if (!callbackRegisteredForStaIface(ifaceName)) {
+        wpa_printf(MSG_INFO, "Callback does not need to be unregistered"
+            " for STA iface %s", ifaceName.c_str());
+        return;
+    }
+    auto callback = sta_iface_callbacks_[ifaceName];
+    binder_status_t status = AIBinder_unlinkToDeath(callback->asBinder().get(),
+        death_notifier_, nullptr /* cookie */);
+    if (status != STATUS_OK) {
+        wpa_printf(MSG_ERROR, "Received code %d when unlinking death recipient"
+            " for callback on STA iface %s", status, ifaceName.c_str());
+    }
+    sta_iface_callbacks_.erase(ifaceName);
+}
+
+std::shared_ptr<IStaInterfaceCallback> CallbackManager::getStaIfaceCallback(
+        std::string ifaceName) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (!callbackRegisteredForStaIface(ifaceName)) {
+        return nullptr;
+    }
+    return sta_iface_callbacks_[ifaceName];
+}
diff --git a/wpa_supplicant/aidl/mainline/callback_manager.h b/wpa_supplicant/aidl/mainline/callback_manager.h
new file mode 100644
index 0000000..788c7b3
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_manager.h
@@ -0,0 +1,52 @@
+/*
+ * WPA Supplicant - Manager for callback objects
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_CALLBACK_MANAGER_H
+#define MAINLINE_SUPPLICANT_CALLBACK_MANAGER_H
+
+#include <map>
+#include <mutex>
+#include <string>
+
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterfaceCallback.h>
+
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterfaceCallback;
+
+/**
+ * Class to manage all registered callback objects.
+ *
+ * On startup, a singleton instance should be created using initialize().
+ * Subsequent callers should retrieve the singleton using getInstance().
+ */
+class CallbackManager {
+    public:
+        // Singleton access
+        static void initialize(struct wpa_global* wpa_global);
+        static CallbackManager* getInstance();
+
+        // Member functions
+        bool registerStaIfaceCallback(std::string ifaceName,
+            const std::shared_ptr<IStaInterfaceCallback>& callback);
+        void unregisterStaIfaceCallback(std::string ifaceName);
+        std::shared_ptr<IStaInterfaceCallback> getStaIfaceCallback(std::string ifaceName);
+
+    private:
+        inline bool callbackRegisteredForStaIface(std::string ifaceName) {
+            return sta_iface_callbacks_.find(ifaceName) != sta_iface_callbacks_.end();
+        }
+
+        // Singleton instance of this class
+        static CallbackManager* instance_;
+
+        // Member variables
+        std::mutex mutex_;
+        AIBinder_DeathRecipient* death_notifier_;
+        std::map<std::string, std::shared_ptr<IStaInterfaceCallback>> sta_iface_callbacks_;
+};
+
+#endif // MAINLINE_SUPPLICANT_CALLBACK_MANAGER_H
diff --git a/wpa_supplicant/aidl/mainline/config/mainline_supplicant.rc b/wpa_supplicant/aidl/mainline/config/mainline_supplicant.rc
index 8c436c6..018df1c 100644
--- a/wpa_supplicant/aidl/mainline/config/mainline_supplicant.rc
+++ b/wpa_supplicant/aidl/mainline/config/mainline_supplicant.rc
@@ -3,7 +3,9 @@
     -g@android:wpa_wlan0
     interface aidl wifi_mainline_supplicant
     class main
-    user root
+    user wifi
+    group wifi net_raw net_admin
+    capabilities NET_RAW NET_ADMIN
     socket wpa_wlan0 dgram 660 wifi wifi
     disabled
     oneshot
diff --git a/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp b/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp
index dd2babe..ff5c388 100644
--- a/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp
+++ b/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp
@@ -7,6 +7,7 @@
  */
 
 #include "aidl/shared/shared_utils.h"
+#include "callback_manager.h"
 #include "mainline_supplicant.h"
 #include "utils.h"
 
@@ -18,14 +19,17 @@
     wpa_global_ = global;
 }
 
-ndk::ScopedAStatus MainlineSupplicant::addUsdInterface(const std::string& ifaceName) {
+ndk::ScopedAStatus MainlineSupplicant::addStaInterface(const std::string& ifaceName,
+        std::shared_ptr<IStaInterface>* _aidl_return) {
     if (ifaceName.empty()) {
         wpa_printf(MSG_ERROR, "Empty iface name provided");
         return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
     }
 
-    if (active_usd_ifaces_.find(ifaceName) != active_usd_ifaces_.end()) {
+    if (active_sta_ifaces_.find(ifaceName) != active_sta_ifaces_.end()) {
         wpa_printf(MSG_INFO, "Interface %s already exists", ifaceName.c_str());
+        std::shared_ptr<IStaInterface> staIface = active_sta_ifaces_[ifaceName];
+        _aidl_return = &staIface;
         return ndk::ScopedAStatus::ok();
     }
 
@@ -46,18 +50,22 @@
         return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
     }
 
+    std::shared_ptr<IStaInterface> staIface =
+        ndk::SharedRefBase::make<StaIface>(wpa_global_, ifaceName);
+    active_sta_ifaces_[ifaceName] = staIface;
+    *_aidl_return = staIface;
+
     wpa_printf(MSG_INFO, "Interface %s was added successfully", ifaceName.c_str());
-    active_usd_ifaces_.insert(ifaceName);
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus MainlineSupplicant::removeUsdInterface(const std::string& ifaceName) {
+ndk::ScopedAStatus MainlineSupplicant::removeStaInterface(const std::string& ifaceName) {
     if (ifaceName.empty()) {
         wpa_printf(MSG_ERROR, "Empty iface name provided");
         return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
     }
 
-    if (active_usd_ifaces_.find(ifaceName) == active_usd_ifaces_.end()) {
+    if (active_sta_ifaces_.find(ifaceName) == active_sta_ifaces_.end()) {
         wpa_printf(MSG_ERROR, "Interface %s does not exist", ifaceName.c_str());
         return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN);
     }
@@ -73,8 +81,13 @@
         return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
     }
 
+    // Remove interface and callback from the internal maps
+    CallbackManager* callbackManager = CallbackManager::getInstance();
+    WPA_ASSERT(callbackManager);
+    callbackManager->unregisterStaIfaceCallback(ifaceName);
+    active_sta_ifaces_.erase(ifaceName);
+
     wpa_printf(MSG_INFO, "Interface %s was removed successfully", ifaceName.c_str());
-    active_usd_ifaces_.erase(ifaceName);
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/wpa_supplicant/aidl/mainline/mainline_supplicant.h b/wpa_supplicant/aidl/mainline/mainline_supplicant.h
index 38a355f..fea7c73 100644
--- a/wpa_supplicant/aidl/mainline/mainline_supplicant.h
+++ b/wpa_supplicant/aidl/mainline/mainline_supplicant.h
@@ -9,9 +9,12 @@
 #ifndef MAINLINE_SUPPLICANT_IMPL_H
 #define MAINLINE_SUPPLICANT_IMPL_H
 
-#include <set>
+#include <map>
+
+#include "sta_iface.h"
 
 #include <aidl/android/system/wifi/mainline_supplicant/BnMainlineSupplicant.h>
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterface.h>
 #include <aidl/android/system/wifi/mainline_supplicant/SupplicantStatusCode.h>
 
 extern "C"
@@ -24,20 +27,22 @@
 }
 
 using ::aidl::android::system::wifi::mainline_supplicant::BnMainlineSupplicant;
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterface;
 using ::aidl::android::system::wifi::mainline_supplicant::SupplicantStatusCode;
 
 class MainlineSupplicant : public BnMainlineSupplicant {
     public:
         MainlineSupplicant(struct wpa_global* global);
-        ndk::ScopedAStatus addUsdInterface(const std::string& ifaceName);
-        ndk::ScopedAStatus removeUsdInterface(const std::string& ifaceName);
+        ndk::ScopedAStatus addStaInterface(const std::string& ifaceName,
+            std::shared_ptr<IStaInterface>* _aidl_return);
+        ndk::ScopedAStatus removeStaInterface(const std::string& ifaceName);
         ndk::ScopedAStatus terminate();
 
     private:
         // Raw pointer to the global structure maintained by the core
         struct wpa_global* wpa_global_;
-        // Names of all active USD interfaces
-        std::set<std::string> active_usd_ifaces_;
+        // Map containing all active STA interfaces, mapped by iface name -> object
+        std::map<std::string, std::shared_ptr<IStaInterface>> active_sta_ifaces_;
 };
 
 #endif  // MAINLINE_SUPPLICANT_IMPL_H
diff --git a/wpa_supplicant/aidl/mainline/service.cpp b/wpa_supplicant/aidl/mainline/service.cpp
index da343ea..002abbb 100644
--- a/wpa_supplicant/aidl/mainline/service.cpp
+++ b/wpa_supplicant/aidl/mainline/service.cpp
@@ -9,6 +9,7 @@
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
+#include "callback_manager.h"
 #include "mainline_supplicant.h"
 
 extern "C"
@@ -80,6 +81,7 @@
     }
 
     wpa_printf(MSG_INFO, "AIDL setup is complete");
+    CallbackManager::initialize(global);
     return priv;
 }
 
diff --git a/wpa_supplicant/aidl/mainline/sta_iface.cpp b/wpa_supplicant/aidl/mainline/sta_iface.cpp
new file mode 100644
index 0000000..0eaa199
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/sta_iface.cpp
@@ -0,0 +1,188 @@
+/*
+ * WPA Supplicant - Station mode interface
+ * Copyright (c) 2024, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "callback_manager.h"
+#include "sta_iface.h"
+#include "usd_utils.h"
+
+extern "C"
+{
+#include "utils/common.h"
+#include "nan_usd.h"
+#include "wpa_supplicant_i.h"
+}
+
+StaIface::StaIface(struct wpa_global* wpa_global, std::string iface_name)
+    : wpa_global_(wpa_global), iface_name_(iface_name) {}
+
+struct wpa_supplicant* StaIface::retrieveIfacePtr() {
+    return wpa_supplicant_get_iface(wpa_global_, iface_name_.c_str());
+}
+
+::ndk::ScopedAStatus StaIface::registerCallback(
+        const std::shared_ptr<IStaInterfaceCallback>& callback) {
+    CallbackManager* callbackManager = CallbackManager::getInstance();
+    WPA_ASSERT(callbackManager);
+    if (callbackManager->registerStaIfaceCallback(iface_name_, callback)) {
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+}
+
+::ndk::ScopedAStatus StaIface::getUsdCapabilities(UsdCapabilities* _aidl_return) {
+    UsdCapabilities capabilities;
+    capabilities.isUsdPublisherSupported = kIsUsdPublisherSupported;
+    capabilities.isUsdSubscriberSupported = kIsUsdSubscriberSupported;
+    capabilities.maxLocalSsiLengthBytes = kMaxUsdLocalSsiLengthBytes;
+    capabilities.maxServiceNameLengthBytes = kMaxUsdServiceNameLengthBytes;
+    capabilities.maxMatchFilterLengthBytes = kMaxUsdMatchFilterLengthBytes;
+    capabilities.maxNumPublishSessions = kMaxNumUsdPublishSessions;
+    capabilities.maxNumSubscribeSessions = kMaxNumUsdSubscribeSessions;
+    *_aidl_return = capabilities;
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::startUsdPublish(int32_t cmdId,
+        const UsdPublishConfig& publishConfig) {
+    if (!validateUsdPublishConfig(publishConfig)) {
+        wpa_printf(MSG_ERROR, "USD publish config is invalid");
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr ssiBuffer = {nullptr, nullptr};
+    if (!publishConfig.baseConfig.serviceSpecificInfo.empty()) {
+        ssiBuffer = convertVectorToWpaBuf(publishConfig.baseConfig.serviceSpecificInfo);
+        if (ssiBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert USD publish SSI to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+
+    struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+    struct nan_publish_params nanPublishParams =
+        convertAidlUsdPublishConfigToInternal(publishConfig);
+    int publishId = wpas_nan_usd_publish(
+        wpa_s, publishConfig.baseConfig.serviceName.c_str(),
+        convertAidlServiceProtoTypeToInternal(
+            publishConfig.baseConfig.serviceProtoType),
+        ssiBuffer.get(), &nanPublishParams, false /* p2p */);
+
+    // Core supplicant does not have an internal callback for USD publish,
+    // so invoke the failure callback directly if needed.
+    if (publishId < 0) {
+        wpa_printf(MSG_INFO, "Failed to configure USD publish");
+        auto callback = getStaIfaceCallback(iface_name_);
+        if (callback) {
+            callback->onUsdPublishConfigFailed(
+                cmdId, IStaInterfaceCallback::UsdConfigErrorCode::FAILURE_UNKNOWN);
+        }
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::startUsdSubscribe(int32_t cmdId,
+        const UsdSubscribeConfig& subscribeConfig) {
+    if (!validateUsdSubscribeConfig(subscribeConfig)) {
+        wpa_printf(MSG_ERROR, "USD subscribe config is invalid");
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr ssiBuffer = {nullptr, nullptr};
+    if (!subscribeConfig.baseConfig.serviceSpecificInfo.empty()) {
+        ssiBuffer = convertVectorToWpaBuf(subscribeConfig.baseConfig.serviceSpecificInfo);
+        if (ssiBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert USD subscribe SSI to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+
+    struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+    struct nan_subscribe_params nanSubscribeParams =
+        convertAidlUsdSubscribeConfigToInternal(subscribeConfig);
+    int subscribeId = wpas_nan_usd_subscribe(
+        wpa_s, subscribeConfig.baseConfig.serviceName.c_str(),
+        convertAidlServiceProtoTypeToInternal(
+            subscribeConfig.baseConfig.serviceProtoType),
+        ssiBuffer.get(), &nanSubscribeParams, false /* p2p */);
+
+    // Core supplicant does not have an internal callback for USD subscribe,
+    // so invoke the failure callback directly if needed.
+    if (subscribeId < 0) {
+        wpa_printf(MSG_INFO, "Failed to configure USD subscribe");
+        auto callback = getStaIfaceCallback(iface_name_);
+        if (callback) {
+            callback->onUsdSubscribeConfigFailed(
+                cmdId, IStaInterfaceCallback::UsdConfigErrorCode::FAILURE_UNKNOWN);
+        }
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::updateUsdPublish(int32_t publishId,
+        const std::vector<uint8_t>& serviceSpecificInfo) {
+    if (!checkContainerSize(serviceSpecificInfo, kMaxUsdLocalSsiLengthBytes)) {
+        wpa_printf(MSG_ERROR, "Updated USD publish SSI of size %zu exceeds the"
+            " supported size of %d", serviceSpecificInfo.size(),
+            kMaxUsdLocalSsiLengthBytes);
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr ssiBuffer = {nullptr, nullptr};
+    if (!serviceSpecificInfo.empty()) {
+        ssiBuffer = convertVectorToWpaBuf(serviceSpecificInfo);
+        if (ssiBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert updated USD publish SSI to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+    int status = wpas_nan_usd_update_publish(
+        retrieveIfacePtr(), publishId, ssiBuffer.get());
+    if (status < 0) {
+        wpa_printf(MSG_ERROR, "Failed to update USD publish");
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::cancelUsdPublish(int32_t publishId) {
+    // Status code is returned by the callback
+    wpas_nan_usd_cancel_publish(retrieveIfacePtr(), publishId);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::cancelUsdSubscribe(int32_t subscribeId) {
+    // Status code is returned by the callback
+    wpas_nan_usd_cancel_subscribe(retrieveIfacePtr(), subscribeId);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::sendUsdMessage(const UsdMessageInfo& messageInfo) {
+    if (!checkContainerSize(messageInfo.message, kMaxUsdLocalSsiLengthBytes)) {
+        wpa_printf(MSG_ERROR, "USD message of size %zu exceeds the supported size of %d",
+            messageInfo.message.size(), kMaxUsdLocalSsiLengthBytes);
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr msgBuffer = {nullptr, nullptr};
+    if (!messageInfo.message.empty()) {
+        msgBuffer = convertVectorToWpaBuf(messageInfo.message);
+        if (msgBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert USD message contents to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+    int handle = messageInfo.ownId;
+    int reqInstanceId = messageInfo.peerId;
+    int status = wpas_nan_usd_transmit(
+        retrieveIfacePtr(), handle, msgBuffer.get(), nullptr /* elems */,
+        messageInfo.peerMacAddress.data(), reqInstanceId);
+    if (status < 0) {
+        wpa_printf(MSG_ERROR, "Failed to send USD message");
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
diff --git a/wpa_supplicant/aidl/mainline/sta_iface.h b/wpa_supplicant/aidl/mainline/sta_iface.h
new file mode 100644
index 0000000..8f9b665
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/sta_iface.h
@@ -0,0 +1,42 @@
+/*
+ * WPA Supplicant - Station mode interface
+ * Copyright (c) 2024, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_STA_IFACE_H
+#define MAINLINE_SUPPLICANT_STA_IFACE_H
+
+#include <aidl/android/system/wifi/mainline_supplicant/BnStaInterface.h>
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterfaceCallback.h>
+
+using ::aidl::android::system::wifi::mainline_supplicant::BnStaInterface;
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterfaceCallback;
+using ::aidl::android::system::wifi::mainline_supplicant::UsdMessageInfo;
+
+class StaIface : public BnStaInterface {
+    public:
+        StaIface(struct wpa_global* wpa_global, std::string iface_name);
+        ::ndk::ScopedAStatus registerCallback(
+            const std::shared_ptr<IStaInterfaceCallback>& in_callback) override;
+        ::ndk::ScopedAStatus getUsdCapabilities(UsdCapabilities* _aidl_return) override;
+        ::ndk::ScopedAStatus startUsdPublish(int32_t in_cmdId,
+            const UsdPublishConfig& in_publishConfig) override;
+        ::ndk::ScopedAStatus startUsdSubscribe(int32_t in_cmdId,
+            const UsdSubscribeConfig& in_subscribeConfig) override;
+        ::ndk::ScopedAStatus updateUsdPublish(int32_t in_publishId,
+            const std::vector<uint8_t>& in_serviceSpecificInfo) override;
+        ::ndk::ScopedAStatus cancelUsdPublish(int32_t in_publishId) override;
+        ::ndk::ScopedAStatus cancelUsdSubscribe(int32_t in_subscribeId) override;
+        ::ndk::ScopedAStatus sendUsdMessage(const UsdMessageInfo& in_messageInfo) override;
+
+    private:
+        wpa_global* wpa_global_;
+        std::string iface_name_;
+
+        struct wpa_supplicant* retrieveIfacePtr();
+};
+
+#endif // MAINLINE_SUPPLICANT_STA_IFACE_H
diff --git a/wpa_supplicant/aidl/mainline/usd_utils.h b/wpa_supplicant/aidl/mainline/usd_utils.h
new file mode 100644
index 0000000..4d8a52f
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/usd_utils.h
@@ -0,0 +1,196 @@
+/*
+ * WPA Supplicant - Helper functions for USD
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_USD_UTILS_H
+#define MAINLINE_SUPPLICANT_USD_UTILS_H
+
+#include "utils.h"
+
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterface.h>
+#include <aidl/android/system/wifi/mainline_supplicant/UsdServiceProtoType.h>
+
+extern "C"
+{
+#include "utils/common.h"
+#include "src/common/nan_de.h"
+}
+
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterface;
+using ::aidl::android::system::wifi::mainline_supplicant::UsdServiceProtoType;
+
+constexpr bool kIsUsdPublisherSupported = true;
+constexpr bool kIsUsdSubscriberSupported = true;
+constexpr int32_t kMaxUsdLocalSsiLengthBytes = 1400;
+constexpr int32_t kMaxUsdServiceNameLengthBytes = 255;
+constexpr int32_t kMaxUsdMatchFilterLengthBytes = 0;
+constexpr int32_t kMaxNumUsdPublishSessions = NAN_DE_MAX_SERVICE;
+constexpr int32_t kMaxNumUsdSubscribeSessions = NAN_DE_MAX_SERVICE;
+
+static bool validateUsdBaseConfig(IStaInterface::UsdBaseConfig baseConfig) {
+    if (!isValidEnumValue(baseConfig.serviceProtoType,
+            UsdServiceProtoType::GENERIC, UsdServiceProtoType::CSA_MATTER)) {
+        wpa_printf(MSG_ERROR, "Unknown protocol type received: %d",
+            static_cast<int>(baseConfig.serviceProtoType));
+        return false;
+    }
+    if (!checkContainerSize(baseConfig.serviceName, kMaxUsdServiceNameLengthBytes)) {
+        wpa_printf(MSG_ERROR, "Service name of size %zu exceeds the supported size of %d",
+            baseConfig.serviceName.size(), kMaxUsdServiceNameLengthBytes);
+        return false;
+    }
+    if (!checkContainerSize(baseConfig.serviceSpecificInfo, kMaxUsdLocalSsiLengthBytes)) {
+        wpa_printf(MSG_ERROR, "Service specific info of size %zu exceeds"
+            " the supported size of %d", baseConfig.serviceSpecificInfo.size(),
+            kMaxUsdLocalSsiLengthBytes);
+        return false;
+    }
+    if (baseConfig.txMatchFilter.has_value() && !checkContainerSize(
+            baseConfig.txMatchFilter.value(), kMaxUsdMatchFilterLengthBytes)) {
+        wpa_printf(MSG_ERROR, "TX match filter of size %zu exceeds"
+            " the supported size of %d", baseConfig.txMatchFilter.value().size(),
+            kMaxUsdMatchFilterLengthBytes);
+        return false;
+    }
+    if (baseConfig.rxMatchFilter.has_value() && !checkContainerSize(
+            baseConfig.rxMatchFilter.value(), kMaxUsdMatchFilterLengthBytes)) {
+        wpa_printf(MSG_ERROR, "RX match filter of size %zu exceeds"
+            " the supported size of %d", baseConfig.rxMatchFilter.value().size(),
+            kMaxUsdMatchFilterLengthBytes);
+        return false;
+    }
+    return true;
+}
+
+static bool validateUsdPublishConfig(IStaInterface::UsdPublishConfig publishConfig) {
+    if (!validateUsdBaseConfig(publishConfig.baseConfig)) {
+        return false;
+    }
+    if (!isValidEnumValue(publishConfig.publishType,
+            IStaInterface::UsdPublishType::SOLICITED_ONLY,
+            IStaInterface::UsdPublishType::SOLICITED_AND_UNSOLICITED)) {
+        wpa_printf(MSG_ERROR, "Unknown publish type received: %d",
+            static_cast<int>(publishConfig.publishType));
+        return false;
+    }
+    if (!isValidEnumValue(publishConfig.transmissionType,
+            IStaInterface::UsdPublishTransmissionType::UNICAST,
+            IStaInterface::UsdPublishTransmissionType::MULTICAST)) {
+        wpa_printf(MSG_ERROR, "Unknown transmission type received: %d",
+            static_cast<int>(publishConfig.transmissionType));
+        return false;
+    }
+    return true;
+}
+
+static bool validateUsdSubscribeConfig(IStaInterface::UsdSubscribeConfig subscribeConfig) {
+    if (!validateUsdBaseConfig(subscribeConfig.baseConfig)) {
+        return false;
+    }
+    if (!isValidEnumValue(subscribeConfig.subscribeType,
+            IStaInterface::UsdSubscribeType::PASSIVE_MODE,
+            IStaInterface::UsdSubscribeType::ACTIVE_MODE)) {
+        wpa_printf(MSG_ERROR, "Unknown subscribe type received: %d",
+            static_cast<int>(subscribeConfig.subscribeType));
+        return false;
+    }
+    return true;
+}
+
+static struct nan_publish_params convertAidlUsdPublishConfigToInternal(
+        IStaInterface::UsdPublishConfig publishConfig) {
+    struct nan_publish_params nanPublishParams;
+    nanPublishParams.unsolicited =
+        publishConfig.publishType == IStaInterface::UsdPublishType::SOLICITED_AND_UNSOLICITED
+            || publishConfig.publishType == IStaInterface::UsdPublishType::UNSOLICITED_ONLY;
+    nanPublishParams.solicited =
+        publishConfig.publishType == IStaInterface::UsdPublishType::SOLICITED_AND_UNSOLICITED
+            || publishConfig.publishType == IStaInterface::UsdPublishType::SOLICITED_ONLY;
+    nanPublishParams.solicited_multicast = nanPublishParams.solicited &&
+        publishConfig.transmissionType == IStaInterface::UsdPublishTransmissionType::MULTICAST;
+    nanPublishParams.ttl = publishConfig.baseConfig.ttlSec;
+    nanPublishParams.fsd = publishConfig.isFsd;
+    nanPublishParams.freq = publishConfig.baseConfig.defaultFreqMhz;
+    nanPublishParams.announcement_period = publishConfig.announcementPeriodMillis;
+    nanPublishParams.disable_events = !publishConfig.eventsEnabled;
+    // Pass the original pointer to the freq list, since the receiver will memcpy the data
+    nanPublishParams.freq_list = publishConfig.baseConfig.freqsMhz.empty()
+        ? NULL : publishConfig.baseConfig.freqsMhz.data();
+    return nanPublishParams;
+}
+
+static struct nan_subscribe_params convertAidlUsdSubscribeConfigToInternal(
+        IStaInterface::UsdSubscribeConfig subscribeConfig) {
+    struct nan_subscribe_params nanSubscribeParams;
+    nanSubscribeParams.active =
+        subscribeConfig.subscribeType == IStaInterface::UsdSubscribeType::ACTIVE_MODE;
+    nanSubscribeParams.ttl = subscribeConfig.baseConfig.ttlSec;
+    nanSubscribeParams.freq = subscribeConfig.baseConfig.defaultFreqMhz;
+    nanSubscribeParams.query_period = subscribeConfig.queryPeriodMillis;
+    // Pass the original pointer to the freq list, since the receiver will memcpy the data
+    nanSubscribeParams.freq_list = subscribeConfig.baseConfig.freqsMhz.empty()
+        ? NULL : subscribeConfig.baseConfig.freqsMhz.data();
+    return nanSubscribeParams;
+}
+
+static nan_service_protocol_type convertAidlServiceProtoTypeToInternal(
+        UsdServiceProtoType serviceProtoType) {
+    switch (serviceProtoType) {
+        case UsdServiceProtoType::GENERIC:
+            return NAN_SRV_PROTO_GENERIC;
+        case UsdServiceProtoType::CSA_MATTER:
+            return NAN_SRV_PROTO_CSA_MATTER;
+        default:
+            // Default case is not expected, due to the USD validation method
+            return NAN_SRV_PROTO_GENERIC;
+    };
+}
+
+static UsdServiceProtoType convertInternalUsdServiceProtoTypeToAidl(
+        nan_service_protocol_type protocolType) {
+    switch (protocolType) {
+        case NAN_SRV_PROTO_GENERIC:
+            return UsdServiceProtoType::GENERIC;
+        case NAN_SRV_PROTO_CSA_MATTER:
+            return UsdServiceProtoType::CSA_MATTER;
+        default:
+            wpa_printf(MSG_ERROR, "Received invalid USD proto type %d from internal",
+                static_cast<int>(protocolType));
+            return UsdServiceProtoType::GENERIC;
+    }
+}
+
+static IStaInterfaceCallback::UsdServiceDiscoveryInfo createUsdServiceDiscoveryInfo(
+        enum nan_service_protocol_type srv_proto_type,
+        int own_id, int peer_id, const u8 *peer_addr,
+        bool fsd, const u8 *ssi, size_t ssi_len) {
+    IStaInterfaceCallback::UsdServiceDiscoveryInfo discoveryInfo;
+    discoveryInfo.ownId = own_id;
+    discoveryInfo.peerId = peer_id;
+    // TODO: Fill the matchFilter field in the AIDL struct
+    discoveryInfo.matchFilter = std::vector<uint8_t>();
+    discoveryInfo.peerMacAddress = macAddrBytesToArray(peer_addr);
+    discoveryInfo.serviceProtoType = convertInternalUsdServiceProtoTypeToAidl(srv_proto_type);
+    discoveryInfo.serviceSpecificInfo = ssi ? byteArrToVec(ssi, ssi_len) : std::vector<uint8_t>();
+    discoveryInfo.isFsd = fsd;
+    return discoveryInfo;
+}
+
+static IStaInterfaceCallback::UsdTerminateReasonCode convertInternalUsdTerminateReasonCodeToAidl(
+        nan_de_reason terminateReason) {
+    switch (terminateReason) {
+        case NAN_DE_REASON_TIMEOUT:
+            return IStaInterfaceCallback::UsdTerminateReasonCode::TIMEOUT;
+        case NAN_DE_REASON_USER_REQUEST:
+            return IStaInterfaceCallback::UsdTerminateReasonCode::USER_REQUESTED;
+        case NAN_DE_REASON_FAILURE:
+        default:
+            return IStaInterfaceCallback::UsdTerminateReasonCode::FAILURE_UNKNOWN;
+    }
+}
+
+#endif // MAINLINE_SUPPLICANT_USD_UTILS_H
diff --git a/wpa_supplicant/aidl/mainline/utils.h b/wpa_supplicant/aidl/mainline/utils.h
index 703b9ee..bf7b9fb 100644
--- a/wpa_supplicant/aidl/mainline/utils.h
+++ b/wpa_supplicant/aidl/mainline/utils.h
@@ -9,17 +9,79 @@
 #ifndef MAINLINE_SUPPLICANT_UTILS_H
 #define MAINLINE_SUPPLICANT_UTILS_H
 
+#include "callback_manager.h"
+
 #include <aidl/android/system/wifi/mainline_supplicant/SupplicantStatusCode.h>
 
+extern "C"
+{
+#include "utils/common.h"
+#include "wpabuf.h"
+}
+
+namespace {
+// Custom deleter for wpabuf
+void freeWpaBuf(wpabuf *ptr) { wpabuf_free(ptr); }
+}
+
+using ::aidl::android::system::wifi::mainline_supplicant::SupplicantStatusCode;
+
+using wpabuf_unique_ptr = std::unique_ptr<wpabuf, void (*)(wpabuf *)>;
+
 inline ndk::ScopedAStatus createStatus(SupplicantStatusCode statusCode) {
-	return ndk::ScopedAStatus::fromServiceSpecificError(
-		static_cast<int32_t>(statusCode));
+    return ndk::ScopedAStatus::fromServiceSpecificError(
+        static_cast<int32_t>(statusCode));
 }
 
 inline ndk::ScopedAStatus createStatusWithMsg(
-	    SupplicantStatusCode statusCode, std::string msg) {
-	return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-		static_cast<int32_t>(statusCode), msg.c_str());
+        SupplicantStatusCode statusCode, std::string msg) {
+    return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+        static_cast<int32_t>(statusCode), msg.c_str());
+}
+
+// Check whether the container is within the maximum size
+template <typename T>
+inline bool checkContainerSize(const T& container, int maxSize) {
+    return container.size() <= maxSize;
+}
+
+// Check whether the enum value is within the specified range
+template <typename T>
+inline bool isValidEnumValue(T value, T enumRangeMin, T enumRangeMax) {
+    return static_cast<uint32_t>(value) >= static_cast<uint32_t>(enumRangeMin)
+        && static_cast<uint32_t>(value) <= static_cast<uint32_t>(enumRangeMax);
+}
+
+// Create a unique_ptr for a wpabuf ptr with a custom deleter
+inline wpabuf_unique_ptr createWpaBufUniquePtr(struct wpabuf *raw_ptr) {
+    return {raw_ptr, freeWpaBuf};
+}
+
+// Create a wpabuf ptr with a custom deleter, copying the data from the provided vector
+inline wpabuf_unique_ptr convertVectorToWpaBuf(const std::vector<uint8_t> &data) {
+    return createWpaBufUniquePtr(wpabuf_alloc_copy(data.data(), data.size()));
+}
+
+// Convert a byte array representation of a MAC address to an std::array
+inline std::array<uint8_t, ETH_ALEN> macAddrBytesToArray(const uint8_t* mac_addr) {
+    std::array<uint8_t, ETH_ALEN> arr;
+    std::copy(mac_addr, mac_addr + ETH_ALEN, std::begin(arr));
+    return arr;
+}
+
+// Convert a byte array to an std::vector
+inline std::vector<uint8_t> byteArrToVec(const uint8_t* arr, int len) {
+    return std::vector<uint8_t>{arr, arr + len};
+}
+
+// Wrapper to retrieve a STA iface callback registered with the callback manager
+static std::shared_ptr<IStaInterfaceCallback> getStaIfaceCallback(
+        std::string ifaceName) {
+    CallbackManager* callback_manager = CallbackManager::getInstance();
+    if (!callback_manager) {
+        return nullptr;
+    }
+    return callback_manager->getStaIfaceCallback(ifaceName);
 }
 
 #endif // MAINLINE_SUPPLICANT_UTILS_H
diff --git a/wpa_supplicant/aidl/vendor/aidl.cpp b/wpa_supplicant/aidl/vendor/aidl.cpp
index a0446fe..462e118 100644
--- a/wpa_supplicant/aidl/vendor/aidl.cpp
+++ b/wpa_supplicant/aidl/vendor/aidl.cpp
@@ -629,7 +629,7 @@
 
 	aidl_manager->notifyP2pProvisionDiscovery(
 		wpa_s, dev_addr, request, status, config_methods,
-		generated_pin, group_ifname);
+		generated_pin, group_ifname, 0);
 }
 
 void wpas_aidl_notify_p2p_sd_response(
@@ -1116,3 +1116,134 @@
 	wpa_printf(MSG_DEBUG, "Notifying Qos Policy SCS Response");
 	aidl_manager->notifyQosPolicyScsResponse(wpa_s, count, scs_resp);
 }
+
+void wpas_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+		bool fsd, const u8 *ssi, size_t ssi_len)
+{
+	if (!wpa_s || !peer_addr || !ssi)
+		return;
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying USD service discovered");
+	aidl_manager->notifyUsdServiceDiscovered(wpa_s, srv_proto_type,
+		subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+}
+
+void wpas_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int publish_id, int peer_subscribe_id,
+		const u8 *peer_addr, const u8 *ssi, size_t ssi_len)
+{
+	if (!wpa_s || !peer_addr || !ssi)
+		return;
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying USD publish replied");
+	aidl_manager->notifyUsdPublishReplied(wpa_s, srv_proto_type,
+		publish_id, peer_subscribe_id, peer_addr, ssi, ssi_len);
+}
+
+void wpas_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s,
+		int id, int peer_instance_id, const u8 *peer_addr,
+		const u8 *message, size_t message_len)
+{
+	if (!wpa_s || !peer_addr || !message)
+		return;
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying USD message received");
+	aidl_manager->notifyUsdMessageReceived(wpa_s, id, peer_instance_id,
+		peer_addr, message, message_len);
+}
+
+void wpas_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+		int publish_id, enum nan_de_reason reason)
+{
+	if (!wpa_s)
+		return;
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying USD publish terminated");
+	aidl_manager->notifyUsdPublishTerminated(wpa_s, publish_id, reason);
+}
+
+void wpas_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+		int subscribe_id, enum nan_de_reason reason)
+{
+	if (!wpa_s)
+		return;
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying USD subscribe terminated");
+	aidl_manager->notifyUsdSubscribeTerminated(wpa_s, subscribe_id, reason);
+}
+
+static enum p2p_prov_disc_status convert_p2p_status_code_to_p2p_prov_disc_status(int status) {
+	switch (status) {
+		case P2P_SC_SUCCESS:
+			return P2P_PROV_DISC_SUCCESS;
+		case P2P_SC_COMEBACK:
+			return P2P_PROV_DISC_INFO_UNAVAILABLE;
+		default:
+			return P2P_PROV_DISC_REJECTED;
+	}
+}
+
+void wpas_aidl_notify_p2p_bootstrap_request(
+	struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+	int status, u16 bootstrap_method, const char *group_ifname)
+{
+	if (!wpa_s || !dev_addr)
+		return;
+
+	wpa_printf(
+		MSG_DEBUG,
+		"Notifying P2P P2P bootstrap request to aidl control " MACSTR,
+		MAC2STR(dev_addr));
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	aidl_manager->notifyP2pProvisionDiscovery(
+		wpa_s, dev_addr, true, convert_p2p_status_code_to_p2p_prov_disc_status(status),
+		WPS_NOT_READY, 0, group_ifname, bootstrap_method);
+}
+
+void wpas_aidl_notify_p2p_bootstrap_response(
+	struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+	int status, u16 bootstrap_method, const char *group_ifname)
+{
+	if (!wpa_s || !dev_addr)
+		return;
+
+	wpa_printf(
+		MSG_DEBUG,
+		"Notifying P2P bootstrap response to aidl control " MACSTR,
+		MAC2STR(dev_addr));
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	aidl_manager->notifyP2pProvisionDiscovery(
+		wpa_s, dev_addr, false, convert_p2p_status_code_to_p2p_prov_disc_status(status),
+		WPS_NOT_READY, 0, group_ifname, bootstrap_method);
+}
diff --git a/wpa_supplicant/aidl/vendor/aidl.h b/wpa_supplicant/aidl/vendor/aidl.h
index 71620f4..039c6e1 100644
--- a/wpa_supplicant/aidl/vendor/aidl.h
+++ b/wpa_supplicant/aidl/vendor/aidl.h
@@ -96,6 +96,12 @@
 		struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
 		enum p2p_prov_disc_status status, u16 config_methods,
 		unsigned int generated_pin, const char *group_ifname);
+	void wpas_aidl_notify_p2p_bootstrap_request(
+		struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+		int status, u16 bootstrap_method, const char *group_ifname);
+	void wpas_aidl_notify_p2p_bootstrap_response(
+		struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+		int status, u16 bootstrap_method, const char *group_ifname);
 	void wpas_aidl_notify_p2p_sd_response(
 		struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
 		const u8 *tlvs, size_t tlvs_len);
@@ -157,6 +163,21 @@
 	ssize_t wpas_aidl_list_aliases(const char *prefix, char ***aliases);
 	void wpas_aidl_notify_qos_policy_scs_response(struct wpa_supplicant *wpa_s,
 		unsigned int count, int **scs_resp);
+	void wpas_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+		bool fsd, const u8 *ssi, size_t ssi_len);
+	void wpas_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int publish_id, int peer_subscribe_id,
+		const u8 *peer_addr, const u8 *ssi, size_t ssi_len);
+	void wpas_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+		int peer_instance_id, const u8 *peer_addr,
+		const u8 *message, size_t message_len);
+	void wpas_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+		int publish_id, enum nan_de_reason reason);
+	void wpas_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+		int subscribe_id, enum nan_de_reason reason);
 #else   // CONFIG_CTRL_IFACE_AIDL
 static inline int wpas_aidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -259,6 +280,14 @@
 	enum p2p_prov_disc_status status, u16 config_methods,
 	unsigned int generated_pin, const char *group_ifname)
 {}
+static void wpas_aidl_notify_p2p_bootstrap_request(
+	struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+	int status, u16 bootstrap_method, const char *group_ifname)
+{}
+static void wpas_aidl_notify_p2p_bootstrap_response(
+	struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+	int status, u16 bootstrap_method, const char *group_ifname)
+{}
 static void wpas_aidl_notify_p2p_sd_response(
 	struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
 	const u8 *tlvs, size_t tlvs_len)
@@ -357,6 +386,21 @@
 }
 static void wpas_aidl_notify_qos_policy_scs_response(struct wpa_supplicant *wpa_s,
 	unsigned int count, int **scs_resp) {}
+static void wpas_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+		bool fsd, const u8 *ssi, size_t ssi_len) {}
+static void wpas_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int publish_id, int peer_subscribe_id,
+		const u8 *peer_addr, const u8 *ssi, size_t ssi_len) {}
+static void wpas_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+		int peer_instance_id, const u8 *peer_addr,
+		const u8 *message, size_t message_len) {}
+static void wpas_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+		int publish_id, enum nan_de_reason reason) {}
+static void wpas_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+		int subscribe_id, enum nan_de_reason reason) {}
 #endif  // CONFIG_CTRL_IFACE_AIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/aidl/vendor/aidl_manager.cpp b/wpa_supplicant/aidl/vendor/aidl_manager.cpp
index 177f478..ec895a0 100644
--- a/wpa_supplicant/aidl/vendor/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/vendor/aidl_manager.cpp
@@ -708,8 +708,6 @@
 			return KeyMgmtMask::WAPI_PSK;
 		case WPA_KEY_MGMT_WAPI_CERT:
 			return KeyMgmtMask::WAPI_CERT;
-		case WPA_KEY_MGMT_OSEN:
-			return KeyMgmtMask::OSEN;
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 		case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
 			return KeyMgmtMask::SUITE_B_192;
@@ -945,8 +943,7 @@
 			misc_utils::convertWpaBufToVector(
 			anqp->hs20_connection_capability);
 		aidl_hs20_anqp_data.osuProvidersList =
-			misc_utils::convertWpaBufToVector(
-			anqp->hs20_osu_providers_list);
+			misc_utils::convertWpaBufToVector(NULL);
 #else
 		aidl_hs20_anqp_data.operatorFriendlyName =
 			misc_utils::convertWpaBufToVector(NULL);
@@ -1313,6 +1310,33 @@
 		std::placeholders::_1));
 }
 
+int32_t convertP2pPairingBootstrappingMethodsToAidl(u16 bootstrap_methods)
+{
+	int32_t pairingBootstrappingMethods = 0;
+
+	if (bootstrap_methods & P2P_PBMA_OPPORTUNISTIC) {
+		pairingBootstrappingMethods |=
+			P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_OPPORTUNISTIC;
+	}
+	if (bootstrap_methods & P2P_PBMA_PIN_CODE_DISPLAY) {
+		pairingBootstrappingMethods |=
+			P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_DISPLAY_PINCODE;
+	}
+	if (bootstrap_methods & P2P_PBMA_PASSPHRASE_DISPLAY) {
+		pairingBootstrappingMethods |=
+			P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_DISPLAY_PASSPHRASE;
+	}
+	if (bootstrap_methods & P2P_PBMA_PIN_CODE_KEYPAD) {
+		pairingBootstrappingMethods |=
+			P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_KEYPAD_PINCODE;
+	}
+	if (bootstrap_methods & P2P_PBMA_PASSPHRASE_KEYPAD) {
+		pairingBootstrappingMethods |=
+			P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_KEYPAD_PASSPHRASE;
+	}
+
+	return pairingBootstrappingMethods;
+}
 void AidlManager::notifyP2pDeviceFound(
 	struct wpa_supplicant *wpa_s, const u8 *addr,
 	const struct p2p_peer_info *info, const u8 *peer_wfd_device_info,
@@ -1375,8 +1399,15 @@
 		params.wfdR2DeviceInfo = aidl_peer_wfd_r2_device_info;
 		params.vendorElemBytes = aidl_vendor_elems;
 		if (areAidlServiceAndClientAtLeastVersion(4)) {
-			// TODO Fill the field when supplicant implementation is ready
-			params.pairingBootstrappingMethods = 0;
+			params.pairingBootstrappingMethods = convertP2pPairingBootstrappingMethodsToAidl(
+				info->pairing_config.bootstrap_methods);
+			if (info->nonce_tag_valid) {
+				params.dirInfo->cipherVersion =
+					P2pDirInfo::CipherVersion::DIRA_CIPHER_VERSION_128_BIT;
+				params.dirInfo->deviceInterfaceMacAddress = macAddrToArray(info->p2p_device_addr);
+				params.dirInfo->nonce = byteArrToVec(info->nonce, DEVICE_IDENTITY_NONCE_LEN);
+				params.dirInfo->dirTag = byteArrToVec(info->tag, DEVICE_IDENTITY_TAG_LEN);
+			}
 		}
 		callWithEachP2pIfaceCallback(
 			misc_utils::charBufToString(wpa_s->ifname),
@@ -1485,6 +1516,18 @@
 		std::placeholders::_1, reason));
 }
 
+uint32_t convertSupplicantKeyMgmtForP2pGroupConnectionToAidl(int supp_key_mgmt)
+{
+	uint32_t aidl_key_mgmt = 0;
+	if (supp_key_mgmt & WPA_KEY_MGMT_PSK) {
+		aidl_key_mgmt |= static_cast<uint32_t>(KeyMgmtMask::WPA_PSK);
+	}
+	if (supp_key_mgmt & WPA_KEY_MGMT_SAE) {
+		aidl_key_mgmt |= static_cast<uint32_t>(KeyMgmtMask::SAE);
+	}
+	return aidl_key_mgmt;
+}
+
 void AidlManager::notifyP2pGroupStarted(
 	struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid,
 	int persistent, int client, const u8 *ip)
@@ -1538,10 +1581,10 @@
 			   params.p2pClientIpInfo.ipAddressClient,
 			   params.p2pClientIpInfo.ipAddressMask,
 			   params.p2pClientIpInfo.ipAddressGo);
-        }
+    }
 	if (areAidlServiceAndClientAtLeastVersion(4)) {
-		// TODO Fill the field when supplicant implementation is ready
-		params.keyMgmtMask = 0;
+		params.keyMgmtMask = convertSupplicantKeyMgmtForP2pGroupConnectionToAidl(
+			wpa_group_s->key_mgmt);
 	}
 	callWithEachP2pIfaceCallback(
 		misc_utils::charBufToString(wpa_s->ifname),
@@ -1617,7 +1660,8 @@
 void AidlManager::notifyP2pProvisionDiscovery(
 	struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
 	enum p2p_prov_disc_status status, u16 config_methods,
-	unsigned int generated_pin, const char *group_ifname)
+	unsigned int generated_pin, const char *group_ifname,
+	u16 pairing_bootstrapping_method)
 {
 	if (!wpa_s || !dev_addr)
 		return;
@@ -1626,6 +1670,17 @@
 		p2p_iface_object_map_.end())
 		return;
 
+	int32_t aidl_pairing_bootstrapping_method = 0;
+	if (pairing_bootstrapping_method > 0) {
+		aidl_pairing_bootstrapping_method = convertP2pPairingBootstrappingMethodsToAidl(
+			pairing_bootstrapping_method);
+		if (aidl_pairing_bootstrapping_method == 0 || !areAidlServiceAndClientAtLeastVersion(4)) {
+			wpa_printf(MSG_ERROR, "Drop pairing bootstrapping message - method %d",
+				pairing_bootstrapping_method);
+			return;
+		}
+	}
+
 	std::string aidl_generated_pin;
 	if (generated_pin > 0) {
 		aidl_generated_pin =
@@ -1640,6 +1695,9 @@
 		params.status = static_cast<P2pProvDiscStatusCode>(status);
 		params.configMethods = config_methods;
 		params.generatedPin = aidl_generated_pin;
+		if (areAidlServiceAndClientAtLeastVersion(4)) {
+			params.pairingBootstrappingMethod = aidl_pairing_bootstrapping_method;
+		}
 		if (group_ifname != NULL) {
 			params.groupInterfaceName = misc_utils::charBufToString(group_ifname);
 		}
@@ -1679,75 +1737,6 @@
 		byteArrToVec(tlvs, tlvs_len)));
 }
 
-void AidlManager::notifyUsdBasedServiceDiscoveryResult(
-	struct wpa_supplicant *wpa_s, const u8 *peer_addr, int subscribe_id,
-	int peer_publish_id, int srv_proto_type, const u8 *ssi, size_t ssi_len)
-{
-	// TODO define the reason and map to AIDL defenition.
-	if (!wpa_s)
-		return;
-
-	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
-		p2p_iface_object_map_.end())
-		return;
-
-	if (!areAidlServiceAndClientAtLeastVersion(4)) {
-	      return;
-	}
-	// TODO Fill the fields when supplicant implementation is ready
-	P2pUsdBasedServiceDiscoveryResultParams params;
-
-	callWithEachP2pIfaceCallback(
-		misc_utils::charBufToString(wpa_s->ifname),
-		std::bind(
-		&ISupplicantP2pIfaceCallback::onUsdBasedServiceDiscoveryResult,
-		std::placeholders::_1, params));
-}
-
-void AidlManager::notifyUsdBasedServiceDiscoveryTerminated(
-	struct wpa_supplicant *wpa_s, int subscribe_id, int reason)
-{
-	// TODO define the reason and map to AIDL defenition.
-	if (!wpa_s)
-		return;
-
-	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
-		p2p_iface_object_map_.end())
-		return;
-
-	if (!areAidlServiceAndClientAtLeastVersion(4)) {
-	      return;
-	}
-
-	callWithEachP2pIfaceCallback(
-		misc_utils::charBufToString(wpa_s->ifname),
-		std::bind(
-		&ISupplicantP2pIfaceCallback::onUsdBasedServiceDiscoveryTerminated,
-		std::placeholders::_1, subscribe_id, UsdTerminateReasonCode::UNKNOWN));
-}
-
-void AidlManager::notifyUsdBasedServiceAdvertisementTerminated(
-	struct wpa_supplicant *wpa_s, int publish_id, int reason)
-{
-	// TODO define the reason and map to AIDL defenition.
-	if (!wpa_s)
-		return;
-
-	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
-		p2p_iface_object_map_.end())
-		return;
-
-	if (!areAidlServiceAndClientAtLeastVersion(4)) {
-	      return;
-	}
-
-	callWithEachP2pIfaceCallback(
-		misc_utils::charBufToString(wpa_s->ifname),
-		std::bind(
-		&ISupplicantP2pIfaceCallback::onUsdBasedServiceAdvertisementTerminated,
-		std::placeholders::_1, publish_id, UsdTerminateReasonCode::UNKNOWN));
-}
-
 void AidlManager::notifyApStaAuthorized(
 	struct wpa_supplicant *wpa_group_s, const u8 *sta, const u8 *p2p_dev_addr,
 	const u8 *ip)
@@ -1895,7 +1884,11 @@
 		return;
 	}
 
-	aidl_dpp_config_data.password = misc_utils::charBufToString(config->passphrase);
+	if (aidl_dpp_config_data.securityAkm == DppAkm::SAE)
+		aidl_dpp_config_data.password = misc_utils::charBufToString(config->sae_password);
+	else
+		aidl_dpp_config_data.password = misc_utils::charBufToString(config->passphrase);
+
 	aidl_dpp_config_data.psk = byteArrToVec(config->psk, 32);
 	std::vector<uint8_t> aidl_ssid(
 		config->ssid,
@@ -2976,6 +2969,210 @@
 			std::placeholders::_1, scsResponses));
 }
 
+void AidlManager::notifyUsdPublishStarted(struct wpa_supplicant *wpa_s,
+		int cmd_id, int publish_id)
+{
+	if (!wpa_s) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	callWithEachStaIfaceCallback(
+		misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdPublishStarted,
+			std::placeholders::_1, cmd_id, publish_id));
+}
+void AidlManager::notifyUsdSubscribeStarted(struct wpa_supplicant *wpa_s,
+		int cmd_id, int subscribe_id)
+{
+	if (!wpa_s) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	callWithEachStaIfaceCallback(
+		misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdSubscribeStarted,
+			std::placeholders::_1, cmd_id, subscribe_id));
+}
+void AidlManager::notifyUsdPublishConfigFailed(struct wpa_supplicant *wpa_s,
+	int cmd_id, ISupplicantStaIfaceCallback::UsdConfigErrorCode error_code)
+{
+	if (!wpa_s) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	callWithEachStaIfaceCallback(
+		misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdPublishConfigFailed,
+			std::placeholders::_1, cmd_id, error_code));
+}
+
+void AidlManager::notifyUsdSubscribeConfigFailed(struct wpa_supplicant *wpa_s,
+	int cmd_id, ISupplicantStaIfaceCallback::UsdConfigErrorCode error_code)
+{
+	if (!wpa_s) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	callWithEachStaIfaceCallback(
+		misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdSubscribeConfigFailed,
+			std::placeholders::_1, cmd_id, error_code));
+}
+
+UsdServiceProtoType convertUsdServiceProtoTypeToAidl(nan_service_protocol_type type) {
+	switch (type) {
+		case NAN_SRV_PROTO_GENERIC:
+			return UsdServiceProtoType::GENERIC;
+		case NAN_SRV_PROTO_CSA_MATTER:
+			return UsdServiceProtoType::CSA_MATTER;
+		default:
+			// Should not reach here
+			wpa_printf(MSG_ERROR, "Received invalid USD proto type %d from internal",
+				static_cast<int>(type));
+			return UsdServiceProtoType::GENERIC;
+	}
+}
+
+P2pUsdBasedServiceDiscoveryResultParams createP2pUsdBasedServiceDiscoveryResult(
+		enum nan_service_protocol_type srv_proto_type,
+		int own_id, int peer_id, const u8 *peer_addr,
+		const u8 *ssi, size_t ssi_len) {
+	P2pUsdBasedServiceDiscoveryResultParams p2pServiceDiscoveryInfo;
+	p2pServiceDiscoveryInfo.peerMacAddress = macAddrToArray(peer_addr);
+	p2pServiceDiscoveryInfo.sessionId = own_id;
+	p2pServiceDiscoveryInfo.peerSessionId = peer_id;
+	p2pServiceDiscoveryInfo.serviceProtocolType = static_cast<int>(srv_proto_type);
+	if (ssi != NULL) {
+		p2pServiceDiscoveryInfo.serviceSpecificInfo = byteArrToVec(ssi, ssi_len);
+	}
+	return p2pServiceDiscoveryInfo;
+}
+
+UsdServiceDiscoveryInfo createUsdServiceDiscoveryInfo(
+		enum nan_service_protocol_type srv_proto_type,
+		int own_id, int peer_id, const u8 *peer_addr,
+		bool fsd, const u8 *ssi, size_t ssi_len) {
+	// TODO: Fill the matchFilter field in the AIDL struct
+	UsdServiceDiscoveryInfo discoveryInfo;
+	discoveryInfo.ownId = own_id;
+	discoveryInfo.peerId = peer_id;
+	discoveryInfo.peerMacAddress = macAddrToArray(peer_addr);
+	discoveryInfo.protoType = convertUsdServiceProtoTypeToAidl(srv_proto_type);
+	discoveryInfo.serviceSpecificInfo = byteArrToVec(ssi, ssi_len);
+	discoveryInfo.isFsd = fsd;
+	return discoveryInfo;
+}
+
+void AidlManager::notifyUsdServiceDiscovered(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+		bool fsd, const u8 *ssi, size_t ssi_len)
+{
+	if (!wpa_s || !peer_addr) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	if (p2p_iface_object_map_.find(wpa_s->ifname) != p2p_iface_object_map_.end()) {
+		// Notify service discovered event on P2P device interface.
+		P2pUsdBasedServiceDiscoveryResultParams p2pServiceDiscoveryInfo =
+			createP2pUsdBasedServiceDiscoveryResult(srv_proto_type, subscribe_id,
+				peer_publish_id, peer_addr, ssi, ssi_len);
+		callWithEachP2pIfaceCallback(misc_utils::charBufToString(wpa_s->ifname),
+		std::bind(&ISupplicantP2pIfaceCallback::onUsdBasedServiceDiscoveryResult,
+			std::placeholders::_1, p2pServiceDiscoveryInfo));
+	} else {
+		// Notify service discovered event on STA interface.
+		UsdServiceDiscoveryInfo discoveryInfo = createUsdServiceDiscoveryInfo(
+			srv_proto_type, subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+		callWithEachStaIfaceCallback(
+				misc_utils::charBufToString(wpa_s->ifname), std::bind(
+					&ISupplicantStaIfaceCallback::onUsdServiceDiscovered,
+					std::placeholders::_1, discoveryInfo));
+	}
+}
+
+void AidlManager::notifyUsdPublishReplied(struct wpa_supplicant *wpa_s,
+		enum nan_service_protocol_type srv_proto_type,
+		int publish_id, int peer_subscribe_id,
+		const u8 *peer_addr, const u8 *ssi, size_t ssi_len)
+{
+	if (!wpa_s || !peer_addr || !ssi) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	UsdServiceDiscoveryInfo discoveryInfo = createUsdServiceDiscoveryInfo(
+		srv_proto_type, publish_id, peer_subscribe_id, peer_addr, false /* fsd */,
+		ssi, ssi_len);
+	callWithEachStaIfaceCallback(
+		misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdPublishReplied,
+			std::placeholders::_1, discoveryInfo));
+}
+
+void AidlManager::notifyUsdMessageReceived(struct wpa_supplicant *wpa_s, int id,
+		int peer_instance_id, const u8 *peer_addr,
+		const u8 *message, size_t message_len)
+{
+	if (!wpa_s || !peer_addr || !message) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+
+	UsdMessageInfo messageInfo;
+	messageInfo.ownId = id;
+	messageInfo.peerId = peer_instance_id;
+	messageInfo.peerMacAddress = macAddrToArray(peer_addr);
+	messageInfo.message = byteArrToVec(message, message_len);
+
+	callWithEachStaIfaceCallback(
+		misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdMessageReceived,
+			std::placeholders::_1, messageInfo));
+}
+
+UsdTerminateReasonCode convertUsdTerminateReasonCodeToAidl(nan_de_reason reason) {
+	switch (reason) {
+	case NAN_DE_REASON_TIMEOUT:
+		return UsdTerminateReasonCode::TIMEOUT;
+	case NAN_DE_REASON_USER_REQUEST:
+		return UsdTerminateReasonCode::USER_REQUEST;
+	case NAN_DE_REASON_FAILURE:
+		return UsdTerminateReasonCode::FAILURE;
+	default:
+		return UsdTerminateReasonCode::UNKNOWN;
+	}
+}
+
+void AidlManager::notifyUsdPublishTerminated(struct wpa_supplicant *wpa_s,
+		int publish_id, enum nan_de_reason reason)
+{
+	if (!wpa_s) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	UsdTerminateReasonCode aidlReasonCode = convertUsdTerminateReasonCodeToAidl(reason);
+	if (p2p_iface_object_map_.find(wpa_s->ifname) != p2p_iface_object_map_.end()) {
+		// Notify publish terminated event on P2P device interface.
+		callWithEachP2pIfaceCallback(
+			misc_utils::charBufToString(wpa_s->ifname),
+			std::bind(
+			&ISupplicantP2pIfaceCallback::onUsdBasedServiceAdvertisementTerminated,
+			std::placeholders::_1, publish_id, aidlReasonCode));
+	} else {
+		// Notify publish terminated event on STA interface.
+		callWithEachStaIfaceCallback(
+			misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdPublishTerminated,
+			std::placeholders::_1, publish_id, aidlReasonCode));
+	}
+}
+
+void AidlManager::notifyUsdSubscribeTerminated(struct wpa_supplicant *wpa_s,
+		int subscribe_id, enum nan_de_reason reason)
+{
+	if (!wpa_s) return;
+	if (!areAidlServiceAndClientAtLeastVersion(4)) return;
+	UsdTerminateReasonCode aidlReasonCode = convertUsdTerminateReasonCodeToAidl(reason);
+	if (p2p_iface_object_map_.find(wpa_s->ifname) != p2p_iface_object_map_.end()) {
+		// Notify subscribe terminated event on P2P device interface.
+		callWithEachP2pIfaceCallback(
+			misc_utils::charBufToString(wpa_s->ifname),
+			std::bind(
+			&ISupplicantP2pIfaceCallback::onUsdBasedServiceDiscoveryTerminated,
+			std::placeholders::_1, subscribe_id, aidlReasonCode));
+	} else {
+		// Notify subscribe terminated event on STA interface.
+		callWithEachStaIfaceCallback(
+			misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onUsdSubscribeTerminated,
+			std::placeholders::_1, subscribe_id, aidlReasonCode));
+	}
+}
+
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/aidl/vendor/aidl_manager.h b/wpa_supplicant/aidl/vendor/aidl_manager.h
index 46a40aa..3c18789 100644
--- a/wpa_supplicant/aidl/vendor/aidl_manager.h
+++ b/wpa_supplicant/aidl/vendor/aidl_manager.h
@@ -116,19 +116,11 @@
 	void notifyP2pProvisionDiscovery(
 		struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
 		enum p2p_prov_disc_status status, u16 config_methods,
-		unsigned int generated_pin, const char *group_ifname);
+		unsigned int generated_pin, const char *group_ifname,
+		u16 pairing_bootstrapping_method);
 	void notifyP2pSdResponse(
 		struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
 		const u8 *tlvs, size_t tlvs_len);
-	void notifyUsdBasedServiceDiscoveryResult(
-		struct wpa_supplicant *wpa_s, const u8 *peer_addr, int subscribe_id,
-		int peer_publish_id, int srv_proto_type, const u8 *ssi, size_t ssi_len);
-	void notifyUsdBasedServiceDiscoveryTerminated(
-		struct wpa_supplicant *wpa_s, int subscribe_id,
-		int reason);
-	void notifyUsdBasedServiceAdvertisementTerminated(
-		struct wpa_supplicant *wpa_s, int publish_id,
-		int reason);
 	void notifyApStaAuthorized(
 		struct wpa_supplicant *wpa_s, const u8 *sta,
 		const u8 *p2p_dev_addr, const u8 *ip);
@@ -178,6 +170,29 @@
 			unsigned int count, int **scs_resp);
 	void notifyMloLinksInfoChanged(struct wpa_supplicant *wpa_s,
 				       enum mlo_info_change_reason reason);
+	void notifyUsdPublishStarted(struct wpa_supplicant *wpa_s,
+			int cmd_id, int publish_id);
+	void notifyUsdSubscribeStarted(struct wpa_supplicant *wpa_s,
+			int cmd_id, int subscribe_id);
+	void notifyUsdPublishConfigFailed(struct wpa_supplicant *wpa_s,
+			int cmd_id, ISupplicantStaIfaceCallback::UsdConfigErrorCode error_code);
+	void notifyUsdSubscribeConfigFailed(struct wpa_supplicant *wpa_s,
+			int cmd_id, ISupplicantStaIfaceCallback::UsdConfigErrorCode error_code);
+	void notifyUsdServiceDiscovered(struct wpa_supplicant *wpa_s,
+			enum nan_service_protocol_type srv_proto_type,
+			int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+			bool fsd, const u8 *ssi, size_t ssi_len);
+	void notifyUsdPublishReplied(struct wpa_supplicant *wpa_s,
+			enum nan_service_protocol_type srv_proto_type,
+			int publish_id, int peer_subscribe_id,
+			const u8 *peer_addr, const u8 *ssi, size_t ssi_len);
+	void notifyUsdMessageReceived(struct wpa_supplicant *wpa_s, int id,
+			int peer_instance_id, const u8 *peer_addr,
+			const u8 *message, size_t message_len);
+	void notifyUsdPublishTerminated(struct wpa_supplicant *wpa_s,
+			int publish_id, enum nan_de_reason reason);
+	void notifyUsdSubscribeTerminated(struct wpa_supplicant *wpa_s,
+			int subscribe_id, enum nan_de_reason reason);
 
 	// Methods called from aidl objects.
 	int32_t isAidlServiceVersionAtLeast(int32_t expected_version);
@@ -338,10 +353,6 @@
 	WPA_KEY_MGMT_FT_PSK,
 	"KeyMgmt value mismatch");
 static_assert(
-	static_cast<uint32_t>(KeyMgmtMask::OSEN) ==
-	WPA_KEY_MGMT_OSEN,
-	"KeyMgmt value mismatch");
-static_assert(
 	static_cast<uint32_t>(KeyMgmtMask::SAE) ==
 	WPA_KEY_MGMT_SAE,
 	"KeyMgmt value mismatch");
@@ -378,10 +389,6 @@
 	WPA_PROTO_RSN,
 	"Proto value mismatch");
 static_assert(
-	static_cast<uint32_t>(ProtoMask::OSEN) ==
-	WPA_PROTO_OSEN,
-	"Proto value mismatch");
-static_assert(
 	static_cast<uint32_t>(ProtoMask::WAPI) ==
 	WPA_PROTO_WAPI,
 	"Proto value mismatch");
@@ -498,11 +505,6 @@
 	Hs20AnqpSubtypes::CONNECTION_CAPABILITY) ==
 	HS20_STYPE_CONNECTION_CAPABILITY,
 	"HS Subtype value mismatch");
-static_assert(
-	static_cast<uint32_t>(
-	Hs20AnqpSubtypes::OSU_PROVIDERS_LIST) ==
-	HS20_STYPE_OSU_PROVIDERS_LIST,
-	"HS Subtype value mismatch");
 
 static_assert(
 	static_cast<uint16_t>(
diff --git a/wpa_supplicant/aidl/vendor/p2p_iface.cpp b/wpa_supplicant/aidl/vendor/p2p_iface.cpp
index b1cd1cd..cbdf342 100644
--- a/wpa_supplicant/aidl/vendor/p2p_iface.cpp
+++ b/wpa_supplicant/aidl/vendor/p2p_iface.cpp
@@ -24,6 +24,10 @@
 }
 
 #define P2P_JOIN_LIMIT 3
+#define WPA_KEY_MGMT_P2P_MODE_WFD_PCC (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PSK)
+#define IS_P2P_MODE_WFD_PCC_KEY_MGMT(key_mgmt_mask) \
+	((key_mgmt_mask & WPA_KEY_MGMT_P2P_MODE_WFD_PCC) == WPA_KEY_MGMT_P2P_MODE_WFD_PCC)
+#define DEFAULT_QUERY_PERIOD_MS 40
 
 namespace {
 const char kConfigMethodStrPbc[] = "pbc";
@@ -40,9 +44,14 @@
 
 using aidl::android::hardware::wifi::supplicant::ISupplicantP2pIface;
 using aidl::android::hardware::wifi::supplicant::ISupplicantStaNetwork;
+using aidl::android::hardware::wifi::supplicant::KeyMgmtMask;
 using aidl::android::hardware::wifi::supplicant::MiracastMode;
 using aidl::android::hardware::wifi::supplicant::P2pFrameTypeMask;
 
+constexpr uint32_t kAllowedKeyMgmtMask =
+	(static_cast<uint32_t>(KeyMgmtMask::WPA_PSK) |
+	 static_cast<uint32_t>(KeyMgmtMask::SAE));
+
 uint8_t convertAidlMiracastModeToInternal(
 	MiracastMode mode)
 {
@@ -168,13 +177,23 @@
 	const uint8_t *group_owner_bssid,
 	const std::vector<uint8_t>& ssid,
 	const std::string& passphrase,
-	uint32_t freq)
+	uint32_t freq,
+	uint32_t key_mgmt_mask)
 {
 	int ret = 0;
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
 
+	if (key_mgmt_mask != 0 && key_mgmt_mask & ~kAllowedKeyMgmtMask) {
+		return -1;
+	}
+
+	if (key_mgmt_mask == WPA_KEY_MGMT_SAE) {
+		wpa_s->p2p2 = true;
+		wpa_s->p2p_mode = WPA_P2P_MODE_WFD_R2;
+	}
+
 	// Construct a network for adding group.
 	// Group client follows the persistent attribute of Group Owner.
 	// If joined group is persistent, it adds a persistent network on GroupStarted.
@@ -191,7 +210,8 @@
 	if (wpas_p2p_group_add_persistent(
 		wpa_s, wpa_network, 0, 0, freq, 0, ht40, vht,
 		CONF_OPER_CHWIDTH_USE_HT, he, 0, NULL, 0, 0, is6GhzAllowed(wpa_s),
-		P2P_JOIN_LIMIT, isAnyEtherAddr(group_owner_bssid) ? NULL : group_owner_bssid)) {
+		P2P_JOIN_LIMIT, isAnyEtherAddr(group_owner_bssid) ? NULL : group_owner_bssid,
+		NULL, NULL, NULL, 0)) {
 		ret = -1;
 	}
 
@@ -400,7 +420,7 @@
 		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
 		&P2pIface::connectInternal, _aidl_return, in_peerAddress,
 		in_provisionMethod, in_preSelectedPin, in_joinExistingGroup,
-		in_persistent, in_goIntent);
+		in_persistent, in_goIntent, 0, "", 0, false, "");
 }
 
 ::ndk::ScopedAStatus P2pIface::cancelConnect()
@@ -417,7 +437,7 @@
 	return validateAndCall(
 		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
 		&P2pIface::provisionDiscoveryInternal, in_peerAddress,
-		in_provisionMethod);
+		in_provisionMethod, 0);
 }
 
 ndk::ScopedAStatus P2pIface::addGroup(
@@ -426,7 +446,7 @@
 	return validateAndCall(
 		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
 		&P2pIface::addGroupInternal, in_persistent,
-		in_persistentNetworkId);
+		in_persistentNetworkId, false);
 }
 
 ::ndk::ScopedAStatus P2pIface::addGroupWithConfig(
@@ -439,7 +459,8 @@
 		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
 		&P2pIface::addGroupWithConfigInternal, in_ssid,
 		in_pskPassphrase, in_persistent, in_freq,
-		in_peerAddress, in_joinExistingGroup);
+		in_peerAddress, in_joinExistingGroup,
+		static_cast<uint32_t>(KeyMgmtMask::WPA_PSK));
 }
 
 ::ndk::ScopedAStatus P2pIface::removeGroup(
@@ -475,8 +496,8 @@
 {
 	return validateAndCall(
 		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
-		&P2pIface::reinvokeInternal, in_persistentNetworkId,
-		in_peerAddress);
+		&P2pIface::reinvokeInternal, in_peerAddress, in_persistentNetworkId,
+		-1);
 }
 
 ::ndk::ScopedAStatus P2pIface::configureExtListen(
@@ -1098,13 +1119,38 @@
 	return ndk::ScopedAStatus::ok();
 }
 
+u16 convertAidlPairingBootstrappingMethodToWpa(uint32_t pairing_bootstrapping_method)
+{
+	switch (pairing_bootstrapping_method) {
+		case P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_OPPORTUNISTIC:
+			return P2P_PBMA_OPPORTUNISTIC;
+		case P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_DISPLAY_PINCODE:
+			return P2P_PBMA_PIN_CODE_DISPLAY;
+		case P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_DISPLAY_PASSPHRASE:
+			return P2P_PBMA_PASSPHRASE_DISPLAY;
+		case P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_KEYPAD_PINCODE:
+			return P2P_PBMA_PIN_CODE_KEYPAD;
+		case P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_KEYPAD_PASSPHRASE:
+			return P2P_PBMA_PASSPHRASE_KEYPAD;
+		case P2pPairingBootstrappingMethodMask::BOOTSTRAPPING_OUT_OF_BAND:
+			return P2P_PBMA_HANDSHAKE_SKIP;
+		default:
+			return 0;
+	}
+}
+
 // This method only implements support for subset (needed by Android framework)
 // of parameters that can be specified for connect.
 std::pair<std::string, ndk::ScopedAStatus> P2pIface::connectInternal(
 	const std::vector<uint8_t>& peer_address,
 	WpsProvisionMethod provision_method,
 	const std::string& pre_selected_pin, bool join_existing_group,
-	bool persistent, uint32_t go_intent)
+	bool persistent, uint32_t go_intent,
+	uint32_t pairing_bootstrapping_method,
+	const std::string& password,
+	uint32_t frequency,
+	bool authorize,
+	const std::string& group_ifname)
 {
 	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
 	if (go_intent > 15) {
@@ -1129,6 +1175,28 @@
 		wps_method = WPS_NOT_READY;
 		break;
 	}
+
+	bool p2p2 = false;
+	u16 bootstrap = 0;
+	const char* pairing_password = nullptr;
+	bool skip_prov = false;
+	int auto_join = 0;
+	if (wps_method == WPS_NOT_READY) {
+		bootstrap = convertAidlPairingBootstrappingMethodToWpa(
+			pairing_bootstrapping_method);
+		if (bootstrap == 0) {
+			return {"", createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
+		}
+		if (bootstrap == P2P_PBMA_HANDSHAKE_SKIP) {
+			skip_prov = true;
+		}
+		p2p2 = true;
+		pairing_password = password.length() > 0 ? password.data() : nullptr;
+		if (authorize) {
+			auto_join = 1;
+		}
+	}
+
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
@@ -1136,10 +1204,10 @@
 	const char* pin =
 		pre_selected_pin.length() > 0 ? pre_selected_pin.data() : nullptr;
 	int new_pin = wpas_p2p_connect(
-		wpa_s, peer_address.data(), pin, wps_method, persistent, false,
-		join_existing_group, false, go_intent_signed, 0, 0, -1, false, ht40,
+		wpa_s, peer_address.data(), pin, wps_method, persistent, auto_join,
+		join_existing_group, authorize, go_intent_signed, 0, 0, -1, false, ht40,
 		vht, CONF_OPER_CHWIDTH_USE_HT, he, edmg, nullptr, 0, is6GhzAllowed(wpa_s),
-		false, 0, NULL);
+		p2p2, bootstrap, pairing_password, skip_prov);
 	if (new_pin < 0) {
 		return {"", createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
 	}
@@ -1167,10 +1235,12 @@
 
 ndk::ScopedAStatus P2pIface::provisionDiscoveryInternal(
 	const std::vector<uint8_t>& peer_address,
-	WpsProvisionMethod provision_method)
+	WpsProvisionMethod provision_method,
+	uint32_t pairing_bootstrapping_method)
 {
 	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
 	p2ps_provision* prov_param;
+	u16 bootstrap = 0;
 	const char* config_method_str = nullptr;
 	if (peer_address.size() != ETH_ALEN) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
@@ -1185,13 +1255,18 @@
 	case WpsProvisionMethod::KEYPAD:
 		config_method_str = kConfigMethodStrKeypad;
 		break;
-	// TODO Handle pairing bootstrapping method when supplicant implementation is ready
 	case WpsProvisionMethod::NONE:
 		config_method_str = kConfigMethodStrNone;
 		break;
 	}
+	if (provision_method == WpsProvisionMethod::NONE) {
+		bootstrap = convertAidlPairingBootstrappingMethodToWpa(pairing_bootstrapping_method);
+		if (bootstrap == 0) {
+			return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+		}
+	}
 	if (wpas_p2p_prov_disc(
-		wpa_s, peer_address.data(), config_method_str,
+		wpa_s, peer_address.data(), config_method_str, bootstrap,
 		WPAS_P2P_PD_FOR_GO_NEG, nullptr)) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
@@ -1245,25 +1320,29 @@
 }
 
 ndk::ScopedAStatus P2pIface::reinvokeInternal(
+	const std::vector<uint8_t>& peer_address,
 	int32_t persistent_network_id,
-	const std::vector<uint8_t>& peer_address)
+	int32_t device_identity_entry_id)
 {
 	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	struct wpa_ssid* ssid = NULL;
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
 	int edmg = wpa_s->conf->p2p_go_edmg;
-	struct wpa_ssid* ssid =
-		wpa_config_get_network(wpa_s->conf, persistent_network_id);
-	if (ssid == NULL || ssid->disabled != 2) {
-		return createStatus(SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN);
-	}
+	bool p2p2 = device_identity_entry_id >= 0 && persistent_network_id == -1;
 	if (peer_address.size() != ETH_ALEN) {
 		return {createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
 	}
+	if (!p2p2) {
+		ssid = wpa_config_get_network(wpa_s->conf, persistent_network_id);
+		if (ssid == NULL || ssid->disabled != 2) {
+			return createStatus(SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN);
+		}
+	}
 	if (wpas_p2p_invite(
 		wpa_s, peer_address.data(), ssid, NULL, 0, 0, ht40, vht,
-		CONF_OPER_CHWIDTH_USE_HT, 0, he, edmg, is6GhzAllowed(wpa_s))) {
+		CONF_OPER_CHWIDTH_USE_HT, 0, he, edmg, is6GhzAllowed(wpa_s), p2p2)) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
 	return ndk::ScopedAStatus::ok();
@@ -1714,19 +1793,21 @@
 }
 
 ndk::ScopedAStatus P2pIface::addGroupInternal(
-	bool persistent, int32_t persistent_network_id)
+	bool persistent, int32_t persistent_network_id, bool p2p2)
 {
 	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
 	int edmg = wpa_s->conf->p2p_go_edmg;
+	enum wpa_p2p_mode p2p_mode = p2p2 ? WPA_P2P_MODE_WFD_R2 : WPA_P2P_MODE_WFD_R1;
 	struct wpa_ssid* ssid =
 		wpa_config_get_network(wpa_s->conf, persistent_network_id);
 	if (ssid == NULL) {
 		if (wpas_p2p_group_add(
 			wpa_s, persistent, 0, 0, ht40, vht,
-			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s))) {
+			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s),
+			p2p2, p2p_mode)) {
 			return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 		} else {
 			return ndk::ScopedAStatus::ok();
@@ -1735,7 +1816,8 @@
 		if (wpas_p2p_group_add_persistent(
 			wpa_s, ssid, 0, 0, 0, 0, ht40, vht,
 			CONF_OPER_CHWIDTH_USE_HT, he, edmg, NULL, 0, 0,
-			is6GhzAllowed(wpa_s), 0, NULL)) {
+			is6GhzAllowed(wpa_s), 0, NULL,
+			NULL, NULL, NULL, 0)) {
 			return createStatus(SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN);
 		} else {
 			return ndk::ScopedAStatus::ok();
@@ -1744,10 +1826,21 @@
 	return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 }
 
+enum wpa_p2p_mode convertAidlKeyMgmtMaskToWpaP2pMode(uint32_t key_mgmt_mask)
+{
+	if (IS_P2P_MODE_WFD_PCC_KEY_MGMT(key_mgmt_mask)) {
+		return WPA_P2P_MODE_WFD_PCC;
+	} else if (wpa_key_mgmt_sae(key_mgmt_mask)) {
+		return WPA_P2P_MODE_WFD_R2;
+	} else {
+		return WPA_P2P_MODE_WFD_R1;
+	}
+}
+
 ndk::ScopedAStatus P2pIface::addGroupWithConfigInternal(
 	const std::vector<uint8_t>& ssid, const std::string& passphrase,
 	bool persistent, uint32_t freq, const std::vector<uint8_t>& peer_address,
-	bool joinExistingGroup)
+	bool joinExistingGroup, uint32_t key_mgmt_mask)
 {
 	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
 	int he = wpa_s->conf->p2p_go_he;
@@ -1769,10 +1862,17 @@
 			"Passphrase is invalid.");
 	}
 
+	if (key_mgmt_mask != 0 && key_mgmt_mask & ~kAllowedKeyMgmtMask) {
+		return createStatusWithMsg(SupplicantStatusCode::FAILURE_ARGS_INVALID,
+			"Unknown key_mgmt_mask");
+	}
+
+	enum wpa_p2p_mode p2p_mode = convertAidlKeyMgmtMaskToWpaP2pMode(key_mgmt_mask);
+
 	wpa_printf(MSG_DEBUG,
-		    "P2P: Add group with config Role: %s network name: %s freq: %d",
+		    "P2P: Add group with config Role: %s network name: %s freq: %d p2p_mode: %d",
 		    joinExistingGroup ? "CLIENT" : "GO",
-		    wpa_ssid_txt(ssid.data(), ssid.size()), freq);
+		    wpa_ssid_txt(ssid.data(), ssid.size()), freq, p2p_mode);
 	if (!joinExistingGroup) {
 		struct p2p_data *p2p = wpa_s->global->p2p;
 		os_memcpy(p2p->ssid, ssid.data(), ssid.size());
@@ -1785,7 +1885,8 @@
 
 		if (wpas_p2p_group_add(
 			wpa_s, persistent, freq, 0, ht40, vht,
-			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s))) {
+			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s),
+			false, p2p_mode)) {
 			return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 		}
 		return ndk::ScopedAStatus::ok();
@@ -1798,7 +1899,7 @@
 		return createStatusWithMsg(SupplicantStatusCode::FAILURE_ARGS_INVALID,
 			"Peer address is invalid.");
 	}
-	if (joinGroup(wpa_s, peer_address.data(), ssid, passphrase, freq)) {
+	if (joinGroup(wpa_s, peer_address.data(), ssid, passphrase, freq, key_mgmt_mask)) {
 		return createStatusWithMsg(SupplicantStatusCode::FAILURE_UNKNOWN,
 			"Failed to start scan.");
 	}
@@ -1979,7 +2080,12 @@
 		connectInfo.peerAddress.begin(), connectInfo.peerAddress.end()};
 	return connectInternal(peerAddressVec, connectInfo.provisionMethod,
 		connectInfo.preSelectedPin, connectInfo.joinExistingGroup,
-		connectInfo.persistent, connectInfo.goIntent);
+		connectInfo.persistent, connectInfo.goIntent,
+		connectInfo.pairingBootstrappingMethod,
+		connectInfo.password.has_value() ? connectInfo.password.value() : "",
+		connectInfo.frequencyMHz, connectInfo.authorizeConnectionFromPeer,
+		connectInfo.groupInterfaceName.has_value() ?
+			connectInfo.groupInterfaceName.value() : "");
 }
 
 ndk::ScopedAStatus P2pIface::findWithParamsInternal(const P2pDiscoveryInfo& discoveryInfo)
@@ -2015,78 +2121,228 @@
 		groupConfigurationParams.ssid, groupConfigurationParams.passphrase,
 		groupConfigurationParams.isPersistent, groupConfigurationParams.frequencyMHzOrBand,
 		goInterfaceAddressVec,
-		groupConfigurationParams.joinExistingGroup);
+		groupConfigurationParams.joinExistingGroup,
+		groupConfigurationParams.keyMgmtMask);
 }
 
 ndk::ScopedAStatus P2pIface::createGroupOwnerInternal(
 	const P2pCreateGroupOwnerInfo& groupOwnerInfo)
 {
 	return addGroupInternal(
-		groupOwnerInfo.persistent, groupOwnerInfo.persistentNetworkId);
+		groupOwnerInfo.persistent, groupOwnerInfo.persistentNetworkId,
+		groupOwnerInfo.isP2pV2);
 }
 
 std::pair<int64_t, ndk::ScopedAStatus> P2pIface::getFeatureSetInternal()
 {
-	// TODO Fill the field when supplicant implementation is ready
+	// By default, core supplicant enable WFD R2 and PCC mode for all drivers.
+	// TODO Enable this code once core supplicant implement the configuration flag
+	// to enable/disable the feature for all driver implementations.
+#if 0
+	int64_t featureSet = 0;
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_P2P_FEATURE_V2) {
+		featureSet |= ISupplicantP2pIface::P2P_FEATURE_V2;
+	}
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE) {
+		featureSet |= ISupplicantP2pIface::P2P_FEATURE_PCC_MODE_WPA3_COMPATIBILITY;
+	}
+	return {featureSet, ndk::ScopedAStatus::ok()};
+#else
 	return {0, ndk::ScopedAStatus::ok()};
+#endif
 }
 
 std::pair<uint32_t, ndk::ScopedAStatus>
 P2pIface::startUsdBasedServiceDiscoveryInternal(
 	const P2pUsdBasedServiceDiscoveryConfig& serviceDiscoveryConfig)
 {
-	// TODO Fill the field when supplicant implementation is ready
-	return {0, ndk::ScopedAStatus::ok()};
+#ifdef CONFIG_NAN_USD
+	int sessionId;
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	struct nan_subscribe_params params;
+	struct wpabuf *service_specific_info = NULL;
+
+	os_memset(&params, 0, sizeof(params));
+
+	if (serviceDiscoveryConfig.serviceSpecificInfo.size() > 0) {
+		auto ssiBuffer = misc_utils::convertVectorToWpaBuf(
+			serviceDiscoveryConfig.serviceSpecificInfo);
+		if (ssiBuffer && ssiBuffer.get() != nullptr) {
+			service_specific_info = ssiBuffer.get();
+		}
+	}
+
+	params.active = true;
+	params.ttl = serviceDiscoveryConfig.timeoutInSeconds;
+	params.query_period = DEFAULT_QUERY_PERIOD_MS;
+	if (serviceDiscoveryConfig.bandMask != 0) {
+		// TODO convert band to channel instead of scanning all channel frequencies.
+		params.freq_list = wpas_nan_usd_all_freqs(wpa_s);
+	} else {
+		if (serviceDiscoveryConfig.frequencyListMhz.size() != 0) {
+			params.freq_list = serviceDiscoveryConfig.frequencyListMhz.data();
+		} else {
+			params.freq = NAN_USD_DEFAULT_FREQ;
+		}
+	}
+	sessionId = wpas_nan_usd_subscribe(wpa_s, serviceDiscoveryConfig.serviceName.c_str(),
+					      (enum nan_service_protocol_type)
+						  serviceDiscoveryConfig.serviceProtocolType,
+						  service_specific_info, &params, true);
+	if (service_specific_info != NULL) {
+		freeWpaBuf(service_specific_info);
+	}
+	if (sessionId > 0) {
+		return {sessionId, ndk::ScopedAStatus::ok()};
+	}
+	return {-1, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
+#else
+	return {-1, createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED)};
+#endif
 }
 
 ndk::ScopedAStatus P2pIface::stopUsdBasedServiceDiscoveryInternal(
 	uint32_t sessionId)
 {
-	// TODO Fill the field when supplicant implementation is ready
+#ifdef CONFIG_NAN_USD
+	wpas_nan_usd_cancel_subscribe(retrieveIfacePtr(), sessionId);
 	return ndk::ScopedAStatus::ok();
+#else
+	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+#endif
 }
 
 std::pair<uint32_t, ndk::ScopedAStatus>
 P2pIface::startUsdBasedServiceAdvertisementInternal(
 	const P2pUsdBasedServiceAdvertisementConfig& serviceAdvertisementConfig)
 {
-	// TODO Fill the field when supplicant implementation is ready
-	return {0, ndk::ScopedAStatus::ok()};
+#ifdef CONFIG_NAN_USD
+	int sessionId;
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	struct nan_publish_params params;
+	struct wpabuf *service_specific_info = NULL;
+	os_memset(&params, 0, sizeof(params));
+
+	if (serviceAdvertisementConfig.serviceSpecificInfo.size() > 0) {
+		auto ssiBuffer = misc_utils::convertVectorToWpaBuf(
+			serviceAdvertisementConfig.serviceSpecificInfo);
+		if (ssiBuffer && ssiBuffer.get() != nullptr) {
+			service_specific_info = ssiBuffer.get();
+		}
+	}
+
+	params.solicited = true;
+	params.ttl = serviceAdvertisementConfig.timeoutInSeconds;
+	params.freq = serviceAdvertisementConfig.frequencyMHz;
+	sessionId = wpas_nan_usd_publish(wpa_s, serviceAdvertisementConfig.serviceName.c_str(),
+					      (enum nan_service_protocol_type)
+						  serviceAdvertisementConfig.serviceProtocolType,
+						  service_specific_info, &params, true);
+	if (service_specific_info != NULL) {
+		freeWpaBuf(service_specific_info);
+	}
+	if (sessionId > 0) {
+		return {sessionId, ndk::ScopedAStatus::ok()};
+	}
+	return {-1, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
+#else
+	return {-1, createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED)};
+#endif
 }
 
 ndk::ScopedAStatus P2pIface::stopUsdBasedServiceAdvertisementInternal(
 	uint32_t sessionId)
 {
-	// TODO Fill the field when supplicant implementation is ready
+#ifdef CONFIG_NAN_USD
+	wpas_nan_usd_cancel_publish(retrieveIfacePtr(), sessionId);
 	return ndk::ScopedAStatus::ok();
+#else
+	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+#endif
 }
 
 ndk::ScopedAStatus P2pIface::provisionDiscoveryWithParamsInternal(
 	const P2pProvisionDiscoveryParams& params)
 {
-	// TODO Fill the field when supplicant implementation is ready
-	return ndk::ScopedAStatus::ok();
+	std::vector<uint8_t> peerMacAddressVec {
+		params.peerMacAddress.begin(),
+		params.peerMacAddress.end()};
+	return provisionDiscoveryInternal(peerMacAddressVec, params.provisionMethod,
+		params.pairingBootstrappingMethod);
 }
 
 std::pair<P2pDirInfo, ndk::ScopedAStatus> P2pIface::getDirInfoInternal()
 {
-	// TODO Fill the field when supplicant implementation is ready
 	P2pDirInfo dirInfo = {};
+	char dir_info_buffer[100] = {0};
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	int id = wpas_p2p_get_dira(wpa_s, dir_info_buffer, sizeof(dir_info_buffer));
+	if (id < 0) {
+		return {dirInfo, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
+	}
+
+	std::istringstream dir_info_ss(dir_info_buffer);
+	std::string mac_str, nonce_str, dir_tag_str;
+	std::vector<uint8_t> mac_addr = std::vector<uint8_t>(ETH_ALEN);
+	std::vector<uint8_t> nonce = std::vector<uint8_t>(DEVICE_IDENTITY_NONCE_LEN);
+	std::vector<uint8_t> dir_tag = std::vector<uint8_t>(DEVICE_IDENTITY_TAG_LEN);
+	// Extract MAC address
+	std::getline(dir_info_ss, mac_str, ' ');
+	if (hexstr2bin(mac_str.c_str(), mac_addr.data(), ETH_ALEN)) {
+		wpa_printf(MSG_ERROR, "Failed to get MAC address from DIR Info");
+		return {dirInfo, createStatusWithMsg(SupplicantStatusCode::FAILURE_UNKNOWN,
+                    "Failed to parse MAC address.")};
+	}
+	// Extract nonce
+	std::getline(dir_info_ss, nonce_str, ' ');
+	if (hexstr2bin(nonce_str.c_str(), nonce.data(), DEVICE_IDENTITY_NONCE_LEN)) {
+		return {dirInfo, createStatusWithMsg(SupplicantStatusCode::FAILURE_UNKNOWN,
+                    "Failed to parse nonce.")};
+	}
+	// Extract dir tag
+	std::getline(dir_info_ss, dir_tag_str, ' ');
+	if (hexstr2bin(dir_tag_str.c_str(), dir_tag.data(), DEVICE_IDENTITY_TAG_LEN)) {
+		return {dirInfo, createStatusWithMsg(SupplicantStatusCode::FAILURE_UNKNOWN,
+                    "Failed to parse dir tag.")};
+	}
+
+	std::copy_n(mac_addr.begin(), ETH_ALEN, dirInfo.deviceInterfaceMacAddress.begin());
+	dirInfo.nonce = nonce;
+	dirInfo.dirTag = dir_tag;
+	dirInfo.cipherVersion = P2pDirInfo::CipherVersion::DIRA_CIPHER_VERSION_128_BIT;
+
 	return {dirInfo, ndk::ScopedAStatus::ok()};
 }
 
 std::pair<int32_t, ndk::ScopedAStatus> P2pIface::validateDirInfoInternal(
 	const P2pDirInfo& dirInfo)
 {
-	// TODO Fill the field when supplicant implementation is ready
-	return {0, ndk::ScopedAStatus::ok()};
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (dirInfo.cipherVersion != P2pDirInfo::CipherVersion::DIRA_CIPHER_VERSION_128_BIT ||
+		dirInfo.deviceInterfaceMacAddress.size() != ETH_ALEN ||
+		dirInfo.nonce.size() != DEVICE_IDENTITY_NONCE_LEN ||
+		dirInfo.dirTag.size() != DEVICE_IDENTITY_TAG_LEN) {
+		return {-1, createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID)};
+	}
+
+	int32_t id = wpas_p2p_validate_dira(wpa_s, dirInfo.deviceInterfaceMacAddress.data(),
+			DIRA_CIPHER_VERSION_128, dirInfo.nonce.data(), dirInfo.dirTag.data());
+	if (id >= 0) {
+		return {id, ndk::ScopedAStatus::ok()};
+	}
+	return {-1, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
 }
 
 ndk::ScopedAStatus P2pIface::reinvokePersistentGroupInternal(
 	const P2pReinvokePersistentGroupParams& reinvokeGroupParams)
 {
-	// TODO Fill the field when supplicant implementation is ready
-	return ndk::ScopedAStatus::ok();
+	std::vector<uint8_t> peerMacAddressVec {
+		reinvokeGroupParams.peerMacAddress.begin(),
+		reinvokeGroupParams.peerMacAddress.end()};
+	return reinvokeInternal(peerMacAddressVec, reinvokeGroupParams.persistentNetworkId,
+		reinvokeGroupParams.deviceIdentityEntryId);
 }
 
 /**
diff --git a/wpa_supplicant/aidl/vendor/p2p_iface.h b/wpa_supplicant/aidl/vendor/p2p_iface.h
index 545a6c9..9bc7874 100644
--- a/wpa_supplicant/aidl/vendor/p2p_iface.h
+++ b/wpa_supplicant/aidl/vendor/p2p_iface.h
@@ -19,6 +19,7 @@
 #include <aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.h>
 #include <aidl/android/hardware/wifi/supplicant/ISupplicantP2pNetwork.h>
 #include <aidl/android/hardware/wifi/supplicant/MiracastMode.h>
+#include <aidl/android/hardware/wifi/supplicant/P2pPairingBootstrappingMethodMask.h>
 #include <aidl/android/hardware/wifi/supplicant/WpsProvisionMethod.h>
 
 extern "C"
@@ -230,16 +231,20 @@
 		const std::vector<uint8_t>& peer_address,
 		WpsProvisionMethod provision_method,
 		const std::string& pre_selected_pin, bool join_existing_group,
-		bool persistent, uint32_t go_intent);
+		bool persistent, uint32_t go_intent,
+		uint32_t pairing_bootstrapping_method, const std::string& password,
+		uint32_t frequency, bool authorize, const std::string& group_ifname);
 	ndk::ScopedAStatus cancelConnectInternal();
 	ndk::ScopedAStatus provisionDiscoveryInternal(
 		const std::vector<uint8_t>& peer_address,
-		WpsProvisionMethod provision_method);
-	ndk::ScopedAStatus addGroupInternal(bool in_persistent, int32_t in_persistentNetworkId);
+		WpsProvisionMethod provision_method,
+		uint32_t pairingBootstrappingMethod);
+	ndk::ScopedAStatus addGroupInternal(bool in_persistent, int32_t in_persistentNetworkId,
+		bool isP2pV2);
 	ndk::ScopedAStatus addGroupWithConfigInternal(
 		const std::vector<uint8_t>& ssid, const std::string& passphrase,
 		bool persistent, uint32_t freq, const std::vector<uint8_t>& peer_address,
-		bool joinExistingGroup);
+		bool joinExistingGroup, uint32_t key_mgmt_mask);
 	ndk::ScopedAStatus removeGroupInternal(const std::string& group_ifname);
 	ndk::ScopedAStatus rejectInternal(
 		const std::vector<uint8_t>& peer_address);
@@ -248,8 +253,9 @@
 		const std::vector<uint8_t>& go_device_address,
 		const std::vector<uint8_t>& peer_address);
 	ndk::ScopedAStatus reinvokeInternal(
+		const std::vector<uint8_t>& peer_address,
 		int32_t persistent_network_id,
-		const std::vector<uint8_t>& peer_address);
+		int32_t device_identity_entry_id);
 	ndk::ScopedAStatus configureExtListenInternal(
 		uint32_t period_in_millis, uint32_t interval_in_millis);
 	ndk::ScopedAStatus setListenChannelInternal(
diff --git a/wpa_supplicant/aidl/vendor/sta_iface.cpp b/wpa_supplicant/aidl/vendor/sta_iface.cpp
index 1a6ae08..3fe0030 100644
--- a/wpa_supplicant/aidl/vendor/sta_iface.cpp
+++ b/wpa_supplicant/aidl/vendor/sta_iface.cpp
@@ -23,6 +23,7 @@
 #include "dpp_supplicant.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/pmksa_cache.h"
+#include "src/common/nan_de.h"
 }
 
 namespace {
@@ -38,6 +39,12 @@
 using aidl::android::hardware::wifi::supplicant::LegacyMode;
 using aidl::android::hardware::wifi::supplicant::RxFilterType;
 using aidl::android::hardware::wifi::supplicant::SupplicantStatusCode;
+using aidl::android::hardware::wifi::supplicant::UsdBaseConfig;
+using aidl::android::hardware::wifi::supplicant::UsdCapabilities;
+using aidl::android::hardware::wifi::supplicant::UsdPublishConfig;
+using aidl::android::hardware::wifi::supplicant::UsdPublishTransmissionType;
+using aidl::android::hardware::wifi::supplicant::UsdServiceProtoType;
+using aidl::android::hardware::wifi::supplicant::UsdSubscribeConfig;
 using aidl::android::hardware::wifi::supplicant::WifiChannelWidthInMhz;
 using aidl::android::hardware::wifi::supplicant::WifiTechnology;
 using aidl::android::hardware::wifi::supplicant::misc_utils::createStatus;
@@ -58,6 +65,24 @@
 	static_cast<uint32_t>(ISupplicant::EXT_RADIO_WORK_TIMEOUT_IN_SECS);
 constexpr char kExtRadioWorkNamePrefix[] = "ext:";
 
+#ifdef CONFIG_NAN_USD
+constexpr bool kIsUsdPublisherSupported = true;
+constexpr bool kIsUsdSubscriberSupported = true;
+constexpr int32_t kMaxUsdLocalSsiLengthBytes = 1400;
+constexpr int32_t kMaxUsdServiceNameLengthBytes = 255;
+constexpr int32_t kMaxUsdMatchFilterLengthBytes = 0;
+constexpr int32_t kMaxNumUsdPublishSessions = NAN_DE_MAX_SERVICE;
+constexpr int32_t kMaxNumUsdSubscribeSessions = NAN_DE_MAX_SERVICE;
+#else
+constexpr bool kIsUsdPublisherSupported = false;
+constexpr bool kIsUsdSubscriberSupported = false;
+constexpr int32_t kMaxUsdLocalSsiLengthBytes = 0;
+constexpr int32_t kMaxUsdServiceNameLengthBytes = 0;
+constexpr int32_t kMaxUsdMatchFilterLengthBytes = 0;
+constexpr int32_t kMaxNumUsdPublishSessions = 0;
+constexpr int32_t kMaxNumUsdSubscribeSessions = 0;
+#endif
+
 uint8_t convertAidlRxFilterTypeToInternal(
 	RxFilterType type)
 {
@@ -84,6 +109,21 @@
 	WPA_ASSERT(false);
 }
 
+nan_service_protocol_type convertAidlServiceProtoTypeToInternal(
+	UsdServiceProtoType type)
+{
+	switch (type) {
+	case UsdServiceProtoType::GENERIC:
+		return NAN_SRV_PROTO_GENERIC;
+	case UsdServiceProtoType::CSA_MATTER:
+		return NAN_SRV_PROTO_CSA_MATTER;
+	default:
+		// Default case is not expected, due
+		// to the USD validation method
+		return NAN_SRV_PROTO_GENERIC;
+	};
+}
+
 ndk::ScopedAStatus doZeroArgDriverCommand(
 	struct wpa_supplicant *wpa_s, const char *cmd)
 {
@@ -284,6 +324,120 @@
 	return arr;
 }
 
+template <typename T>
+inline bool checkContainerSize(const T& container, int maxSize) {
+  return container.size() <= maxSize;
+}
+
+template <typename T>
+inline bool isValidEnumValue(T value, T enumRangeMin, T enumRangeMax) {
+	return static_cast<uint32_t>(value) >= static_cast<uint32_t>(enumRangeMin)
+		&& static_cast<uint32_t>(value) <= static_cast<uint32_t>(enumRangeMax);
+}
+
+bool validateUsdBaseConfig(UsdBaseConfig baseConfig) {
+	if (!isValidEnumValue(baseConfig.serviceProtoType,
+			UsdServiceProtoType::GENERIC, UsdServiceProtoType::CSA_MATTER)) {
+		wpa_printf(MSG_ERROR, "Unknown protocol type received: %d",
+			static_cast<int>(baseConfig.serviceProtoType));
+		return false;
+	}
+	if (!checkContainerSize(baseConfig.serviceName, kMaxUsdServiceNameLengthBytes)) {
+		wpa_printf(MSG_ERROR, "Service name of size %zu exceeds the supported size of %d",
+			baseConfig.serviceName.size(), kMaxUsdServiceNameLengthBytes);
+		return false;
+	}
+	if (!checkContainerSize(baseConfig.serviceSpecificInfo, kMaxUsdLocalSsiLengthBytes)) {
+		wpa_printf(MSG_ERROR, "Service specific info of size %zu exceeds"
+			" the supported size of %d", baseConfig.serviceSpecificInfo.size(),
+			kMaxUsdLocalSsiLengthBytes);
+		return false;
+	}
+	if (baseConfig.txMatchFilter.has_value() && !checkContainerSize(
+			baseConfig.txMatchFilter.value(), kMaxUsdMatchFilterLengthBytes)) {
+		wpa_printf(MSG_ERROR, "TX match filter of size %zu exceeds"
+			" the supported size of %d", baseConfig.txMatchFilter.value().size(),
+			kMaxUsdMatchFilterLengthBytes);
+		return false;
+	}
+	if (baseConfig.rxMatchFilter.has_value() && !checkContainerSize(
+			baseConfig.rxMatchFilter.value(), kMaxUsdMatchFilterLengthBytes)) {
+		wpa_printf(MSG_ERROR, "RX match filter of size %zu exceeds"
+			" the supported size of %d", baseConfig.rxMatchFilter.value().size(),
+			kMaxUsdMatchFilterLengthBytes);
+		return false;
+	}
+	return true;
+}
+
+bool validateUsdPublishConfig(UsdPublishConfig publishConfig) {
+	if (!validateUsdBaseConfig(publishConfig.usdBaseConfig)) {
+		return false;
+	}
+	if (!isValidEnumValue(publishConfig.publishType,
+			UsdPublishConfig::PublishType::SOLICITED_ONLY,
+			UsdPublishConfig::PublishType::SOLICITED_AND_UNSOLICITED)) {
+		wpa_printf(MSG_ERROR, "Unknown publish type received: %d",
+			static_cast<int>(publishConfig.publishType));
+		return false;
+	}
+	if (!isValidEnumValue(publishConfig.transmissionType,
+			UsdPublishTransmissionType::UNICAST,
+			UsdPublishTransmissionType::MULTICAST)) {
+		wpa_printf(MSG_ERROR, "Unknown transmission type received: %d",
+			static_cast<int>(publishConfig.transmissionType));
+		return false;
+	}
+	return true;
+}
+
+bool validateUsdSubscribeConfig(UsdSubscribeConfig subscribeConfig) {
+	if (!validateUsdBaseConfig(subscribeConfig.usdBaseConfig)) {
+		return false;
+	}
+	if (!isValidEnumValue(subscribeConfig.subscribeType,
+			UsdSubscribeConfig::SubscribeType::PASSIVE_MODE,
+			UsdSubscribeConfig::SubscribeType::ACTIVE_MODE)) {
+		wpa_printf(MSG_ERROR, "Unknown subscribe type received: %d",
+			static_cast<int>(subscribeConfig.subscribeType));
+		return false;
+	}
+	return true;
+}
+
+struct nan_publish_params convertAidlNanPublishParamsToInternal(UsdPublishConfig publishConfig) {
+	struct nan_publish_params nanPublishParams;
+	nanPublishParams.unsolicited =
+		publishConfig.publishType == UsdPublishConfig::PublishType::UNSOLICITED_ONLY
+			|| publishConfig.publishType == UsdPublishConfig::PublishType::SOLICITED_AND_UNSOLICITED;
+	nanPublishParams.solicited =
+		publishConfig.publishType == UsdPublishConfig::PublishType::SOLICITED_ONLY
+			|| publishConfig.publishType == UsdPublishConfig::PublishType::SOLICITED_AND_UNSOLICITED;
+	nanPublishParams.solicited_multicast = nanPublishParams.solicited &&
+		publishConfig.transmissionType == UsdPublishTransmissionType::MULTICAST;
+	nanPublishParams.ttl = publishConfig.usdBaseConfig.ttlSec;
+	nanPublishParams.fsd = publishConfig.isFsd;
+	nanPublishParams.freq = publishConfig.usdBaseConfig.defaultFreqMhz;
+	nanPublishParams.announcement_period = publishConfig.announcementPeriodMillis;
+	nanPublishParams.disable_events = !publishConfig.eventsEnabled;
+	// Pass the original pointer to the freq list, since the receiver will memcpy the data
+	nanPublishParams.freq_list = publishConfig.usdBaseConfig.freqsMhz.data();
+	return nanPublishParams;
+}
+
+struct nan_subscribe_params convertAidlNanSubscribeParamsToInternal(
+		UsdSubscribeConfig subscribeConfig) {
+	struct nan_subscribe_params nanSubscribeParams;
+	nanSubscribeParams.active =
+		subscribeConfig.subscribeType == UsdSubscribeConfig::SubscribeType::ACTIVE_MODE;
+	nanSubscribeParams.ttl = subscribeConfig.usdBaseConfig.ttlSec;
+	nanSubscribeParams.freq = subscribeConfig.usdBaseConfig.defaultFreqMhz;
+	nanSubscribeParams.query_period = subscribeConfig.queryPeriodMillis;
+	// Pass the original pointer to the freq list, since the receiver will memcpy the data
+	nanSubscribeParams.freq_list = subscribeConfig.usdBaseConfig.freqsMhz.data();
+	return nanSubscribeParams;
+}
+
 }  // namespace
 
 namespace aidl {
@@ -855,7 +1009,7 @@
 {
 	return validateAndCall(
 		this, SupplicantStatusCode::FAILURE_UNKNOWN,
-		&StaIface::startUsdPublishInternal, in_usdPublishConfig);
+		&StaIface::startUsdPublishInternal, in_cmdId, in_usdPublishConfig);
 }
 
 ::ndk::ScopedAStatus StaIface::startUsdSubscribe(int32_t in_cmdId,
@@ -863,7 +1017,7 @@
 {
 	return validateAndCall(
 		this, SupplicantStatusCode::FAILURE_UNKNOWN,
-		&StaIface::startUsdSubscribeInternal, in_usdSubscribeConfig);
+		&StaIface::startUsdSubscribeInternal, in_cmdId, in_usdSubscribeConfig);
 }
 
 ::ndk::ScopedAStatus StaIface::updateUsdPublish(int32_t in_publishId,
@@ -1191,22 +1345,7 @@
 ndk::ScopedAStatus StaIface::initiateHs20IconQueryInternal(
 	const std::vector<uint8_t> &mac_address, const std::string &file_name)
 {
-#ifdef CONFIG_HS20
-	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
-	if (mac_address.size() != ETH_ALEN) {
-		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
-	}
-	wpa_s->fetch_osu_icon_in_progress = 0;
-	if (hs20_anqp_send_req(
-		wpa_s, mac_address.data(), BIT(HS20_STYPE_ICON_REQUEST),
-		reinterpret_cast<const uint8_t *>(file_name.c_str()),
-		file_name.size(), true)) {
-		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
-	}
-	return ndk::ScopedAStatus::ok();
-#else
 	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
-#endif /* CONFIG_HS20 */
 }
 
 std::pair<std::vector<uint8_t>, ndk::ScopedAStatus>
@@ -1961,7 +2100,7 @@
 #endif
 	AidlManager *aidl_manager = AidlManager::getInstance();
 	WPA_ASSERT(aidl_manager);
-	if (aidl_manager->isAidlServiceVersionAtLeast(4) && wpas_rsn_overriding(wpa_s)) {
+	if (aidl_manager->isAidlServiceVersionAtLeast(4) && wpas_rsn_overriding(wpa_s, NULL)) {
 		mask |= static_cast<uint32_t>(WpaDriverCapabilitiesMask::RSN_OVERRIDING);
 	}
 
@@ -2608,35 +2747,145 @@
 }
 
 std::pair<UsdCapabilities, ndk::ScopedAStatus> StaIface::getUsdCapabilitiesInternal() {
+	// TODO (b/382756996): Retrieve the capabilities dynamically
 	UsdCapabilities capabilities;
+	capabilities.isUsdPublisherSupported = kIsUsdPublisherSupported;
+	capabilities.isUsdSubscriberSupported = kIsUsdSubscriberSupported;
+	capabilities.maxLocalSsiLengthBytes = kMaxUsdLocalSsiLengthBytes;
+	capabilities.maxServiceNameLengthBytes = kMaxUsdServiceNameLengthBytes;
+	capabilities.maxMatchFilterLengthBytes = kMaxUsdMatchFilterLengthBytes;
+	capabilities.maxNumPublishSessions = kMaxNumUsdPublishSessions;
+	capabilities.maxNumSubscribeSessions = kMaxNumUsdSubscribeSessions;
 	return {capabilities, ndk::ScopedAStatus::ok()};
 }
 
 ndk::ScopedAStatus StaIface::startUsdPublishInternal(
-		const UsdPublishConfig& usdPublishConfig) {
-	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+		int32_t cmdId, const UsdPublishConfig& usdPublishConfig) {
+	if (!validateUsdPublishConfig(usdPublishConfig)) {
+		return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+	}
+	auto ssiBuffer = misc_utils::convertVectorToWpaBuf(
+		usdPublishConfig.usdBaseConfig.serviceSpecificInfo);
+	if (ssiBuffer.get() == nullptr) {
+		wpa_printf(MSG_INFO, "Unable to convert USD publish SSI to buffer");
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct nan_publish_params nanPublishParams =
+		convertAidlNanPublishParamsToInternal(usdPublishConfig);
+
+	int publishId = wpas_nan_usd_publish(
+		wpa_s, usdPublishConfig.usdBaseConfig.serviceName.c_str(),
+		convertAidlServiceProtoTypeToInternal(
+			usdPublishConfig.usdBaseConfig.serviceProtoType),
+		ssiBuffer.get(), &nanPublishParams, false /* p2p */);
+
+	// Core supplicant does not have an internal callback for USD publish, but some
+	// implementations may decide to offload and return the result in a callback.
+	// In our case (core supplicant), the AIDL callback will be invoked directly here.
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	WPA_ASSERT(aidl_manager);
+	if (publishId < 0) {
+		wpa_printf(MSG_INFO, "Failed to configure USD publish");
+		aidl_manager->notifyUsdPublishConfigFailed(
+			wpa_s, cmdId, ISupplicantStaIfaceCallback::UsdConfigErrorCode::FAILURE_UNKNOWN);
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	aidl_manager->notifyUsdPublishStarted(wpa_s, cmdId, publishId);
+	return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus StaIface::startUsdSubscribeInternal(
-		const UsdSubscribeConfig& usdSubscribeConfig) {
-	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+		int32_t cmdId, const UsdSubscribeConfig& usdSubscribeConfig) {
+	if (!validateUsdSubscribeConfig(usdSubscribeConfig)) {
+		return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+	}
+	auto ssiBuffer = misc_utils::convertVectorToWpaBuf(
+		usdSubscribeConfig.usdBaseConfig.serviceSpecificInfo);
+	if (ssiBuffer.get() == nullptr) {
+		wpa_printf(MSG_INFO, "Unable to convert USD subscribe SSI to buffer");
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct nan_subscribe_params nanSubscribeParams =
+		convertAidlNanSubscribeParamsToInternal(usdSubscribeConfig);
+
+	int subscribeId = wpas_nan_usd_subscribe(
+		wpa_s, usdSubscribeConfig.usdBaseConfig.serviceName.c_str(),
+		convertAidlServiceProtoTypeToInternal(
+			usdSubscribeConfig.usdBaseConfig.serviceProtoType),
+		ssiBuffer.get(), &nanSubscribeParams, false /* p2p */);
+
+	// See comment in startUsdPublishInternal regarding callbacks
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	WPA_ASSERT(aidl_manager);
+	if (subscribeId < 0) {
+		wpa_printf(MSG_INFO, "Failed to configure USD subscribe");
+		aidl_manager->notifyUsdSubscribeConfigFailed(
+			wpa_s, cmdId, ISupplicantStaIfaceCallback::UsdConfigErrorCode::FAILURE_UNKNOWN);
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	aidl_manager->notifyUsdSubscribeStarted(wpa_s, cmdId, subscribeId);
+	return ndk::ScopedAStatus::ok();
 }
 
 ::ndk::ScopedAStatus StaIface::updateUsdPublishInternal(int32_t publishId,
 		const std::vector<uint8_t>& serviceSpecificInfo) {
-	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+	if (!checkContainerSize(serviceSpecificInfo, kMaxUsdLocalSsiLengthBytes)) {
+		wpa_printf(MSG_ERROR, "Service specific info of size %zu exceeds the"
+			" supported size of %d", serviceSpecificInfo.size(),
+			kMaxUsdLocalSsiLengthBytes);
+		return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+	}
+	auto ssiBuffer = misc_utils::convertVectorToWpaBuf(serviceSpecificInfo);
+	if (ssiBuffer.get() == nullptr) {
+		wpa_printf(MSG_INFO, "Unable to convert USD update SSI to buffer");
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	int status = wpas_nan_usd_update_publish(
+		retrieveIfacePtr(), publishId, ssiBuffer.get());
+	if (status < 0) {
+		wpa_printf(MSG_INFO, "Failed to update USD publish");
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	return ndk::ScopedAStatus::ok();
 }
 
 ::ndk::ScopedAStatus StaIface::cancelUsdPublishInternal(int32_t publishId) {
-	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+	// Status code is returned by the callback
+	wpas_nan_usd_cancel_publish(retrieveIfacePtr(), publishId);
+	return ndk::ScopedAStatus::ok();
 }
 
 ::ndk::ScopedAStatus StaIface::cancelUsdSubscribeInternal(int32_t subscribeId) {
-	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+	// Status code is returned by the callback
+	wpas_nan_usd_cancel_subscribe(retrieveIfacePtr(), subscribeId);
+	return ndk::ScopedAStatus::ok();
 }
 
 ::ndk::ScopedAStatus StaIface::sendUsdMessageInternal(const UsdMessageInfo& messageInfo) {
-	return createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED);
+	if (!checkContainerSize(messageInfo.message, kMaxUsdLocalSsiLengthBytes)) {
+		wpa_printf(MSG_ERROR, "Message of size %zu exceeds the supported size of %d",
+			messageInfo.message.size(), kMaxUsdLocalSsiLengthBytes);
+		return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+	}
+	auto msgBuffer = misc_utils::convertVectorToWpaBuf(messageInfo.message);
+	if (msgBuffer.get() == nullptr) {
+		wpa_printf(MSG_INFO, "Unable to convert message contents to buffer");
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	int handle = messageInfo.ownId;
+	int reqInstanceId = messageInfo.peerId;
+	int status = wpas_nan_usd_transmit(
+		retrieveIfacePtr(), handle, msgBuffer.get(), nullptr /* elems */,
+		messageInfo.peerMacAddress.data(), reqInstanceId);
+	if (status < 0) {
+		wpa_printf(MSG_INFO, "Failed to send USD message");
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	return ndk::ScopedAStatus::ok();
 }
 
 /**
diff --git a/wpa_supplicant/aidl/vendor/sta_iface.h b/wpa_supplicant/aidl/vendor/sta_iface.h
index 6c6cfb9..8703aaf 100644
--- a/wpa_supplicant/aidl/vendor/sta_iface.h
+++ b/wpa_supplicant/aidl/vendor/sta_iface.h
@@ -31,6 +31,7 @@
 #include "wpa_supplicant_i.h"
 #include "config.h"
 #include "driver_i.h"
+#include "nan_usd.h"
 #include "wpa.h"
 }
 
@@ -292,8 +293,10 @@
 	::ndk::ScopedAStatus configureMscsInternal(const MscsParams& params);
 	::ndk::ScopedAStatus disableMscsInternal();
 	std::pair<UsdCapabilities, ndk::ScopedAStatus> getUsdCapabilitiesInternal();
-	::ndk::ScopedAStatus startUsdPublishInternal(const UsdPublishConfig& usdPublishConfig);
-	::ndk::ScopedAStatus startUsdSubscribeInternal(const UsdSubscribeConfig& usdSubscribeConfig);
+	::ndk::ScopedAStatus startUsdPublishInternal(
+		int32_t cmdId, const UsdPublishConfig& usdPublishConfig);
+	::ndk::ScopedAStatus startUsdSubscribeInternal(
+		int32_t cmdId, const UsdSubscribeConfig& usdSubscribeConfig);
 	::ndk::ScopedAStatus updateUsdPublishInternal(int32_t publishId,
 		const std::vector<uint8_t>& serviceSpecificInfo);
 	::ndk::ScopedAStatus cancelUsdPublishInternal(int32_t publishId);
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 69a0e5e..de18a68 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -540,12 +540,6 @@
 		conf->supported_rates = list;
 	}
 
-#ifdef CONFIG_IEEE80211AX
-	if (ssid->mode == WPAS_MODE_P2P_GO ||
-	    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
-		conf->ieee80211ax = ssid->he;
-#endif /* CONFIG_IEEE80211AX */
-
 	bss->isolate = !wpa_s->conf->p2p_intra_bss;
 	bss->extended_key_id = wpa_s->conf->extended_key_id;
 	bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
@@ -582,6 +576,18 @@
 	else
 		bss->wpa_key_mgmt = ssid->key_mgmt;
 	bss->wpa_pairwise = ssid->pairwise_cipher;
+
+#ifdef CONFIG_P2P
+	if (ssid->p2p_mode == WPA_P2P_MODE_WFD_PCC) {
+		bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+		bss->rsn_override_key_mgmt = WPA_KEY_MGMT_SAE |
+			WPA_KEY_MGMT_PASN;
+		bss->wpa_pairwise = WPA_CIPHER_CCMP;
+		bss->rsn_override_pairwise = WPA_CIPHER_CCMP;
+		bss->rsn_override_mfp = 2;
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) && ssid->passphrase) {
 		bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
 	} else if (ssid->psk_set) {
@@ -638,10 +644,7 @@
 		bss->sae_passwords = pw;
 	}
 
-	if (ssid->sae_pwe != DEFAULT_SAE_PWE)
-		bss->sae_pwe = ssid->sae_pwe;
-	else
-		bss->sae_pwe = wpa_s->conf->sae_pwe;
+	bss->sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 #endif /* CONFIG_SAE */
 
 	if (wpa_s->conf->go_interworking) {
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 39de8ba..0afac49 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -91,9 +91,6 @@
 	ANQP_DUP(hs20_wan_metrics);
 	ANQP_DUP(hs20_connection_capability);
 	ANQP_DUP(hs20_operating_class);
-	ANQP_DUP(hs20_osu_providers_list);
-	ANQP_DUP(hs20_operator_icon_metadata);
-	ANQP_DUP(hs20_osu_providers_nai_list);
 #endif /* CONFIG_HS20 */
 #undef ANQP_DUP
 
@@ -176,9 +173,6 @@
 	wpabuf_free(anqp->hs20_wan_metrics);
 	wpabuf_free(anqp->hs20_connection_capability);
 	wpabuf_free(anqp->hs20_operating_class);
-	wpabuf_free(anqp->hs20_osu_providers_list);
-	wpabuf_free(anqp->hs20_operator_icon_metadata);
-	wpabuf_free(anqp->hs20_osu_providers_nai_list);
 #endif /* CONFIG_HS20 */
 
 	os_free(anqp);
@@ -220,6 +214,7 @@
 		    const char *reason)
 {
 	struct wpa_connect_work *cwork;
+	unsigned int j;
 
 	if (wpa_s->last_scan_res) {
 		unsigned int i;
@@ -245,6 +240,45 @@
 		wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
 	wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
 	wpa_bss_anqp_free(bss->anqp);
+
+	if (wpa_s->current_bss == bss) {
+		wpa_printf(MSG_DEBUG,
+			   "BSS: Clear current_bss due to bss removal");
+		wpa_s->current_bss = NULL;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	if (wpa_s->interworking_gas_bss == bss) {
+		wpa_printf(MSG_DEBUG,
+			   "BSS: Clear interworking_gas_bss due to bss removal");
+		wpa_s->interworking_gas_bss = NULL;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_WNM
+	if (wpa_s->wnm_target_bss == bss) {
+		wpa_printf(MSG_DEBUG,
+			   "BSS: Clear wnm_target_bss due to bss removal");
+		wpa_s->wnm_target_bss = NULL;
+	}
+#endif /* CONFIG_WNM */
+
+	if (wpa_s->ml_connect_probe_bss == bss) {
+		wpa_printf(MSG_DEBUG,
+			   "BSS: Clear ml_connect_probe_bss due to bss removal");
+		wpa_s->ml_connect_probe_bss = NULL;
+	}
+
+	for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
+		if (wpa_s->links[j].bss == bss) {
+			wpa_printf(MSG_DEBUG,
+				   "BSS: Clear links[%d].bss due to bss removal",
+				   j);
+			wpa_s->valid_links &= ~BIT(j);
+			wpa_s->links[j].bss = NULL;
+		}
+	}
+
 	os_free(bss);
 }
 
@@ -316,7 +350,8 @@
 					       &owe_ssid_len))
 			continue;
 
-		if (owe_ssid_len == ssid_len &&
+		if (bss->ssid_len &&
+		    owe_ssid_len == ssid_len &&
 		    os_memcmp(owe_ssid, ssid, ssid_len) == 0)
 			return bss;
 #endif /* CONFIG_OWE */
@@ -422,6 +457,28 @@
 }
 
 
+#ifdef CONFIG_OWE
+static int wpa_bss_owe_trans_known(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss,
+				   const u8 *entry_ssid, size_t entry_ssid_len)
+{
+	const u8 *owe, *owe_bssid, *owe_ssid;
+	size_t owe_ssid_len;
+
+	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+	if (!owe)
+		return 0;
+
+	if (wpas_get_owe_trans_network(owe, &owe_bssid, &owe_ssid,
+				       &owe_ssid_len))
+		return 0;
+
+	return entry_ssid_len == owe_ssid_len &&
+		os_memcmp(owe_ssid, entry_ssid, owe_ssid_len) == 0;
+}
+#endif /* CONFIG_OWE */
+
+
 static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
 	struct wpa_ssid *ssid;
@@ -435,6 +492,11 @@
 		if (ssid->ssid_len == bss->ssid_len &&
 		    os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
 			return 1;
+#ifdef CONFIG_OWE
+		if (wpa_bss_owe_trans_known(wpa_s, bss, ssid->ssid,
+					    ssid->ssid_len))
+			return 1;
+#endif /* CONFIG_OWE */
 	}
 
 	return 0;
@@ -485,6 +547,7 @@
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (!wpa_bss_known(wpa_s, bss) &&
+		    !wpa_bss_in_use(wpa_s, bss) &&
 		    !wpa_bss_is_wps_candidate(wpa_s, bss)) {
 			wpa_bss_remove(wpa_s, bss, __func__);
 			return 0;
@@ -795,9 +858,17 @@
 		struct wpa_bss *nbss;
 		struct dl_list *prev = bss->list_id.prev;
 		struct wpa_connect_work *cwork;
-		unsigned int i;
+		unsigned int i, j;
 		bool update_current_bss = wpa_s->current_bss == bss;
 		bool update_ml_probe_bss = wpa_s->ml_connect_probe_bss == bss;
+		int update_link_bss = -1;
+
+		for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
+			if (wpa_s->links[j].bss == bss) {
+				update_link_bss = j;
+				break;
+			}
+		}
 
 		cwork = wpa_bss_check_pending_connect(wpa_s, bss);
 
@@ -819,6 +890,9 @@
 			if (update_ml_probe_bss)
 				wpa_s->ml_connect_probe_bss = nbss;
 
+			if (update_link_bss >= 0)
+				wpa_s->links[update_link_bss].bss = nbss;
+
 			if (cwork)
 				wpa_bss_update_pending_connect(cwork, nbss);
 
@@ -1083,6 +1157,7 @@
 		if (wpa_s->reassoc_same_ess &&
 		    wpa_s->wpa_state != WPA_COMPLETED &&
 		    wpa_s->last_ssid &&
+		    wpa_s->last_ssid->ssid &&
 		    bss->ssid_len == wpa_s->last_ssid->ssid_len &&
 		    os_memcmp(bss->ssid, wpa_s->last_ssid->ssid,
 			      bss->ssid_len) == 0)
@@ -1588,18 +1663,15 @@
 	end = pos + len;
 	pos += sizeof(*ap_info);
 
-	for (i = 0; i < count; i++) {
-		u8 bss_params;
-
+	for (i = 0; i < count; i++, pos += ap_info->tbtt_info_len) {
 		if (end - pos < ap_info->tbtt_info_len)
 			break;
 
-		bss_params = pos[1 + ETH_ALEN + 4];
 		mld_params = pos + mld_params_offset;
 
 		link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
 		if (link_id >= MAX_NUM_MLD_LINKS)
-			return;
+			continue;
 
 		if (*mld_params != mbssid_idx) {
 			wpa_printf(MSG_DEBUG,
@@ -1621,10 +1693,8 @@
 			if (!neigh_bss) {
 				*missing |= BIT(link_id);
 			} else if ((!ssid ||
-				    (bss_params & (RNR_BSS_PARAM_SAME_SSID |
-						   RNR_BSS_PARAM_CO_LOCATED)) ||
 				    wpa_scan_res_match(wpa_s, 0, neigh_bss,
-						       ssid, 1, 0)) &&
+						       ssid, 1, 0, true)) &&
 				   !wpa_bssid_ignore_is_listed(
 					   wpa_s, neigh_bss->bssid)) {
 				struct mld_link *l;
@@ -1637,8 +1707,6 @@
 					RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
 			}
 		}
-
-		pos += ap_info->tbtt_info_len;
 	}
 }
 
@@ -1711,11 +1779,11 @@
 		const u8 *rsne;
 		size_t rsne_len;
 
-		if (elems.rsne_override_2 && wpas_rsn_overriding(wpa_s)) {
+		if (elems.rsne_override_2 && wpas_rsn_overriding(wpa_s, ssid)) {
 			rsne = elems.rsne_override_2;
 			rsne_len = elems.rsne_override_2_len;
 		} else if (elems.rsne_override &&
-			   wpas_rsn_overriding(wpa_s)) {
+			   wpas_rsn_overriding(wpa_s, ssid)) {
 			rsne = elems.rsne_override;
 			rsne_len = elems.rsne_override_len;
 		} else {
@@ -1822,8 +1890,8 @@
 				goto out;
 
 			wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx,
-						     ap_info, len, &seen,
-						     &missing, ssid);
+						     ap_info, ap_info_len,
+						     &seen, &missing, ssid);
 
 			pos += ap_info_len;
 			len -= ap_info_len;
@@ -1865,8 +1933,9 @@
 	const u8 *pos = wpa_bss_ie_ptr(bss);
 	size_t len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
 	const struct ieee80211_eht_ml *ml;
+	const struct eht_ml_reconf_common_info *common_info;
 	u16 removed_links = 0;
-	u8 ml_common_len;
+	u8 expected_ml_common_len;
 
 	if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
 		return 0;
@@ -1881,22 +1950,33 @@
 	ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
 	len = wpabuf_len(mlbuf);
 
-	if (len < sizeof(*ml))
+	/* There must be at least one octet for the Common Info Length subfield
+	 */
+	if (len < sizeof(*ml) + 1UL)
 		goto out;
 
-	ml_common_len = 1;
-	if (ml->ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR)
-		ml_common_len += ETH_ALEN;
+	expected_ml_common_len = 1;
+	if (le_to_host16(ml->ml_control) &
+	    RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR)
+		expected_ml_common_len += ETH_ALEN;
 
-	if (len < sizeof(*ml) + ml_common_len) {
+	common_info = (const struct eht_ml_reconf_common_info *) ml->variable;
+	if (len < sizeof(*ml) + common_info->len) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: Unexpected Reconfiguration ML element length: (%zu < %zu)",
-			   len, sizeof(*ml) + ml_common_len);
+			   len, sizeof(*ml) + common_info->len);
 		goto out;
 	}
 
-	pos = ml->variable + ml_common_len;
-	len -= sizeof(*ml) + ml_common_len;
+	if (common_info->len < expected_ml_common_len) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Invalid common info len=%u; min expected=%u",
+			   common_info->len, expected_ml_common_len);
+		goto out;
+	}
+
+	pos = ml->variable + common_info->len;
+	len -= sizeof(*ml) + common_info->len;
 
 	while (len >= 2 + sizeof(struct ieee80211_eht_per_sta_profile)) {
 		size_t sub_elem_len = *(pos + 1);
@@ -1908,7 +1988,8 @@
 			goto out;
 		}
 
-		if  (*pos == EHT_ML_SUB_ELEM_PER_STA_PROFILE) {
+		if  (*pos == EHT_ML_SUB_ELEM_PER_STA_PROFILE &&
+		     sub_elem_len >= 2) {
 			const struct ieee80211_eht_per_sta_profile *sta_prof =
 				(const struct ieee80211_eht_per_sta_profile *)
 				(pos + 2);
@@ -1931,6 +2012,8 @@
 }
 
 
+#ifndef CONFIG_NO_WPA
+
 static bool wpa_bss_supported_cipher(struct wpa_supplicant *wpa_s,
 				     int pairwise_cipher)
 {
@@ -2056,14 +2139,17 @@
 	return true;
 }
 
+#endif /* CONFIG_NO_WPA */
+
 
 const u8 * wpa_bss_get_rsne(struct wpa_supplicant *wpa_s,
 			    const struct wpa_bss *bss, struct wpa_ssid *ssid,
 			    bool mlo)
 {
+#ifndef CONFIG_NO_WPA
 	const u8 *ie;
 
-	if (wpas_rsn_overriding(wpa_s)) {
+	if (wpas_rsn_overriding(wpa_s, ssid)) {
 		if (!ssid)
 			ssid = wpa_s->current_ssid;
 
@@ -2087,6 +2173,7 @@
 				return ie;
 		}
 	}
+#endif /* CONFIG_NO_WPA */
 
 	return wpa_bss_get_ie(bss, WLAN_EID_RSN);
 }
@@ -2098,7 +2185,7 @@
 {
 	const u8 *ie;
 
-	if (wpas_rsn_overriding(wpa_s)) {
+	if (wpas_rsn_overriding(wpa_s, ssid)) {
 		ie = wpa_bss_get_vendor_ie(bss, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
 		if (ie) {
 			const u8 *tmp;
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 31688fa..74ef3c8 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -61,9 +61,6 @@
 	struct wpabuf *hs20_wan_metrics;
 	struct wpabuf *hs20_connection_capability;
 	struct wpabuf *hs20_operating_class;
-	struct wpabuf *hs20_osu_providers_list;
-	struct wpabuf *hs20_operator_icon_metadata;
-	struct wpabuf *hs20_osu_providers_nai_list;
 #endif /* CONFIG_HS20 */
 };
 
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 5b08478..47d9e6d 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -693,8 +693,6 @@
 		else if (os_strcmp(start, "RSN") == 0 ||
 			 os_strcmp(start, "WPA2") == 0)
 			val |= WPA_PROTO_RSN;
-		else if (os_strcmp(start, "OSEN") == 0)
-			val |= WPA_PROTO_OSEN;
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
 				   line, start);
@@ -749,14 +747,6 @@
 		pos += ret;
 	}
 
-	if (ssid->proto & WPA_PROTO_OSEN) {
-		ret = os_snprintf(pos, end - pos, "%sOSEN",
-				  pos == buf ? "" : " ");
-		if (os_snprintf_error(end - pos, ret))
-			return buf;
-		pos += ret;
-	}
-
 	if (pos == buf) {
 		os_free(buf);
 		buf = NULL;
@@ -831,10 +821,6 @@
 		else if (os_strcmp(start, "FT-SAE-EXT-KEY") == 0)
 			val |= WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
-#ifdef CONFIG_HS20
-		else if (os_strcmp(start, "OSEN") == 0)
-			val |= WPA_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_SUITEB
 		else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
@@ -1071,18 +1057,6 @@
 	}
 #endif /* CONFIG_SAE */
 
-#ifdef CONFIG_HS20
-	if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) {
-		ret = os_snprintf(pos, end - pos, "%sOSEN",
-				  pos == buf ? "" : " ");
-		if (os_snprintf_error(end - pos, ret)) {
-			end[-1] = '\0';
-			return buf;
-		}
-		pos += ret;
-	}
-#endif /* CONFIG_HS20 */
-
 #ifdef CONFIG_SUITEB
 	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B",
@@ -2138,7 +2112,27 @@
 }
 
 
+static int wpa_config_parse_p2p2_client_list(const struct parse_data *data,
+					     struct wpa_ssid *ssid, int line,
+					     const char *value)
+{
+	int *ids = wpa_config_parse_int_array(value);
+
+	if (!ids) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid p2p2_client_list '%s'",
+			   line, value);
+		return -1;
+	}
+
+	os_free(ssid->p2p2_client_list);
+	ssid->p2p2_client_list = ids;
+
+	return 0;
+}
+
+
 #ifndef NO_CONFIG_WRITE
+
 static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
 					       struct wpa_ssid *ssid)
 {
@@ -2146,6 +2140,14 @@
 					  ssid->num_p2p_clients,
 					  "p2p_client_list");
 }
+
+
+static char * wpa_config_write_p2p2_client_list(const struct parse_data *data,
+						struct wpa_ssid *ssid)
+{
+	return wpa_config_write_freqs(data, ssid->p2p2_client_list);
+}
+
 #endif /* NO_CONFIG_WRITE */
 
 
@@ -2529,6 +2531,7 @@
 	{ INT(mem_only_psk) },
 	{ STR_KEY(sae_password) },
 	{ STR(sae_password_id) },
+	{ INT(sae_pwe) },
 	{ FUNC(proto) },
 	{ FUNC(key_mgmt) },
 	{ INT(bg_scan_period) },
@@ -2543,7 +2546,7 @@
 	{ INT_RANGE(he, 0, 1) },
 	{ INT_RANGE(ht40, -1, 1) },
 	{ INT_RANGE(max_oper_chwidth, CONF_OPER_CHWIDTH_USE_HT,
-		    CONF_OPER_CHWIDTH_80P80MHZ) },
+		    CONF_OPER_CHWIDTH_320MHZ) },
 	{ INT(vht_center_freq1) },
 	{ INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
@@ -2671,7 +2674,9 @@
 #ifdef CONFIG_P2P
 	{ FUNC(go_p2p_dev_addr) },
 	{ FUNC(p2p_client_list) },
+	{ FUNC(p2p2_client_list) },
 	{ FUNC(psk_list) },
+	{ INT(go_dik_id) },
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_HT_OVERRIDES
 	{ INT_RANGE(disable_ht, 0, 1) },
@@ -2722,6 +2727,7 @@
 	{ INT_RANGE(macsec_port, 1, 65534) },
 	{ INT_RANGE(mka_priority, 0, 255) },
 	{ INT_RANGE(macsec_csindex, 0, 1) },
+	{ INT_RANGE(macsec_icv_indicator, 0, 1) },
 	{ FUNC_KEY(mka_cak) },
 	{ FUNC_KEY(mka_ckn) },
 #endif /* CONFIG_MACSEC */
@@ -2757,6 +2763,7 @@
 	{ INT_RANGE(enable_4addr_mode, 0, 1)},
 	{ INT_RANGE(max_idle, 0, 65535)},
 	{ INT_RANGE(ssid_protection, 0, 1)},
+	{ INT_RANGE(rsn_overriding, 0, 2)},
 };
 
 #undef OFFSET
@@ -2937,6 +2944,7 @@
 	os_free(ssid->freq_list);
 	os_free(ssid->bgscan);
 	os_free(ssid->p2p_client_list);
+	os_free(ssid->p2p2_client_list);
 	os_free(ssid->bssid_ignore);
 	os_free(ssid->bssid_accept);
 #ifdef CONFIG_HT_OVERRIDES
@@ -3028,6 +3036,7 @@
 {
 	struct wpa_ssid *ssid, *prev = NULL;
 	struct wpa_cred *cred, *cprev;
+	struct wpa_dev_ik *identity, *iprev;
 	int i;
 
 	ssid = config->ssid;
@@ -3044,6 +3053,13 @@
 		wpa_config_free_cred(cprev);
 	}
 
+	identity = config->identity;
+	while (identity) {
+		iprev = identity;
+		identity = identity->next;
+		wpa_config_free_identity(iprev);
+	}
+
 	wpa_config_flush_blobs(config);
 
 	wpabuf_free(config->wps_vendor_ext_m1);
@@ -3084,7 +3100,6 @@
 	os_free(config->sae_groups);
 	wpabuf_free(config->ap_vendor_elements);
 	wpabuf_free(config->ap_assocresp_elements);
-	os_free(config->osu_dir);
 	os_free(config->bgscan);
 	os_free(config->wowlan_triggers);
 	os_free(config->fst_group_id);
@@ -3097,6 +3112,8 @@
 	os_free(config->dpp_extra_conf_req_name);
 	os_free(config->dpp_extra_conf_req_value);
 	wpabuf_free(config->dik);
+	wpabuf_free(config->wfa_gen_capa_supp);
+	wpabuf_free(config->wfa_gen_capa_cert);
 
 	os_free(config);
 }
@@ -3148,6 +3165,35 @@
 }
 
 
+#ifdef CONFIG_P2P
+/**
+ * wpa_config_get_network_with_dik_id - Get configured network based on ID of
+ *	device identity block
+ * @config: Configuration data from wpa_config_read()
+ * @dik_id: DIK ID to search for
+ * Returns: Network configuration or %NULL if not found
+ */
+struct wpa_ssid * wpa_config_get_network_with_dik_id(struct wpa_config *config,
+						     int dik_id)
+{
+	struct wpa_ssid *ssid;
+
+	for (ssid = config->ssid; ssid; ssid = ssid->next) {
+		if (ssid->disabled != 2)
+			continue;
+
+		if (ssid->go_dik_id == dik_id)
+			return ssid;
+
+		if (int_array_includes(ssid->p2p2_client_list, dik_id))
+			return ssid;
+	}
+
+	return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
 /**
  * wpa_config_add_network - Add a new network with empty configuration
  * @config: Configuration data from wpa_config_read()
@@ -3282,6 +3328,7 @@
 #endif /* CONFIG_MACSEC */
 	ssid->mac_addr = WPAS_MAC_ADDR_STYLE_NOT_SET;
 	ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
+	ssid->rsn_overriding = RSN_OVERRIDING_NOT_SET;
 }
 
 
@@ -4828,6 +4875,46 @@
 }
 
 
+static int wpa_global_config_parse_bool(const struct global_parse_data *data,
+					struct wpa_config *config, int line,
+					const char *pos)
+{
+	int val;
+	bool *dst;
+	char *end;
+	bool same;
+
+	dst = (bool *) (((u8 *) config) + (long) data->param1);
+	val = strtol(pos, &end, 0);
+	if (*end) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
+			   line, pos);
+		return -1;
+	}
+
+	if (val < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: too small %s (value=%d min_value=0)",
+			   line, data->name, val);
+		return -1;
+	}
+
+	if (val > 1) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: too large %s (value=%d max_value=1)",
+			   line, data->name, val);
+		return -1;
+	}
+
+	same = *dst == val;
+	*dst = val;
+
+	wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
+
+	return same;
+}
+
+
 static int wpa_global_config_parse_str(const struct global_parse_data *data,
 				       struct wpa_config *config, int line,
 				       const char *pos)
@@ -5318,6 +5405,18 @@
 }
 
 
+static int wpa_config_get_bool(const char *name, struct wpa_config *config,
+			       long offset, char *buf, size_t buflen,
+			       int pretty_print)
+{
+	bool *val = (bool*) (((u8 *) config) + (long) offset);
+
+	if (pretty_print)
+		return os_snprintf(buf, buflen, "%s=%d\n", name, *val);
+	return os_snprintf(buf, buflen, "%d", *val);
+}
+
+
 static int wpa_config_get_str(const char *name, struct wpa_config *config,
 			      long offset, char *buf, size_t buflen,
 			      int pretty_print)
@@ -5393,6 +5492,8 @@
 #define INT(f) _INT(f), NULL, NULL
 #define INT_RANGE(f, min, max) #f, wpa_global_config_parse_int_range, \
 	wpa_config_get_int, OFFSET(f), (void *) min, (void *) max
+#define BOOL(f) #f, wpa_global_config_parse_bool, wpa_config_get_bool, \
+		OFFSET(f), NULL, NULL
 #define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
@@ -5497,6 +5598,14 @@
 	{ INT(p2p_interface_random_mac_addr), 0 },
 	{ INT(p2p_6ghz_disable), 0 },
 	{ INT(p2p_dfs_chan_enable), 0 },
+	{ BOOL(p2p_pairing_setup), 0 },
+	{ BOOL(p2p_pairing_cache), 0 },
+	{ INT(p2p_bootstrap_methods), 0 },
+	{ INT(p2p_pasn_type), 0 },
+	{ INT(p2p_comeback_after), 0 },
+	{ BOOL(p2p_twt_power_mgmt), 0 },
+	{ BOOL(p2p_chan_switch_req_enable), 0 },
+	{ INT(p2p_reg_info), 0 },
 	{ INT(dik_cipher), 0},
 	{ BIN(dik), 0 },
 #endif /* CONFIG_P2P */
@@ -5548,7 +5657,6 @@
 	{ INT(sched_scan_interval), 0 },
 	{ INT(sched_scan_start_delay), 0 },
 	{ INT(tdls_external_control), 0},
-	{ STR(osu_dir), 0 },
 	{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
 	{ INT(p2p_search_delay), 0},
 	{ INT_RANGE(mac_addr, 0, 2), 0 },
@@ -5577,6 +5685,7 @@
 	{ INT(gas_address3), 0 },
 	{ INT_RANGE(ftm_responder, 0, 1), 0 },
 	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
+	{ BOOL(twt_requester), 0 },
 	{ INT(gas_rand_addr_lifetime), 0 },
 	{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
 #ifdef CONFIG_DPP
@@ -5606,16 +5715,21 @@
 	{ INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 },
 	{ FUNC(mld_connect_bssid_pref), 0 },
 #endif /* CONFIG_TESTING_OPTIONS */
-	{ INT_RANGE(ft_prepend_pmkid, 0, 1), CFG_CHANGED_FT_PREPEND_PMKID },
+	{ BOOL(ft_prepend_pmkid), CFG_CHANGED_FT_PREPEND_PMKID },
+	{ INT_RANGE(wfa_gen_capa, 0, 2), 0},
+	{ BIN(wfa_gen_capa_supp), 0 },
+	{ BIN(wfa_gen_capa_cert), 0 },
 	/* NOTE: When adding new parameters here, add_interface() in
 	 * wpa_supplicant/dbus_new_introspect.c may need to be modified to
 	 * increase the size of the iface->xml buffer. */
 };
 
 #undef FUNC
+#undef FUNC_NO_VAR
 #undef _INT
 #undef INT
 #undef INT_RANGE
+#undef BOOL
 #undef _STR
 #undef STR
 #undef STR_RANGE
@@ -5774,3 +5888,129 @@
 
 	return ret;
 }
+
+
+int wpa_config_set_identity(struct wpa_dev_ik *identity, const char *var,
+			    const char *value, int line)
+{
+	char *val;
+	size_t len;
+	int ret = 0;
+
+	if (os_strcmp(var, "dik_cipher") == 0) {
+		identity->dik_cipher = atoi(value);
+		return 0;
+	}
+
+	val = wpa_config_parse_string(value, &len);
+	if (!val) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: invalid field '%s' string value '%s'.",
+			   line, var, value);
+		return -1;
+	}
+
+	if (os_strcmp(var, "dik") == 0) {
+		wpabuf_free(identity->dik);
+		identity->dik = wpabuf_alloc_copy(val, len);
+		if (!identity->dik)
+			ret = -1;
+	} else if (os_strcmp(var, "pmk") == 0) {
+		wpabuf_free(identity->pmk);
+		identity->pmk = wpabuf_alloc_copy(val, len);
+		if (!identity->pmk)
+			ret = -1;
+	} else if (os_strcmp(var, "pmkid") == 0) {
+		if (len == PMKID_LEN) {
+			wpabuf_free(identity->pmkid);
+			identity->pmkid = wpabuf_alloc_copy(val, len);
+			if (!identity->pmkid)
+				ret = -1;
+		} else {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid field '%s' string value '%s'.",
+				   line, var, value);
+			ret = -1;
+		}
+	} else if (line) {
+		wpa_printf(MSG_ERROR, "Line %d: unknown identity field '%s'.",
+			   line, var);
+		ret = -1;
+	}
+
+	os_free(val);
+	return ret;
+}
+
+
+void wpa_config_free_identity(struct wpa_dev_ik *identity)
+{
+	wpabuf_clear_free(identity->dik);
+	wpabuf_clear_free(identity->pmk);
+	wpabuf_free(identity->pmkid);
+	os_free(identity);
+}
+
+
+/**
+ * wpa_config_add_identity - Add a new device identity with empty configuration
+ * @config: Configuration data from wpa_config_read()
+ * Returns: The new device identity or %NULL if operation failed
+ */
+struct wpa_dev_ik * wpa_config_add_identity(struct wpa_config *config)
+{
+	int id;
+	struct wpa_dev_ik *identity, *last = NULL;
+
+	id = 0;
+	identity = config->identity;
+	while (identity) {
+		if (identity->id > id)
+			id = identity->id;
+		last = identity;
+		identity = identity->next;
+	}
+	id++;
+
+	identity = os_zalloc(sizeof(*identity));
+	if (!identity)
+		return NULL;
+	identity->id = id;
+	if (last)
+		last->next = identity;
+	else
+		config->identity = identity;
+
+	return identity;
+}
+
+
+/**
+ * wpa_config_remove_identity - Remove a configured identity based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: 0 on success, or -1 if the network was not found
+ */
+int wpa_config_remove_identity(struct wpa_config *config, int id)
+{
+	struct wpa_dev_ik *identity, *prev = NULL;
+
+	identity = config->identity;
+	while (identity) {
+		if (id == identity->id)
+			break;
+		prev = identity;
+		identity = identity->next;
+	}
+
+	if (!identity)
+		return -1;
+
+	if (prev)
+		prev->next = identity->next;
+	else
+		config->identity = identity->next;
+
+	wpa_config_free_identity(identity);
+	return 0;
+}
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 1281786..c8859ea 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -430,6 +430,47 @@
 	int sim_num;
 };
 
+struct wpa_dev_ik {
+	/**
+	 * next - Next device identity in the list
+	 *
+	 * This pointer can be used to iterate over all device identity keys.
+	 * The head of this list is stored in the dev_ik field of struct
+	 * wpa_config.
+	 */
+	struct wpa_dev_ik *next;
+
+	/**
+	 * id - Unique id for the identity
+	 *
+	 * This identifier is used as a unique identifier for each identity
+	 * block when using the control interface. Each identity is allocated
+	 * an id when it is being created, either when reading the
+	 * configuration file or when a new identity is added.
+	 */
+	int id;
+
+	/**
+	 * dik_cipher - Device identity key cipher version
+	 */
+	int dik_cipher;
+
+	/**
+	 * dik - Device identity key which is unique for the device
+	 */
+	struct wpabuf *dik;
+
+	/**
+	 * pmk - PMK associated with the previous connection with the device
+	 */
+	struct wpabuf *pmk;
+
+	/**
+	 * pmkid - PMKID used in the previous connection with the device
+	 */
+	struct wpabuf *pmkid;
+};
+
 
 #define CFG_CHANGED_DEVICE_NAME BIT(0)
 #define CFG_CHANGED_CONFIG_METHODS BIT(1)
@@ -872,6 +913,14 @@
 	int p2p_optimize_listen_chan;
 	int p2p_6ghz_disable;
 	int p2p_dfs_chan_enable;
+	bool p2p_pairing_setup;
+	bool p2p_pairing_cache;
+	int p2p_bootstrap_methods;
+	int p2p_pasn_type;
+	int p2p_comeback_after;
+	bool p2p_twt_power_mgmt;
+	bool p2p_chan_switch_req_enable;
+	int p2p_reg_info;
 
 	struct wpabuf *wps_vendor_ext_m1;
 
@@ -1401,15 +1450,6 @@
 	u8 ip_addr_end[4];
 
 	/**
-	 * osu_dir - OSU provider information directory
-	 *
-	 * If set, allow FETCH_OSU control interface command to be used to fetch
-	 * OSU provider information into all APs and store the results in this
-	 * directory.
-	 */
-	char *osu_dir;
-
-	/**
 	 * wowlan_triggers - Wake-on-WLAN triggers
 	 *
 	 * If set, these wowlan triggers will be configured.
@@ -1804,17 +1844,9 @@
 	int wowlan_disconnect_on_deinit;
 
 	/**
-	 * rsn_overriding - RSN overriding
-	 *
-	 * 0 = Disabled
-	 * 1 = Enabled automatically if the driver indicates support
-	 * 2 = Forced to be enabled even without driver capability indication
+	 * rsn_overriding - RSN overriding (default behavior)
 	 */
-	enum rsn_overriding {
-		RSN_OVERRIDING_DISABLED = 0,
-		RSN_OVERRIDING_AUTO = 1,
-		RSN_OVERRIDING_ENABLED = 2,
-	} rsn_overriding;
+	enum wpas_rsn_overriding rsn_overriding;
 
 #ifdef CONFIG_PASN
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1849,6 +1881,55 @@
 
 	/* DevIK */
 	struct wpabuf *dik;
+
+	/**
+	 * identity - P2P2 peer device identities
+	 *
+	 * This is the head for the list of all the paired devices.
+	 */
+	struct wpa_dev_ik *identity;
+
+	/**
+	 * twt_requester - Whether TWT Requester Support is enabled
+	 *
+	 * This is for setting the bit 77 of the Extended Capabilities element.
+	 */
+	bool twt_requester;
+
+	/**
+	 * wfa_gen_capa: Whether to indicate Wi-Fi generational capability to
+	 *	the AP
+	 *
+	 * 0 = do not indicate (default)
+	 * 1 = indicate in protected Action frame
+	 * 2 = indicate in unprotected (Re)Association Request frame
+	 */
+	enum {
+		WFA_GEN_CAPA_DISABLED = 0,
+		WFA_GEN_CAPA_PROTECTED = 1,
+		WFA_GEN_CAPA_UNPROTECTED = 2,
+	} wfa_gen_capa;
+
+	/**
+	 * wfa_gen_capa_supp: Supported Generations (hexdump of a bit field)
+	 *
+	 * A bit field of supported Wi-Fi generations. This is encoded as an
+	 * little endian octt string.
+	 * bit 0: Wi-Fi 4
+	 * bit 1: Wi-Fi 5
+	 * bit 2: Wi-Fi 6
+	 * bit 3: Wi-Fi 7
+	 */
+	struct wpabuf *wfa_gen_capa_supp;
+
+	/**
+	 * wfa_gen_capa_cert: Certified Generations (hexdump of a bit field)
+	 *
+	 * This has the same format as wfa_gen_capa_supp. This is an optional
+	 * field, but if included, shall have the same length as
+	 * wfa_gen_capa_supp.
+	 */
+	struct wpabuf *wfa_gen_capa_cert;
 };
 
 
@@ -1860,6 +1941,8 @@
 				void (*func)(void *, struct wpa_ssid *),
 				void *arg);
 struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
+struct wpa_ssid * wpa_config_get_network_with_dik_id(struct wpa_config *config,
+						     int dik_id);
 struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
 int wpa_config_remove_network(struct wpa_config *config, int id);
 void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
@@ -1895,6 +1978,12 @@
 			const char *value, int line);
 char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var);
 
+int wpa_config_set_identity(struct wpa_dev_ik *identity, const char *var,
+			    const char *value, int line);
+void wpa_config_free_identity(struct wpa_dev_ik *identity);
+struct wpa_dev_ik * wpa_config_add_identity(struct wpa_config *config);
+int wpa_config_remove_identity(struct wpa_config *config, int id);
+
 struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 					   const char *driver_param);
 #ifndef CONFIG_NO_STDOUT_DEBUG
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 53614ae..57bfbed 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -296,6 +296,65 @@
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
 
+static struct wpa_dev_ik * wpa_config_read_identity(FILE *f, int *line, int id)
+{
+	struct wpa_dev_ik *identity;
+	int errors = 0, end = 0;
+	char buf[256], *pos, *pos2;
+
+	wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new identity block",
+		   *line);
+	identity = os_zalloc(sizeof(*identity));
+	if (!identity)
+		return NULL;
+	identity->id = id;
+
+	while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+		if (os_strcmp(pos, "}") == 0) {
+			end = 1;
+			break;
+		}
+
+		pos2 = os_strchr(pos, '=');
+		if (!pos2) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid identity line '%s'.",
+				   *line, pos);
+			errors++;
+			continue;
+		}
+
+		*pos2++ = '\0';
+		if (*pos2 == '"') {
+			if (os_strchr(pos2 + 1, '"') == NULL) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: invalid quotation '%s'.",
+					   *line, pos2);
+				errors++;
+				continue;
+			}
+		}
+
+		if (wpa_config_set_identity(identity, pos, pos2, *line) < 0)
+			errors++;
+	}
+
+	if (!end) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: identity block was not terminated properly.",
+			   *line);
+		errors++;
+	}
+
+	if (errors) {
+		wpa_config_free_identity(identity);
+		identity = NULL;
+	}
+
+	return identity;
+}
+
+
 struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
 				    bool ro)
 {
@@ -304,9 +363,11 @@
 	int errors = 0, line = 0;
 	struct wpa_ssid *ssid, *tail, *head;
 	struct wpa_cred *cred, *cred_tail, *cred_head;
+	struct wpa_dev_ik *identity, *identity_tail, *identity_head;
 	struct wpa_config *config;
 	static int id = 0;
 	static int cred_id = 0;
+	static int identity_id = 1;
 
 	if (name == NULL)
 		return NULL;
@@ -325,6 +386,9 @@
 	cred_tail = cred_head = config->cred;
 	while (cred_tail && cred_tail->next)
 		cred_tail = cred_tail->next;
+	identity_tail = identity_head = config->identity;
+	while (identity_tail && identity_tail->next)
+		identity_tail = identity_tail->next;
 
 	wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
 	f = fopen(name, "r");
@@ -383,6 +447,22 @@
 				continue;
 			}
 #endif /* CONFIG_NO_CONFIG_BLOBS */
+		} else if (os_strcmp(pos, "identity={") == 0) {
+			identity = wpa_config_read_identity(f, &line,
+							    identity_id++);
+			if (!identity) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: failed to parse identity block.",
+					   line);
+				errors++;
+				continue;
+			}
+			if (!identity_head) {
+				identity_head = identity_tail = identity;
+			} else {
+				identity_tail->next = identity;
+				identity_tail = identity;
+			}
 		} else if (wpa_config_process_global(config, pos, line) < 0) {
 			wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
 				   "line '%s'.", line, pos);
@@ -396,6 +476,7 @@
 	config->ssid = head;
 	wpa_config_debug_dump_networks(config);
 	config->cred = cred_head;
+	config->identity = identity_head;
 
 #ifndef WPA_IGNORE_CONFIG_ERRORS
 	if (errors) {
@@ -617,6 +698,17 @@
 }
 
 
+static void write_p2p2_client_list(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, "p2p2_client_list");
+
+	if (!value)
+		return;
+	fprintf(f, "\tp2p2_client_list=%s\n", value);
+	os_free(value);
+}
+
+
 static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
 {
 	struct psk_list_entry *psk;
@@ -775,7 +867,9 @@
 #endif /* IEEE8021X_EAPOL */
 	INT(mode);
 	INT(no_auto_peer);
-	INT(mesh_fwding);
+#ifdef CONFIG_MESH
+	INT_DEF(mesh_fwding, DEFAULT_MESH_FWDING);
+#endif /* CONFIG_MESH */
 	INT(frequency);
 	INT(enable_edmg);
 	INT(edmg_channel);
@@ -802,7 +896,9 @@
 #ifdef CONFIG_P2P
 	write_go_p2p_dev_addr(f, ssid);
 	write_p2p_client_list(f, ssid);
+	write_p2p2_client_list(f, ssid);
 	write_psk_list(f, ssid);
+	INT(go_dik_id);
 #endif /* CONFIG_P2P */
 	INT(ap_max_inactivity);
 	INT(dtim_period);
@@ -818,6 +914,7 @@
 	INT(macsec_port);
 	INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER);
 	INT(macsec_csindex);
+	INT(macsec_icv_indicator);
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
 	INT(update_identifier);
@@ -895,6 +992,7 @@
 	INT(enable_4addr_mode);
 	INT(max_idle);
 	INT(ssid_protection);
+	INT_DEF(rsn_overriding, RSN_OVERRIDING_NOT_SET);
 
 #undef STR
 #undef INT
@@ -1298,6 +1396,27 @@
 	if (config->p2p_6ghz_disable)
 		fprintf(f, "p2p_6ghz_disable=%d\n", config->p2p_6ghz_disable);
 
+	if (config->p2p_pairing_setup)
+		fprintf(f, "p2p_pairing_setup=%d\n", config->p2p_pairing_setup);
+	if (config->p2p_pairing_cache)
+		fprintf(f, "p2p_pairing_cache=%d\n", config->p2p_pairing_cache);
+	if (config->p2p_bootstrap_methods)
+		fprintf(f, "p2p_bootstrap_methods=%d\n",
+			config->p2p_bootstrap_methods);
+	if (config->p2p_pasn_type)
+		fprintf(f, "p2p_pasn_type=%d\n", config->p2p_pasn_type);
+	if (config->p2p_comeback_after)
+		fprintf(f, "p2p_comeback_after=%d\n",
+			config->p2p_comeback_after);
+	if (config->p2p_twt_power_mgmt)
+		fprintf(f, "p2p_twt_power_mgmt=%d\n",
+			config->p2p_twt_power_mgmt);
+	if (config->p2p_chan_switch_req_enable)
+		fprintf(f, "p2p_chan_switch_req_enable=%d\n",
+			config->p2p_chan_switch_req_enable);
+	if (config->p2p_reg_info)
+		fprintf(f, "p2p_reg_info=%d\n", config->p2p_reg_info);
+
 	if (WPA_GET_BE32(config->ip_addr_go))
 		fprintf(f, "ip_addr_go=%u.%u.%u.%u\n",
 			config->ip_addr_go[0], config->ip_addr_go[1],
@@ -1567,8 +1686,8 @@
 	if (config->ftm_initiator)
 		fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator);
 
-	if (config->osu_dir)
-		fprintf(f, "osu_dir=%s\n", config->osu_dir);
+	if (config->twt_requester)
+		fprintf(f, "twt_requester=%d\n", config->twt_requester);
 
 	if (config->fst_group_id)
 		fprintf(f, "fst_group_id=%s\n", config->fst_group_id);
@@ -1639,6 +1758,24 @@
 		fprintf(f, "dik_cipher=%d\n", config->dik_cipher);
 		write_global_bin(f, "dik", config->dik);
 	}
+	if (config->wfa_gen_capa)
+		fprintf(f, "wfa_gen_capa=%d\n", config->wfa_gen_capa);
+	write_global_bin(f, "wfa_gen_capa_supp", config->wfa_gen_capa_supp);
+	write_global_bin(f, "wfa_gen_capa_cert", config->wfa_gen_capa_cert);
+}
+
+static void wpa_config_write_identity(FILE *f, struct wpa_dev_ik *dev_ik)
+{
+	fprintf(f, "\tdik_cipher=%d\n", dev_ik->dik_cipher);
+
+	if (dev_ik->dik)
+		write_global_bin(f, "\tdik", dev_ik->dik);
+
+	if (dev_ik->pmk)
+		write_global_bin(f, "\tpmk", dev_ik->pmk);
+
+	if (dev_ik->pmkid)
+		write_global_bin(f, "\tpmkid", dev_ik->pmkid);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1650,6 +1787,7 @@
 	FILE *f;
 	struct wpa_ssid *ssid;
 	struct wpa_cred *cred;
+	struct wpa_dev_ik *dev_ik;
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	struct wpa_config_blob *blob;
 #endif /* CONFIG_NO_CONFIG_BLOBS */
@@ -1697,13 +1835,20 @@
 		    !ssid->psk_set && !ssid->passphrase)
 			continue; /* do not save invalid network */
 		if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
-		    !ssid->passphrase && !ssid->sae_password)
+		    !ssid->passphrase && !ssid->sae_password &&
+		    !ssid->pmk_valid)
 			continue; /* do not save invalid network */
 		fprintf(f, "\nnetwork={\n");
 		wpa_config_write_network(f, ssid);
 		fprintf(f, "}\n");
 	}
 
+	for (dev_ik = config->identity; dev_ik; dev_ik = dev_ik->next) {
+		fprintf(f, "\nidentity={\n");
+		wpa_config_write_identity(f, dev_ik);
+		fprintf(f, "}\n");
+	}
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	for (blob = config->blobs; blob; blob = blob->next) {
 		ret = wpa_config_write_blob(f, blob);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 3d1bc5e..7d275cd 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -79,6 +79,20 @@
 };
 
 /**
+ * rsn_overriding - RSN overriding
+ *
+ * 0 = Disabled
+ * 1 = Enabled automatically if the driver indicates support
+ * 2 = Forced to be enabled even without driver capability indication
+ */
+enum wpas_rsn_overriding {
+	RSN_OVERRIDING_NOT_SET = -1,
+	RSN_OVERRIDING_DISABLED = 0,
+	RSN_OVERRIDING_AUTO = 1,
+	RSN_OVERRIDING_ENABLED = 2,
+};
+
+/**
  * struct wpa_ssid - Network configuration data
  *
  * This structure includes all the configuration variables for a network. This
@@ -232,6 +246,11 @@
 	char *passphrase;
 
 	/**
+	 * pmk_valid - Whether PMK is valid in case of P2P2 derived from PASN
+	 */
+	bool pmk_valid;
+
+	/**
 	 * sae_password - SAE password
 	 *
 	 * This parameter can be used to set a password for SAE. By default, the
@@ -676,6 +695,15 @@
 	 */
 	size_t num_p2p_clients;
 
+	/**
+	 * p2p2_client_list - Array of P2P2 Clients in a persistent group (GO)
+	 *
+	 * This is an int_array of P2P2 Clients (ID of device Identity block)
+	 * that have joined the persistent group. This is maintained on the GO
+	 *for persistent group entries (disabled == 2).
+	 */
+	int *p2p2_client_list;
+
 #ifndef P2P_MAX_STORED_CLIENTS
 #define P2P_MAX_STORED_CLIENTS 100
 #endif /* P2P_MAX_STORED_CLIENTS */
@@ -963,6 +991,14 @@
 	int macsec_csindex;
 
 	/**
+	 * macsec_icv_indicator - Always include ICV Indicator
+	 * (for compatibility with older MACsec switches)
+	 *
+	 * Range: 0-1 (default: 0)
+	 */
+	int macsec_icv_indicator;
+
+	/**
 	 * mka_ckn - MKA pre-shared CKN
 	 */
 #define MACSEC_CKN_MAX_LEN 32
@@ -1282,6 +1318,23 @@
 	 * ssid_protection - Whether to use SSID protection in 4-way handshake
 	 */
 	bool ssid_protection;
+
+	/**
+	 * rsn_overriding - RSN overriding (per-network override for the global
+	 *	parameter with the same name)
+	 */
+	enum wpas_rsn_overriding rsn_overriding;
+
+	/**
+	 * p2p_mode - P2P R1 only, P2P R2 only, or PCC mode
+	 */
+	enum wpa_p2p_mode p2p_mode;
+
+	/**
+	 * go_dik_id - ID of Device Identity block of group owner
+	 */
+	int go_dik_id;
+
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index a8fc962..94fcec5 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -881,6 +881,8 @@
 	} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
 		dpp_test = atoi(value);
 #endif /* CONFIG_DPP */
+	} else if (os_strcasecmp(cmd, "eapol_2_key_info_set_mask") == 0) {
+		wpa_s->eapol_2_key_info_set_mask = strtoul(value, NULL, 0x10);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_FILS
 	} else if (os_strcasecmp(cmd, "disable_fils") == 0) {
@@ -3012,14 +3014,6 @@
 	}
 #endif /* CONFIG_DPP */
 
-	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
-		ret = os_snprintf(pos, end - pos, "%sOSEN",
-				  pos == start ? "" : "+");
-		if (os_snprintf_error(end - pos, ret))
-			return pos;
-		pos += ret;
-	}
-
 #ifdef CONFIG_SHA384
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
 		ret = os_snprintf(pos, end - pos, "%sEAP-SHA384",
@@ -3097,7 +3091,7 @@
 {
 	char *pos, *end;
 	int ret;
-	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe, *rsnxe;
+	const u8 *ie, *ie2, *p2p, *mesh, *owe, *rsnxe;
 
 	mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
 	p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
@@ -3137,10 +3131,6 @@
 			return -1;
 		pos += ret;
 	}
-	osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
-	if (osen_ie)
-		pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
-					    osen_ie, 2 + osen_ie[1]);
 	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
 	if (owe) {
 		ret = os_snprintf(pos, end - pos,
@@ -3150,7 +3140,7 @@
 		pos += ret;
 	}
 	pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
-	if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
+	if (!ie && !ie2 && (bss->caps & IEEE80211_CAP_PRIVACY)) {
 		ret = os_snprintf(pos, end - pos, "[WEP]");
 		if (os_snprintf_error(end - pos, ret))
 			return -1;
@@ -4460,14 +4450,6 @@
 		pos += ret;
 	}
 #endif /* CONFIG_SHA256 */
-#ifdef CONFIG_HS20
-	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OSEN) {
-		ret = os_snprintf(pos, end - pos, " OSEN");
-		if (os_snprintf_error(end - pos, ret))
-			return pos - buf;
-		pos += ret;
-	}
-#endif /* CONFIG_HS20 */
 
 	return pos - buf;
 }
@@ -4960,6 +4942,28 @@
 		return res;
 	}
 
+#ifdef CONFIG_P2P
+	if (os_strcmp(field, "p2p2") == 0) {
+		if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_P2P_FEATURE_V2)
+			res = os_snprintf(buf, buflen, "supported");
+		else
+			res = os_snprintf(buf, buflen, "not supported");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+
+	if (os_strcmp(field, "pcc_mode") == 0) {
+		if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE)
+			res = os_snprintf(buf, buflen, "supported");
+		else
+			res = os_snprintf(buf, buflen, "not supported");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_P2P */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -5329,7 +5333,7 @@
 	size_t i;
 	int ret;
 	char *pos, *end;
-	const u8 *ie, *ie2, *osen_ie, *mesh, *owe, *rsnxe;
+	const u8 *ie, *ie2, *mesh, *owe, *rsnxe;
 
 	pos = buf;
 	end = buf + buflen;
@@ -5462,10 +5466,6 @@
 				return 0;
 			pos += ret;
 		}
-		osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
-		if (osen_ie)
-			pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
-						    osen_ie, 2 + osen_ie[1]);
 		owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
 		if (owe) {
 			ret = os_snprintf(
@@ -5476,7 +5476,7 @@
 			pos += ret;
 		}
 		pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
-		if (!ie && !ie2 && !osen_ie &&
+		if (!ie && !ie2 &&
 		    (bss->caps & IEEE80211_CAP_PRIVACY)) {
 			ret = os_snprintf(pos, end - pos, "[WEP]");
 			if (os_snprintf_error(end - pos, ret))
@@ -5664,12 +5664,6 @@
 				   anqp->hs20_connection_capability);
 		pos = anqp_add_hex(pos, end, "hs20_operating_class",
 				   anqp->hs20_operating_class);
-		pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
-				   anqp->hs20_osu_providers_list);
-		pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata",
-				   anqp->hs20_operator_icon_metadata);
-		pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list",
-				   anqp->hs20_osu_providers_nai_list);
 #endif /* CONFIG_HS20 */
 
 		dl_list_for_each(elem, &anqp->anqp_elems,
@@ -6320,7 +6314,7 @@
 		return -1;
 	}
 
-	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+	return wpas_p2p_prov_disc(wpa_s, addr, NULL, 0, WPAS_P2P_PD_FOR_ASP,
 				  p2ps_prov);
 }
 
@@ -6349,11 +6343,34 @@
 
 	p2ps_prov->pd_seeker = 1;
 
-	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+	return wpas_p2p_prov_disc(wpa_s, addr, NULL, 0, WPAS_P2P_PD_FOR_ASP,
 				  p2ps_prov);
 }
 
 
+static bool bootstrap_pwd_required(u16 bootstrap)
+{
+	switch (bootstrap) {
+	case P2P_PBMA_OPPORTUNISTIC:
+		return false;
+	case P2P_PBMA_PIN_CODE_DISPLAY:
+	case P2P_PBMA_PASSPHRASE_DISPLAY:
+	case P2P_PBMA_QR_DISPLAY:
+	case P2P_PBMA_NFC_TAG:
+	case P2P_PBMA_PIN_CODE_KEYPAD:
+	case P2P_PBMA_PASSPHRASE_KEYPAD:
+	case P2P_PBMA_QR_SCAN:
+	case P2P_PBMA_NFC_READER:
+		return true;
+	case P2P_PBMA_SERVICE_MANAGED:
+	case P2P_PBMA_HANDSHAKE_SKIP:
+		return false;
+	}
+
+	return false;
+}
+
+
 static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
 			    char *buf, size_t buflen)
 {
@@ -6376,7 +6393,7 @@
 	size_t group_ssid_len = 0;
 	int he;
 	bool allow_6ghz;
-	bool p2p2;
+	bool p2p2, skip_prov;
 	u16 bootstrap = 0;
 	const char *password = NULL;
 	char *token, *context = NULL;
@@ -6393,7 +6410,7 @@
 	 * [persistent|persistent=<network id>]
 	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
 	 * [ht40] [vht] [he] [edmg] [auto] [ssid=<hexdump>]
-	 * [p2p2] [bstrapmethod=<value>] [password=<string>]
+	 * [p2p2] [skip_prov] [bstrapmethod=<value>] [password=<string>]
 	 */
 
 	if (hwaddr_aton(cmd, addr))
@@ -6429,6 +6446,7 @@
 	he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
 	edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg;
 	p2p2 = os_strstr(pos, " p2p2") != NULL;
+	skip_prov = os_strstr(pos, " skip_prov") != NULL;
 
 	pos2 = os_strstr(pos, " go_intent=");
 	if (pos2) {
@@ -6505,7 +6523,6 @@
 	if (pos2) {
 		pos2 += 13;
 		bootstrap = atoi(pos2);
-		pd = true;
 	}
 
 	while ((token = str_token(pos, " ", &context))) {
@@ -6515,12 +6532,18 @@
 		}
 	}
 
+	if (bootstrap_pwd_required(bootstrap) && !password) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE: This P2P2 bootstrap method requires password");
+		return -1;
+	}
+
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, automatic, join,
 				   auth, go_intent, freq, freq2, persistent_id,
 				   pd, ht40, vht, max_oper_chwidth, he, edmg,
 				   group_ssid, group_ssid_len, allow_6ghz, p2p2,
-				   bootstrap, password);
+				   bootstrap, password, skip_prov);
 	if (new_pin == -2) {
 		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
 		return 25;
@@ -6558,10 +6581,11 @@
 static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	u8 addr[ETH_ALEN];
-	char *pos;
+	char *pos, *pos2;
 	enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
+	u16 bootstrap = 0;
 
-	/* <addr> <config method> [join|auto] */
+	/* <addr> <config method> [join|auto] [bstrapmethod=<value>] */
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -6576,7 +6600,13 @@
 	else if (os_strstr(pos, " auto") != NULL)
 		use = WPAS_P2P_PD_AUTO;
 
-	return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
+	pos2 = os_strstr(pos, "bstrapmethod=");
+	if (pos2) {
+		pos2 += 13;
+		bootstrap = atoi(pos2);
+	}
+
+	return wpas_p2p_prov_disc(wpa_s, addr, pos, bootstrap, use, NULL);
 }
 
 
@@ -6594,6 +6624,52 @@
 }
 
 
+static int p2p_ctrl_validate_dira(struct wpa_supplicant *wpa_s, char *cmd,
+				  char *buf, size_t buflen)
+{
+	char *pos, *pos2;
+	u8 addr[ETH_ALEN];
+	u8 nonce[DEVICE_IDENTITY_NONCE_LEN];
+	u8 tag[DEVICE_IDENTITY_TAG_LEN];
+	int id;
+	int res;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+
+	pos2 = os_strstr(pos, "nonce=");
+	if (pos2) {
+		pos2 += 6;
+		if (hexstr2bin(pos2, nonce, sizeof(nonce)) < 0)
+			return -1;
+	} else {
+		return -1;
+	}
+
+	pos2 = os_strstr(pos, "tag=");
+	if (pos2) {
+		pos2 += 4;
+		if (hexstr2bin(pos2, tag, sizeof(tag)) < 0)
+			return -1;
+	} else {
+		return -1;
+	}
+
+	id = wpas_p2p_validate_dira(wpa_s, addr, 0, nonce, tag);
+	if (id <= 0)
+		return -1;
+
+	res = os_snprintf(buf, buflen, "%u", id);
+	if (os_snprintf_error(buflen, res))
+		return -1;
+	return res;
+}
+
+
 static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
 				  char *buf, size_t buflen)
 {
@@ -7067,14 +7143,16 @@
 {
 	char *pos;
 	int id;
-	struct wpa_ssid *ssid;
+	struct wpa_ssid *ssid = NULL;
 	u8 *_peer = NULL, peer[ETH_ALEN];
 	int freq = 0, pref_freq = 0;
 	int ht40, vht, he, max_oper_chwidth, chwidth = 0, freq2 = 0;
 	int edmg;
 	bool allow_6ghz;
+	bool p2p2;
 
-	id = atoi(cmd);
+	p2p2 = os_strstr(cmd, " p2p2") != NULL;
+
 	pos = os_strstr(cmd, " peer=");
 	if (pos) {
 		pos += 6;
@@ -7082,11 +7160,19 @@
 			return -1;
 		_peer = peer;
 	}
-	ssid = wpa_config_get_network(wpa_s->conf, id);
-	if (ssid == NULL || ssid->disabled != 2) {
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
-			   "for persistent P2P group",
-			   id);
+
+	if (os_strncmp(cmd, "persistent=", 11) == 0) {
+		id = atoi(cmd + 11);
+		ssid = wpa_config_get_network(wpa_s->conf, id);
+		if (!ssid || ssid->disabled != 2) {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL_IFACE: Could not find SSID id=%d for persistent P2P group",
+				   id);
+			return -1;
+		}
+	} else if (p2p2 && !_peer) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE: Could not find peer for persistent P2P group");
 		return -1;
 	}
 
@@ -7131,7 +7217,7 @@
 
 	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
 			       max_oper_chwidth, pref_freq, he, edmg,
-			       allow_6ghz);
+			       allow_6ghz, p2p2);
 }
 
 
@@ -7171,8 +7257,8 @@
 
 static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
 {
-	if (os_strncmp(cmd, "persistent=", 11) == 0)
-		return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
+	if (os_strncmp(cmd, "persistent", 10) == 0)
+		return p2p_ctrl_invite_persistent(wpa_s, cmd);
 	if (os_strncmp(cmd, "group=", 6) == 0)
 		return p2p_ctrl_invite_group(wpa_s, cmd + 6);
 
@@ -7200,13 +7286,15 @@
 					     vht_center_freq2, ht40, vht,
 					     vht_chwidth, he, edmg,
 					     NULL, 0, 0, allow_6ghz, 0,
-					     go_bssid);
+					     go_bssid, NULL, NULL, NULL, 0);
 }
 
 
 static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	int freq = 0, persistent = 0, group_id = -1;
+	bool p2p2 = false;
+	int p2pmode = WPA_P2P_MODE_WFD_R1;
 	bool allow_6ghz = false;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
@@ -7222,7 +7310,8 @@
 	while ((token = str_token(cmd, " ", &context))) {
 		if (sscanf(token, "freq2=%d", &freq2) == 1 ||
 		    sscanf(token, "persistent=%d", &group_id) == 1 ||
-		    sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
+		    sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1 ||
+		    sscanf(token, "p2pmode=%d", &p2pmode) == 1) {
 			continue;
 #ifdef CONFIG_ACS
 		} else if (os_strcmp(token, "freq=acs") == 0) {
@@ -7243,6 +7332,8 @@
 			persistent = 1;
 		} else if (os_strcmp(token, "allow_6ghz") == 0) {
 			allow_6ghz = true;
+		} else if (os_strcmp(token, "p2p2") == 0) {
+			p2p2 = true;
 		} else if (os_strncmp(token, "go_bssid=", 9) == 0) {
 			if (hwaddr_aton(token + 9, go_bssid_buf))
 				return -1;
@@ -7293,8 +7384,12 @@
 						     edmg, allow_6ghz,
 						     go_bssid);
 
+	if (p2pmode < WPA_P2P_MODE_WFD_R1 || p2pmode > WPA_P2P_MODE_WFD_PCC)
+		return -1;
+
 	return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
-				  max_oper_chwidth, he, edmg, allow_6ghz);
+				  max_oper_chwidth, he, edmg, allow_6ghz, p2p2,
+				  (enum wpa_p2p_mode) p2pmode);
 }
 
 
@@ -7697,6 +7792,53 @@
 		return 0;
 	}
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (os_strcmp(cmd, "pairing_setup") == 0) {
+		p2p_set_pairing_setup(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "pairing_cache") == 0) {
+		p2p_set_pairing_cache(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "supported_bootstrapmethods") == 0) {
+		p2p_set_bootstrapmethods(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "pasn_type") == 0) {
+		p2p_set_pasn_type(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "comeback_after") == 0) {
+		p2p_set_comeback_after(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "reginfo") == 0) {
+		p2p_set_reg_info(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "twt_power_mgmt") == 0) {
+		p2p_set_twt_power_mgmt(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "chan_switch_req_enable") == 0) {
+		p2p_set_chan_switch_req_enable(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "inv_oper_freq") == 0) {
+		p2p_set_invitation_op_freq(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
 		   cmd);
 
@@ -7712,12 +7854,15 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	os_free(wpa_s->get_pref_freq_list_override);
 	wpa_s->get_pref_freq_list_override = NULL;
+	p2p_set_invitation_op_freq(wpa_s->global->p2p, -1);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpas_p2p_stop_find(wpa_s);
 	wpa_s->parent->p2ps_method_config_any = 0;
 	if (wpa_s->global->p2p)
 		p2p_flush(wpa_s->global->p2p);
+
+	wpa_s->p2p2 = false;
 }
 
 
@@ -7804,6 +7949,26 @@
 	return wpas_p2p_lo_start(wpa_s, freq, period, interval, count);
 }
 
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int p2p_ctrl_pmk_get(struct wpa_supplicant *wpa_s, char *buf,
+			    size_t buflen)
+{
+	const u8 *pmk;
+	size_t pmk_len;
+
+	/* Return the PMK from the first identity entry. This assumes test
+	 * cases to remove all indentities at the beginning so that only one
+	 * entry is available. */
+	if (!wpa_s->conf->identity || !wpa_s->conf->identity->pmk)
+		return -1;
+
+	pmk_len = wpabuf_len(wpa_s->conf->identity->pmk);
+	pmk = wpabuf_head(wpa_s->conf->identity->pmk);
+
+	return wpa_snprintf_hex(buf, buflen, pmk, pmk_len);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_P2P */
 
 
@@ -8144,7 +8309,7 @@
 	if (subtypes == 0)
 		return -1;
 
-	return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0);
+	return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0);
 }
 
 
@@ -8167,7 +8332,7 @@
 
 	ret = hs20_anqp_send_req(wpa_s, addr,
 				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
-				 buf, len, 0);
+				 buf, len);
 
 	os_free(buf);
 
@@ -8213,77 +8378,12 @@
 
 	ret = hs20_anqp_send_req(wpa_s, dst_addr,
 				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
-				 buf, len, 0);
+				 buf, len);
 	os_free(buf);
 
 	return ret;
 }
 
-
-static int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply,
-			 int buflen)
-{
-	u8 dst_addr[ETH_ALEN];
-	int used;
-	char *ctx = NULL, *icon, *poffset, *psize;
-
-	used = hwaddr_aton2(cmd, dst_addr);
-	if (used < 0)
-		return -1;
-	cmd += used;
-
-	icon = str_token(cmd, " ", &ctx);
-	poffset = str_token(cmd, " ", &ctx);
-	psize = str_token(cmd, " ", &ctx);
-	if (!icon || !poffset || !psize)
-		return -1;
-
-	wpa_s->fetch_osu_icon_in_progress = 0;
-	return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize),
-			     reply, buflen);
-}
-
-
-static int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd)
-{
-	u8 dst_addr[ETH_ALEN];
-	int used;
-	char *icon;
-
-	if (!cmd[0])
-		return hs20_del_icon(wpa_s, NULL, NULL);
-
-	used = hwaddr_aton2(cmd, dst_addr);
-	if (used < 0)
-		return -1;
-
-	while (cmd[used] == ' ')
-		used++;
-	icon = cmd[used] ? &cmd[used] : NULL;
-
-	return hs20_del_icon(wpa_s, dst_addr, icon);
-}
-
-
-static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem)
-{
-	u8 dst_addr[ETH_ALEN];
-	int used;
-	char *icon;
-
-	used = hwaddr_aton2(cmd, dst_addr);
-	if (used < 0)
-		return -1;
-
-	while (cmd[used] == ' ')
-		used++;
-	icon = &cmd[used];
-
-	wpa_s->fetch_osu_icon_in_progress = 0;
-	return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
-				  (u8 *) icon, os_strlen(icon), inmem);
-}
-
 #endif /* CONFIG_HS20 */
 
 
@@ -8676,6 +8776,7 @@
 	ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen);
 #ifdef CONFIG_P2P
 	if (ret == 0) {
+#ifdef CONFIG_P2P
 		if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
 			struct p2p_data *p2p = wpa_s->global->p2p;
 			if (p2p) {
@@ -8686,6 +8787,7 @@
 				p2p_set_country(p2p, country);
 			}
 		}
+#endif /* CONFIG_P2P */
 		ret = os_snprintf(buf, buflen, "%s\n", "OK");
 		if (os_snprintf_error(buflen, ret))
 			ret = -1;
@@ -8908,13 +9010,6 @@
 
 	wnm_btm_reset(wpa_s);
 
-#ifdef CONFIG_INTERWORKING
-#ifdef CONFIG_HS20
-	hs20_cancel_fetch_osu(wpa_s);
-	hs20_del_icon(wpa_s, NULL, NULL);
-#endif /* CONFIG_HS20 */
-#endif /* CONFIG_INTERWORKING */
-
 	wpa_s->ext_mgmt_frame_handling = 0;
 	wpa_s->ext_eapol_frame_io = 0;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -8931,6 +9026,7 @@
 	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
 	wpa_sm_set_test_eapol_m2_elems(wpa_s->wpa, NULL);
 	wpa_sm_set_test_eapol_m4_elems(wpa_s->wpa, NULL);
+	wpa_sm_set_test_rsnxe_data(wpa_s->wpa, NULL, NULL);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M2, 0);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M4, 0);
 	os_free(wpa_s->get_pref_freq_list_override);
@@ -8959,6 +9055,7 @@
 	wpa_s->oci_freq_override_fils_assoc = 0;
 	wpa_s->oci_freq_override_wnm_sleep = 0;
 	wpa_s->disable_eapol_g2_tx = 0;
+	wpa_s->eapol_2_key_info_set_mask = 0;
 	wpa_s->test_assoc_comeback_type = -1;
 #ifdef CONFIG_DPP
 	os_free(wpa_s->dpp_config_obj_override);
@@ -10302,6 +10399,29 @@
 }
 
 
+static int wpas_ctrl_test_rsnxe_data(struct wpa_supplicant *wpa_s,
+				     const char *cmd)
+{
+	struct wpabuf *data = NULL, *mask = NULL;
+	char *pos;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return -1;
+	*pos++ = '\0';
+
+	if (wpas_get_hex_buf(cmd, &data) < 0 ||
+	    wpas_get_hex_buf(pos, &mask) < 0 ||
+	    wpa_sm_set_test_rsnxe_data(wpa_s->wpa, data, mask) < 0) {
+		wpabuf_free(data);
+		wpabuf_free(mask);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
 {
 	u8 zero[WPA_TK_MAX_LEN];
@@ -10365,6 +10485,7 @@
 	params.key_mgmt_suite = wpa_s->key_mgmt;
 	params.wpa_proto = wpa_s->wpa_proto;
 	params.mgmt_frame_protection = wpa_s->sme.mfp;
+	params.spp_amsdu = wpa_s->sme.spp_amsdu;
 	params.rrm_used = wpa_s->rrm.rrm_used;
 	if (wpa_s->sme.prev_bssid_set)
 		params.prev_bssid = wpa_s->sme.prev_bssid;
@@ -10856,6 +10977,24 @@
 #endif /* CONFIG_AP */
 }
 
+#ifdef CONFIG_P2P
+#ifdef CONFIG_PASN
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int p2p_ctrl_get_pasn_ptk(struct wpa_supplicant *wpa_s, char *buf,
+				 size_t buflen)
+{
+	const u8 *ptk;
+	size_t ptk_len;
+
+	if (wpas_p2p_get_pasn_ptk(wpa_s, &ptk, &ptk_len))
+		return -1;
+	return wpa_snprintf_hex(buf, buflen, ptk, ptk_len);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#endif /* CONFIG_PASN */
+#endif /* CONFIG_P2P */
 
 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 
@@ -12568,6 +12707,53 @@
 	return ret;
 }
 
+
+static int wpas_ctrl_nan_unpause_publish(struct wpa_supplicant *wpa_s,
+					 char *cmd)
+{
+	char *token, *context = NULL;
+	int publish_id = 0;
+	int peer_instance_id = 0;
+	u8 peer_addr[ETH_ALEN];
+
+	os_memset(peer_addr, 0, ETH_ALEN);
+
+	while ((token = str_token(cmd, " ", &context))) {
+		if (sscanf(token, "publish_id=%i", &publish_id) == 1)
+			continue;
+
+		if (sscanf(token, "peer_instance_id=%i",
+			   &peer_instance_id) == 1)
+			continue;
+
+		if (os_strncmp(token, "peer=", 5) == 0) {
+			if (hwaddr_aton(token + 5, peer_addr) < 0)
+				return -1;
+			continue;
+		}
+
+		wpa_printf(MSG_INFO,
+			   "CTRL: Invalid NAN_UNPAUSE_PUBLISH parameter: %s",
+			   token);
+		return -1;
+	}
+
+	if (publish_id <= 0) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: Invalid or missing NAN_UNPAUSE_PUBLIH publish_id");
+		return -1;
+	}
+
+	if (is_zero_ether_addr(peer_addr)) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: Invalid or missing NAN_UNPAUSE_PUBLISH address");
+		return -1;
+	}
+
+	return wpas_nan_usd_unpause_publish(wpa_s, publish_id, peer_instance_id,
+					    peer_addr);
+}
+
 #endif /* CONFIG_NAN_USD */
 
 
@@ -12870,6 +13056,17 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
 		reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
+	} else if (os_strcmp(buf, "P2P_GET_DIRA") == 0) {
+		reply_len = wpas_p2p_get_dira(wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "P2P_VALIDATE_DIRA ", 18) == 0) {
+		reply_len = p2p_ctrl_validate_dira(wpa_s, buf + 18,
+						   reply, reply_size);
+#ifdef CONFIG_PASN
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strcmp(buf, "P2P_GET_PASNPTK") == 0) {
+		reply_len = p2p_ctrl_get_pasn_ptk(wpa_s, reply, reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_PASN */
 	} else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
 		reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
 						   reply_size);
@@ -12936,6 +13133,13 @@
 	} else if (os_strcmp(buf, "P2P_LO_STOP") == 0) {
 		if (wpas_p2p_lo_stop(wpa_s))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_REMOVE_IDENTITY") == 0) {
+		if (wpas_p2p_remove_all_identity(wpa_s))
+			reply_len = -1;
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strncmp(buf, "P2P_PMK_GET", 12) == 0) {
+		reply_len = p2p_ctrl_pmk_get(wpa_s, reply, reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_WIFI_DISPLAY
 	} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
@@ -12988,25 +13192,6 @@
 	} else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
 		if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
 			reply_len = -1;
-	} else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
-		if (hs20_icon_request(wpa_s, buf + 18, 0) < 0)
-			reply_len = -1;
-	} else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) {
-		if (hs20_icon_request(wpa_s, buf + 14, 1) < 0)
-			reply_len = -1;
-	} else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) {
-		reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size);
-	} else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) {
-		if (del_hs20_icon(wpa_s, buf + 14) < 0)
-			reply_len = -1;
-	} else if (os_strcmp(buf, "FETCH_OSU") == 0) {
-		if (hs20_fetch_osu(wpa_s, 0) < 0)
-			reply_len = -1;
-	} else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) {
-		if (hs20_fetch_osu(wpa_s, 1) < 0)
-			reply_len = -1;
-	} else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
-		hs20_cancel_fetch_osu(wpa_s);
 #endif /* CONFIG_HS20 */
 	} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
 	{
@@ -13387,6 +13572,9 @@
 	} else if (os_strncmp(buf, "ML_PROBE_REQ ", 13) == 0) {
 		if (wpas_ctrl_ml_probe(wpa_s, buf + 13))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_RSNXE_DATA ", 16) == 0) {
+		if (wpas_ctrl_test_rsnxe_data(wpa_s, buf + 16) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
 		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
@@ -13593,6 +13781,11 @@
 	} else if (os_strncmp(buf, "NAN_TRANSMIT ", 13) == 0) {
 		if (wpas_ctrl_nan_transmit(wpa_s, buf + 13) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "NAN_UNPAUSE_PUBLISH ", 20) == 0) {
+		if (wpas_ctrl_nan_unpause_publish(wpa_s, buf + 20) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "NAN_FLUSH") == 0) {
+		wpas_nan_usd_flush(wpa_s);
 #endif /* CONFIG_NAN_USD */
 #ifdef CONFIG_PASN
 	} else if (os_strncmp(buf, "PASN_START ", 11) == 0) {
@@ -13631,6 +13824,14 @@
 	} else if (os_strcmp(buf, "MLO_SIGNAL_POLL") == 0) {
 		reply_len = wpas_ctrl_iface_mlo_signal_poll(wpa_s, reply,
 							    reply_size);
+	} else if (os_strcmp(buf, "NEW_RANDOM_MAC_ADDRESS") == 0) {
+		enum wpas_mac_addr_style mac_addr_style =
+			wpa_s->conf->preassoc_mac_addr;
+
+		wpa_s->conf->preassoc_mac_addr = WPAS_MAC_ADDR_STYLE_RANDOM;
+		if (wpas_update_random_addr_disassoc(wpa_s) != 1)
+			reply_len = -1;
+		wpa_s->conf->preassoc_mac_addr = mac_addr_style;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -13948,6 +14149,8 @@
 #ifdef CONFIG_AP
 		"STA-FIRST",
 #endif /* CONFIG_AP */
+		"P2P_GET_DIRA",
+		"P2P_VALIDATE_DIRA",
 		NULL
 	};
 	static const char * prefix[] = {
@@ -13986,6 +14189,7 @@
 		"NFC_REPORT_HANDOVER ",
 		"P2P_ASP_PROVISION ",
 		"P2P_ASP_PROVISION_RESP ",
+		"NAN_",
 #ifdef CONFIG_AP
 		"STA ",
 		"STA-NEXT ",
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index 27003eb..a3f32c3 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -1159,3 +1159,38 @@
 
 	os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
 }
+
+
+dbus_bool_t wpa_dbus_dict_entry_is_int(const struct wpa_dbus_dict_entry *entry)
+{
+	return entry->type == DBUS_TYPE_BYTE ||
+		entry->type == DBUS_TYPE_INT16 ||
+		entry->type == DBUS_TYPE_UINT16 ||
+		entry->type == DBUS_TYPE_INT32 ||
+		entry->type == DBUS_TYPE_UINT32 ||
+		entry->type == DBUS_TYPE_INT64 ||
+		entry->type == DBUS_TYPE_UINT64;
+}
+
+
+int wpa_dbus_dict_entry_get_int(const struct wpa_dbus_dict_entry *entry)
+{
+	switch (entry->type) {
+	case DBUS_TYPE_BYTE:
+		return entry->byte_value;
+	case DBUS_TYPE_INT16:
+		return entry->int16_value;
+	case DBUS_TYPE_UINT16:
+		return entry->uint16_value;
+	case DBUS_TYPE_INT32:
+		return entry->int32_value;
+	case DBUS_TYPE_UINT32:
+		return entry->uint32_value;
+	case DBUS_TYPE_INT64:
+		return entry->int64_value;
+	case DBUS_TYPE_UINT64:
+		return entry->uint64_value;
+	}
+
+	return -1;
+}
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 1d33689..15c8aba 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -158,4 +158,7 @@
 
 void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry);
 
+dbus_bool_t wpa_dbus_dict_entry_is_int(const struct wpa_dbus_dict_entry *entry);
+int wpa_dbus_dict_entry_get_int(const struct wpa_dbus_dict_entry *entry);
+
 #endif  /* DBUS_DICT_HELPERS_H */
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 5ad5bcd..7893f35 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2332,6 +2332,116 @@
 }
 
 
+/**
+ * wpas_dbus_signal_p2p_bootstrap_req - Signals BootstrappingRequest event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @src: Source address of the message triggering this notification
+ * @bootstrap_method: Peer's bootstrap method
+ *
+ * Sends a signal to notify that a peer P2P Device is requesting bootstrapping
+ * negotiation with us.
+ */
+void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+					const u8 *src, u16 bootstrap_method)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(src));
+	path = peer_obj_path;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "BootstrappingRequest");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+					    &bootstrap_method))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_bootstrap_rsp - Signals BootstrappingResponse event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @src: Source address of the peer with which bootstrapping is done
+ * @status: Status of Bootstrapping handshake
+ * @bootstrap_method: Peer's bootstrap method if status is success
+ *
+ * Sends a signal to notify that a peer P2P Device is requesting bootstrapping
+ * negotiation with us.
+ */
+void wpas_dbus_signal_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s,
+					const u8 *src, int status,
+					u16 bootstrap_method)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(src));
+	path = peer_obj_path;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "BootstrappingCompleted");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32,
+					    &status) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+					    &bootstrap_method))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
 #endif /* CONFIG_P2P */
 
 
@@ -2497,41 +2607,6 @@
 
 
 /**
- * wpas_dbus_sta_signal_prop_changed - Signals change of STA property
- * @wpa_s: %wpa_supplicant network interface data
- * @property: indicates which property has changed
- * @address: unique BSS identifier
- *
- * Sends PropertyChanged signals with path, interface, and arguments depending
- * on which property has changed.
- */
-void wpas_dbus_sta_signal_prop_changed(struct wpa_supplicant *wpa_s,
-				       enum wpas_dbus_bss_prop property,
-				       u8 address[ETH_ALEN])
-{
-	char path[WPAS_DBUS_OBJECT_PATH_MAX];
-	char *prop;
-
-	switch (property) {
-	case WPAS_DBUS_STA_PROP_ADDRESS:
-		prop = "Address";
-		break;
-	default:
-		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
-			   __func__, property);
-		return;
-	}
-
-	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
-		    "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR,
-		    wpa_s->dbus_new_path, MAC2STR(address));
-
-	wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
-				       WPAS_DBUS_NEW_IFACE_STA, prop);
-}
-
-
-/**
  * wpas_dbus_signal_debug_level_changed - Signals change of debug param
  * @global: wpa_global structure
  *
@@ -3771,6 +3846,52 @@
 	  },
 	},
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_NAN_USD
+	{ "NANPublish", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_publish,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "publish_id", "u", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANCancelPublish", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_cancel_publish,
+	  {
+		  { "publish_id", "u", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "NANUpdatePublish", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_update_publish,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "NANSubscribe", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_subscribe,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "subscribe_id", "u", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANCancelSubscribe", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_cancel_subscribe,
+	  {
+		  { "subscribe_id", "u", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "NANTransmit", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_transmit,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_NAN_USD */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -4273,6 +4394,20 @@
 		  END_ARGS
 	  }
 	},
+	{ "BootstrappingRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "bootstrap_method", "q", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "BootstrappingCompleted", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "status", "i", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_AP
 	{ "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
@@ -4384,6 +4519,40 @@
 	  }
 	},
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_NAN_USD
+	{ "NANDiscoveryResult", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANReplied", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANReceive", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANPublishTerminated", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "publish_id", "u", ARG_OUT },
+		  { "reason", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANSubscribeTerminated", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "subscribe_id", "u", ARG_OUT },
+		  { "reason", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_NAN_USD */
 	{ NULL, NULL, { END_ARGS } }
 };
 
@@ -5253,3 +5422,259 @@
 	dbus_message_unref(msg);
 }
 #endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_NAN_USD
+
+/**
+ * wpas_dbus_signal_nan_discovery_result - Send NANDiscoveryResult signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @srv_proto_type: Service Protocol Type
+ * @subscribe_id: Subscribe ID of the session
+ * @peer_publish_id: Publish ID of the sender
+ * @peer_addr: MAC address of the peer device
+ * @ssi: Service specific information payload
+ * @ssi_len: Length of the SSI field
+ *
+ * This is used to indicate the NAN DE DiscoveryResult event.
+ */
+void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
+					   enum nan_service_protocol_type
+					   srv_proto_type,
+					   int subscribe_id,
+					   int peer_publish_id,
+					   const u8 *peer_addr,
+					   bool fsd, bool fsd_gas,
+					   const u8 *ssi, size_t ssi_len)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	char addr_str[20];
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANDiscoveryResult");
+	if (!msg)
+		return;
+
+	snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr));
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "subscribe_id",
+					 subscribe_id) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "publish_id",
+					 peer_publish_id) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "fsd", fsd) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "fsd_gas", fsd_gas) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "srv_proto_type",
+					 srv_proto_type) ||
+	    (ssi &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter,
+					      "ssi",
+					      (const char *) ssi,
+					      ssi_len)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_replied - Send NANReplied signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @srv_proto_type: Service Protocol Type
+ * @publish_id: Publish id of the session
+ * @peer_subscribe_id: Subscribe id of the sender
+ * @peer_addr: MAC address of the peer device
+ * @ssi: Service specific information payload
+ * @ssi_len: Length of the SSI field
+ *
+ * This is used to indicate the NAN DE Replied event.
+ */
+void wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s,
+				  enum nan_service_protocol_type srv_proto_type,
+				  int publish_id,
+				  int peer_subscribe_id,
+				  const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	char addr_str[20];
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANReplied");
+	if (!msg)
+		return;
+
+	snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr));
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "publish_id",
+					 publish_id) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "subscribe_id",
+					 peer_subscribe_id) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "srv_proto_type",
+					 srv_proto_type) ||
+	    (ssi &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "ssi",
+					      (const char *) ssi,
+					      ssi_len)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_receive - Send NANReceive signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: The original publish_id or subscribe_id
+ * @peer_id: Peer instance identifier
+ * @peer_addr: Address of the sender
+ * @ssi: Service specific information payload
+ * @ssi_len: Length of the SSI
+ *
+ * This is used to indicate the NAN DE Receive event to notify reception of a
+ * follow-up frame.
+ */
+void wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s,
+				  int id, int peer_id, const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	char addr_str[20];
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANReceive");
+	if (!msg)
+		return;
+
+	snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr));
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "id", id) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "peer_id", peer_id) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) ||
+	    (ssi &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "ssi",
+					      (const char *) ssi,
+					      ssi_len)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_publish_terminated - Send NANPublishTerminated signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @publish_id: The publish_id of the session
+ * @reason: The reason of the termination
+ *
+ * This is used to indicate the NAN DE PublishTerminated event to notify when
+ * the session has expired.
+ */
+void wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s,
+					     int publish_id,
+					     const char *reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	dbus_uint32_t pub_id = publish_id;
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANPublishTerminated");
+	if (!msg)
+		return;
+
+	if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &pub_id,
+				      DBUS_TYPE_INVALID) ||
+	    !dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
+				      DBUS_TYPE_INVALID))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_subscribe_terminated - Send NANSubscribeTerminated signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @subscribe_id: The subscribe_id of the session
+ * @reason: The reason of the termination
+ *
+ * This is used to indicate the NAN DE SubscribeTerminated event to notify when
+ * the session has expired.
+ */
+void wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s,
+					       int subscribe_id,
+					       const char *reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	dbus_uint32_t sub_id = subscribe_id;
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANSubscribeTerminated");
+	if (!msg)
+		return;
+
+	if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &sub_id,
+				      DBUS_TYPE_INVALID) ||
+	    !dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
+				      DBUS_TYPE_INVALID))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_NAN_USD */
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 1db5fe8..d648435 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -21,6 +21,7 @@
 struct wps_event_m2d;
 struct wps_event_fail;
 struct wps_credential;
+enum nan_service_protocol_type;
 
 enum wpas_dbus_prop {
 	WPAS_DBUS_PROP_AP_SCAN,
@@ -265,6 +266,11 @@
 					      const u8 *sa, const u8 *dev_addr,
 					      const u8 *bssid, int id,
 					      int op_freq);
+void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+					const u8 *src, u16 bootstrap_method);
+void wpas_dbus_signal_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s,
+					const u8 *src, int status,
+					u16 bootstrap_method);
 void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid);
 void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
@@ -285,6 +291,28 @@
 				      const u8 *dst, const char *result);
 void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
 					  const char *url);
+void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
+					   enum nan_service_protocol_type
+					   srv_proto_type,
+					   int subscribe_id,
+					   int peer_publish_id,
+					   const u8 *peer_addr,
+					   bool fsd, bool fsd_gas,
+					   const u8 *ssi, size_t ssi_len);
+void wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s,
+				  enum nan_service_protocol_type srv_proto_type,
+				  int publish_id, int peer_subscribe_id,
+				  const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len);
+void wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s, int id,
+				  int peer_id, const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len);
+void wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s,
+					     int publish_id,
+					     const char *reason);
+void wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s,
+					       int subscribe_id,
+					       const char *reason);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -617,6 +645,19 @@
 }
 
 static inline
+void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+					const u8 *src, u16 bootstrap_method)
+{
+}
+
+static inline
+void wpas_dbus_signal_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s,
+					const u8 *src, int status,
+					u16 bootstrap_method)
+{
+}
+
+static inline
 void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid)
 {
@@ -668,6 +709,45 @@
 {
 }
 
+static inline void
+wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
+				      enum nan_service_protocol_type
+				      srv_proto_type,
+				      int subscribe_id,
+				      int peer_publish_id, const u8 *peer_addr,
+				      bool fsd, bool fsd_gas,
+				      const u8 *ssi, size_t ssi_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s,
+			     enum nan_service_protocol_type srv_proto_type,
+			     int publish_id, int peer_subscribe_id,
+			     const u8 *peer_addr, const u8 *ssi, size_t ssi_len)
+{
+}
+
+
+static inline void
+wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s,
+			     int id, int peer_id, const u8 *peer_addr,
+			     const u8 *ssi, size_t ssi_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s,
+					int publish_id, const char *reason)
+{
+}
+
+static inline void
+wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s,
+					  int subscribe_id, const char *reason)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 52e35a7..e43bf83 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -12,6 +12,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/nan_de.h"
 #include "eap_peer/eap_methods.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
@@ -27,6 +28,7 @@
 #include "../autoscan.h"
 #include "../ap.h"
 #include "../interworking.h"
+#include "../nan_usd.h"
 #include "dbus_new_helpers.h"
 #include "dbus_new.h"
 #include "dbus_new_handlers.h"
@@ -5899,24 +5901,6 @@
 			    wpabuf_head(anqp->hs20_operating_class),
 			    wpabuf_len(anqp->hs20_operating_class)))
 			goto nomem;
-		if (anqp->hs20_osu_providers_list &&
-		    !wpa_dbus_dict_append_byte_array(
-			    &iter_dict, "HS20OSUProvidersList",
-			    wpabuf_head(anqp->hs20_osu_providers_list),
-			    wpabuf_len(anqp->hs20_osu_providers_list)))
-			goto nomem;
-		if (anqp->hs20_operator_icon_metadata &&
-		    !wpa_dbus_dict_append_byte_array(
-			    &iter_dict, "HS20OperatorIconMetadata",
-			    wpabuf_head(anqp->hs20_operator_icon_metadata),
-			    wpabuf_len(anqp->hs20_operator_icon_metadata)))
-			goto nomem;
-		if (anqp->hs20_osu_providers_nai_list &&
-		    !wpa_dbus_dict_append_byte_array(
-			    &iter_dict, "HS20OSUProvidersNAIList",
-			    wpabuf_head(anqp->hs20_osu_providers_nai_list),
-			    wpabuf_len(anqp->hs20_osu_providers_nai_list)))
-			goto nomem;
 #endif /* CONFIG_HS20 */
 
 		dl_list_for_each(elem, &anqp->anqp_elems,
@@ -6485,3 +6469,514 @@
 	}
 	return TRUE;
 }
+
+
+#ifdef CONFIG_NAN_USD
+
+/*
+ * wpas_dbus_handler_nan_publish - Send out NAN publish messages
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANPublish" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_publish(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int publish_id;
+	char *srv_name = NULL;
+	enum nan_service_protocol_type srv_proto_type = 0;
+	bool p2p = false;
+	struct nan_publish_params params;
+	int *freq_list = NULL;
+	struct wpabuf *ssi = NULL;
+	dbus_uint32_t id;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANPublish");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	os_memset(&params, 0, sizeof(params));
+	/* USD shall use both solicited and unsolicited transmissions */
+	params.unsolicited = true;
+	params.solicited = true;
+	/* USD shall require FSD without GAS */
+	params.fsd = true;
+	params.freq = NAN_USD_DEFAULT_FREQ;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "srv_name") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			os_free(srv_name);
+			srv_name = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!srv_name)
+				goto oom;
+		} else if (os_strcmp(entry.key, "srv_proto_type") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			srv_proto_type = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "solicited") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.solicited = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "unsolicited") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.unsolicited = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "solicited_multicast") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.solicited_multicast = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ttl") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.ttl = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "disable_events") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.disable_events = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "fsd") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.fsd = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "fsd_gas") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.fsd_gas = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "p2p") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			p2p = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "freq") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.freq = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "announcement_period") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.announcement_period =
+				wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi)
+				goto oom;
+		} else if (os_strcmp(entry.key, "freq_list") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_UINT16) {
+			unsigned int i;
+
+			for (i = 0; i < entry.array_len; i++)
+				int_array_add_unique(
+					&freq_list, entry.uint16array_value[i]);
+			params.freq_list = freq_list;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANPublish - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto fail;
+		}
+	}
+
+	if (!srv_name)
+		goto fail;
+
+	publish_id = wpas_nan_usd_publish(wpa_s, srv_name, srv_proto_type, ssi,
+					  &params, p2p);
+	if (publish_id < 0) {
+		reply = wpas_dbus_error_unknown_error(
+			message, "error publishing NAN USD");
+		goto out;
+	}
+
+	id = publish_id;
+	reply = dbus_message_new_method_return(message);
+	if (!reply) {
+		reply = wpas_dbus_error_no_memory(message);
+		goto out;
+	}
+
+	dbus_message_append_args(reply, DBUS_TYPE_UINT32,
+				 &id, DBUS_TYPE_INVALID);
+
+out:
+	wpabuf_free(ssi);
+	os_free(freq_list);
+	os_free(srv_name);
+	return reply;
+fail:
+	reply = wpas_dbus_error_invalid_args(message,
+					     "failed to parse NANPublish");
+	goto out;
+oom:
+	reply = wpas_dbus_error_no_memory(message);
+	goto out;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_cancel_publish - Cancel a NAN publish
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANCancelPublish" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_cancel_publish(DBusMessage *message,
+						   struct wpa_supplicant *wpa_s)
+{
+	dbus_uint32_t publish_id;
+
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_UINT32, &publish_id,
+				   DBUS_TYPE_INVALID)) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: NANCancelPublish failed to get args");
+		return wpas_dbus_error_invalid_args(message, NULL);
+	}
+
+	wpa_printf(MSG_DEBUG, "dbus: NANCancelPublish: id=%u", publish_id);
+	nan_de_cancel_publish(wpa_s->nan_de, publish_id);
+	return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_update_publish - Update the SSI for a NAN publish
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANUpdatePublish" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_update_publish(DBusMessage *message,
+						   struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int publish_id = -1;
+	struct wpabuf *ssi = NULL;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANUpdatePublish");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "publish_id") == 0 &&
+		    entry.type == DBUS_TYPE_UINT32) {
+			publish_id = entry.uint32_value;
+			wpa_dbus_dict_entry_clear(&entry);
+			wpa_printf(MSG_DEBUG, "dbus: publish_id=%d",
+				   publish_id);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi) {
+				reply = wpas_dbus_error_no_memory(message);
+				goto out;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANTransmit - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto out;
+		}
+	}
+
+	if (publish_id < 0)
+		goto fail;
+
+	if (nan_de_update_publish(wpa_s->nan_de, publish_id, ssi) < 0)
+		reply = wpas_dbus_error_unknown_error(
+			message, "error updating NAN USD publish ssi");
+
+out:
+	wpabuf_free(ssi);
+	return reply;
+fail:
+	reply = wpas_dbus_error_invalid_args(
+		message,
+		"failed to parse NANUpdatePublish");
+	goto out;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_subscribe - Send out NAN USD subscribe messages
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANSubscribe" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_subscribe(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int subscribe_id;
+	char *srv_name = NULL;
+	struct nan_subscribe_params params;
+	enum nan_service_protocol_type srv_proto_type = 0;
+	bool p2p = false;
+	struct wpabuf *ssi = NULL;
+	int *freq_list = NULL;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANSubscribe");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	os_memset(&params, 0, sizeof(params));
+	params.freq = NAN_USD_DEFAULT_FREQ;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "srv_name") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			os_free(srv_name);
+			srv_name = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!srv_name)
+				goto oom;
+		} else if (os_strcmp(entry.key, "srv_proto_type") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			srv_proto_type = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "active") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.active = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "p2p") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			p2p = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ttl") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.ttl = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "freq") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.freq = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "query_period") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.query_period =
+				wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi)
+				goto oom;
+		} else if (os_strcmp(entry.key, "freq_list") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_UINT16) {
+			unsigned int i;
+
+			for (i = 0; i < entry.array_len; i++)
+				int_array_add_unique(
+					&freq_list, entry.uint16array_value[i]);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANSubscribe - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto fail;
+		}
+	}
+
+	if (!srv_name)
+		goto fail;
+
+	subscribe_id = wpas_nan_usd_subscribe(wpa_s, srv_name, srv_proto_type,
+					      ssi, &params, p2p);
+	if (subscribe_id < 0) {
+		reply = wpas_dbus_error_unknown_error(
+			message, "error subscribing NAN USD");
+		goto out;
+	}
+
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply, DBUS_TYPE_UINT32,
+				 &subscribe_id, DBUS_TYPE_INVALID);
+out:
+	wpabuf_free(ssi);
+	os_free(freq_list);
+	os_free(srv_name);
+	return reply;
+fail:
+	reply = wpas_dbus_error_invalid_args(message,
+					     "failed to parse NANSubscribe");
+	goto out;
+oom:
+	reply = wpas_dbus_error_no_memory(message);
+	goto out;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_cancel_subscribe - Cancel a NAN subscription
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANCancelSubscribe" method call of network interface.
+ */
+DBusMessage *
+wpas_dbus_handler_nan_cancel_subscribe(DBusMessage *message,
+				       struct wpa_supplicant *wpa_s)
+{
+	dbus_uint32_t subscribe_id;
+
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_UINT32, &subscribe_id,
+				   DBUS_TYPE_INVALID)) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: NANCancelSubscribe failed to get args");
+		return wpas_dbus_error_invalid_args(message, NULL);
+	}
+
+	wpa_printf(MSG_DEBUG, "dbus: NANCancelSubscribe: id=%u", subscribe_id);
+	nan_de_cancel_subscribe(wpa_s->nan_de, subscribe_id);
+	return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_transmit - Send out NAN followup frames
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANTransmit" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_transmit(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int handle = -1;
+	int req_instance_id = -1;
+	u8 peer_addr[ETH_ALEN];
+	bool peer_addr_set = false;
+	struct wpabuf *ssi = NULL;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANTransmit");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "handle") == 0 &&
+		    entry.type == DBUS_TYPE_UINT32) {
+			handle = entry.uint32_value;
+			wpa_dbus_dict_entry_clear(&entry);
+			wpa_printf(MSG_DEBUG, "dbus: handle=%d", handle);
+		} else if (os_strcmp(entry.key, "req_instance_id") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			req_instance_id = entry.uint32_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "peer_addr") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (hwaddr_aton(entry.str_value, peer_addr) < 0) {
+				wpa_dbus_dict_entry_clear(&entry);
+				goto fail;
+			}
+			peer_addr_set = true;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi) {
+				reply = wpas_dbus_error_no_memory(message);
+				goto out;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANTransmit - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto fail;
+		}
+	}
+
+	if (handle < 0 || req_instance_id < 0 || !peer_addr_set || !ssi)
+		goto fail;
+
+	if (wpas_nan_usd_transmit(wpa_s, handle, ssi, NULL, peer_addr,
+				  req_instance_id) < 0)
+		reply = wpas_dbus_error_unknown_error(
+			message, "failed to transmit follow-up");
+out:
+	wpabuf_free(ssi);
+	return reply;
+
+fail:
+	reply = wpas_dbus_error_invalid_args(message,
+					     "failed to parse NANTransmit");
+	goto out;
+}
+
+#endif /* CONFIG_NAN_USD */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 7faf70a..a526090 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -290,4 +290,18 @@
 DBusMessage * wpas_dbus_handler_unsubscribe_preq(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_nan_publish(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_cancel_publish(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_update_publish(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_subscribe(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_nan_cancel_subscribe(DBusMessage *message,
+				       struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_transmit(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s);
+
 #endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 65bd478..fc59947 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -477,7 +477,8 @@
 						  freq2, ht40, vht,
 						  max_oper_chwidth, he, edmg,
 						  NULL, 0, 0, allow_6ghz,
-						  retry_limit, go_bssid)) {
+						  retry_limit, go_bssid, NULL,
+						  NULL, NULL, 0)) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -485,7 +486,7 @@
 		}
 	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, freq2,
 				      ht40, vht, max_oper_chwidth, he, edmg,
-				      allow_6ghz))
+				      allow_6ghz, wpa_s->p2p2, wpa_s->p2p_mode))
 		goto inv_args;
 
 out:
@@ -706,7 +707,7 @@
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, 0, join, authorize_only,
 				   go_intent, freq, 0, -1, 0, 0, 0, 0, 0, 0,
-				   NULL, 0, false, 0, 0, NULL);
+				   NULL, 0, false, 0, 0, NULL, false);
 
 	if (new_pin >= 0) {
 		char npin[9];
@@ -866,7 +867,7 @@
 			goto err;
 
 		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0,
-				    0, 0, 0, false) < 0) {
+				    0, 0, 0, false, false) < 0) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -928,7 +929,7 @@
 	if (!wpa_s)
 		return wpas_dbus_error_no_p2p_mgmt_iface(message);
 
-	if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
+	if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, 0,
 			       WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
 		return wpas_dbus_error_unknown_error(message,
 				"Failed to send provision discovery request");
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index a8c0d28..bfdc3e2 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -38,7 +38,7 @@
 	if (!iface)
 		return NULL;
 	iface->dbus_interface = os_strdup(dbus_interface);
-	iface->xml = wpabuf_alloc(16000);
+	iface->xml = wpabuf_alloc(18000);
 	if (iface->dbus_interface == NULL || iface->xml == NULL) {
 		os_free(iface->dbus_interface);
 		wpabuf_free(iface->xml);
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 1b2c756..70f7a3b 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -59,6 +59,9 @@
 				     struct dpp_authentication *auth);
 static bool wpas_dpp_tcp_msg_sent(void *ctx, struct dpp_authentication *auth);
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_DPP3 */
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
@@ -1347,6 +1350,16 @@
 	struct dpp_authentication *auth = wpa_s->dpp_auth;
 	int freq;
 
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_announcement && wpa_s->dpp_pb_discovery_done) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to send push button announcement");
+		if (eloop_register_timeout(0, 0, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+	}
+#endif /* CONFIG_DPP3 */
+
 	if (wpa_s->dpp_listen_on_tx_expire && auth && auth->neg_freq) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Start listen on neg_freq %u MHz based on TX wait expiration on the previous channel",
@@ -5545,7 +5558,6 @@
 #define DPP_PB_ANNOUNCE_PER_CHAN 3
 
 static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq);
-static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx);
 
 
 static void wpas_dpp_pb_tx_status(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index b6c7f50..43847cf 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -16,9 +16,17 @@
 static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s,
 				  const char *ifname)
 {
-	if (wpa_s->driver->init2)
+	if (wpa_s->driver->init2) {
+		enum wpa_p2p_mode p2p_mode = WPA_P2P_MODE_WFD_R1;
+
+#ifdef CONFIG_P2P
+		p2p_mode = wpa_s->p2p_mode;
+#endif /* CONFIG_P2P */
+
 		return wpa_s->driver->init2(wpa_s, ifname,
-					    wpa_s->global_drv_priv);
+					    wpa_s->global_drv_priv,
+					    p2p_mode);
+	}
 	if (wpa_s->driver->init) {
 		return wpa_s->driver->init(wpa_s, ifname);
 	}
@@ -104,6 +112,8 @@
 static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
 			       struct wpa_driver_scan_params *params)
 {
+	params->link_id = -1;
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER)
 		return -EBUSY;
diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c
index 3f018c4..794ec19 100644
--- a/wpa_supplicant/eap_register.c
+++ b/wpa_supplicant/eap_register.c
@@ -40,13 +40,6 @@
 		ret = eap_peer_unauth_tls_register();
 #endif /* EAP_UNAUTH_TLS */
 
-#ifdef EAP_TLS
-#ifdef CONFIG_HS20
-	if (ret == 0)
-		ret = eap_peer_wfa_unauth_tls_register();
-#endif /* CONFIG_HS20 */
-#endif /* EAP_TLS */
-
 #ifdef EAP_MSCHAPv2
 	if (ret == 0)
 		ret = eap_peer_mschapv2_register();
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 2a665d7..6808956 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -437,10 +437,13 @@
 
 	wpa_s->ssid_verified = false;
 	wpa_s->bigtk_set = false;
+
+	wpabuf_free(wpa_s->pending_eapol_rx);
+	wpa_s->pending_eapol_rx = NULL;
 }
 
 
-static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s, bool authorized)
+static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ie_data ie;
 	int pmksa_set = -1;
@@ -465,8 +468,7 @@
 						    true);
 		if (pmksa_set == 0) {
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
-			if (authorized)
-				wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
+			wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 			break;
 		}
 	}
@@ -638,9 +640,6 @@
 	if (wpa_key_mgmt_wpa(ssid->key_mgmt))
 		privacy = 1;
 
-	if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)
-		privacy = 1;
-
 	if (bss->caps & IEEE80211_CAP_PRIVACY)
 		return privacy;
 	return !privacy;
@@ -680,7 +679,7 @@
 		return 0;
 	}
 
-	while ((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) && rsn_ie) {
+	while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
 		proto_match++;
 
 		if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
@@ -715,8 +714,7 @@
 		}
 #endif /* CONFIG_WEP */
 
-		if (!(ie.proto & ssid->proto) &&
-		    !(ssid->proto & WPA_PROTO_OSEN)) {
+		if (!(ie.proto & ssid->proto)) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"   skip RSN IE - proto mismatch");
@@ -918,13 +916,6 @@
 		return 0;
 	}
 
-	if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
-	    wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
-		if (debug_print)
-			wpa_dbg(wpa_s, MSG_DEBUG, "   allow in OSEN");
-		return 1;
-	}
-
 	if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   allow in non-WPA/WPA2");
@@ -1045,7 +1036,7 @@
 #ifdef CONFIG_SAE
 			if (flagged && ((rate_ie[j] & 0x7f) ==
 					BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
-				if (wpa_s->conf->sae_pwe ==
+				if (wpas_get_ssid_sae_pwe(wpa_s, ssid) ==
 				    SAE_PWE_HUNT_AND_PECK &&
 				    !ssid->sae_password_id &&
 				    !is_6ghz_freq(bss->freq) &&
@@ -1163,7 +1154,8 @@
 			if (wpas_network_disabled(wpa_s, ssid))
 				continue;
 			if (ssid->ssid_len == *ret_ssid_len &&
-			    os_memcmp(ssid->ssid, ret_ssid, *ret_ssid_len) == 0) {
+			    os_memcmp(ssid->ssid, *ret_ssid, *ret_ssid_len) ==
+			    0) {
 				/* OWE BSS in transition mode for a currently
 				 * enabled OWE network. */
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1221,7 +1213,7 @@
 static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			    const u8 *match_ssid, size_t match_ssid_len,
 			    struct wpa_bss *bss, int bssid_ignore_count,
-			    bool debug_print);
+			    bool debug_print, bool link);
 
 
 #ifdef CONFIG_SAE_PK
@@ -1251,7 +1243,7 @@
 
 		count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid);
 		if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
-				    bss, count, 0))
+				    bss, count, false, false))
 			return true;
 	}
 
@@ -1263,16 +1255,16 @@
 static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			    const u8 *match_ssid, size_t match_ssid_len,
 			    struct wpa_bss *bss, int bssid_ignore_count,
-			    bool debug_print)
+			    bool debug_print, bool link)
 {
 	int res;
-	bool wpa, check_ssid, osen, rsn_osen = false;
-	struct wpa_ie_data data;
+	bool wpa, check_ssid = false;
 #ifdef CONFIG_MBO
 	const u8 *assoc_disallow;
 #endif /* CONFIG_MBO */
 #ifdef CONFIG_SAE
 	u8 rsnxe_capa = 0;
+	enum sae_pwe sae_pwe;
 #endif /* CONFIG_SAE */
 	const u8 *ie;
 
@@ -1280,11 +1272,6 @@
 	wpa = ie && ie[1];
 	ie = wpa_bss_get_rsne(wpa_s, bss, ssid, false);
 	wpa |= ie && ie[1];
-	if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
-	    (data.key_mgmt & WPA_KEY_MGMT_OSEN))
-		rsn_osen = true;
-	ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
-	osen = ie != NULL;
 
 #ifdef CONFIG_SAE
 	ie = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false);
@@ -1343,7 +1330,7 @@
 		return false;
 	}
 
-	if (ssid->bssid_set &&
+	if (!link && ssid->bssid_set &&
 	    !ether_addr_equal(bss->bssid, ssid->bssid)) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID mismatch");
@@ -1373,7 +1360,7 @@
 	if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss, debug_print))
 		return false;
 
-	if (!osen && !wpa &&
+	if (!wpa &&
 	    !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
 	    !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
 	    !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
@@ -1393,13 +1380,6 @@
 	}
 #endif /* CONFIG_WEP */
 
-	if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen && !rsn_osen) {
-		if (debug_print)
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - non-OSEN network not allowed");
-		return false;
-	}
-
 	if (!wpa_supplicant_match_privacy(bss, ssid)) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - privacy mismatch");
@@ -1449,9 +1429,10 @@
 #ifdef CONFIG_SAE
 	/* When using SAE Password Identifier and when operationg on the 6 GHz
 	 * band, only H2E is allowed. */
-	if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+	if ((sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
 	     is_6ghz_freq(bss->freq) || ssid->sae_password_id) &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	    !(wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) &&
@@ -1619,7 +1600,7 @@
 		return false;
 	}
 
-	if (!wpas_valid_ml_bss(wpa_s, bss)) {
+	if (!link && !wpas_valid_ml_bss(wpa_s, bss)) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"   skip - ML BSS going to be removed");
@@ -1634,12 +1615,12 @@
 struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 				     int i, struct wpa_bss *bss,
 				     struct wpa_ssid *group,
-				     int only_first_ssid, int debug_print)
+				     int only_first_ssid, int debug_print,
+				     bool link)
 {
 	u8 wpa_ie_len, rsn_ie_len;
 	const u8 *ie;
 	struct wpa_ssid *ssid;
-	int osen;
 	const u8 *match_ssid;
 	size_t match_ssid_len;
 	int bssid_ignore_count;
@@ -1650,12 +1631,9 @@
 	ie = wpa_bss_get_rsne(wpa_s, bss, NULL, false);
 	rsn_ie_len = ie ? ie[1] : 0;
 
-	ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
-	osen = ie != NULL;
-
 	if (debug_print) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR
-			" ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
+			" ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s",
 			i, MAC2STR(bss->bssid),
 			wpa_ssid_txt(bss->ssid, bss->ssid_len),
 			wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
@@ -1664,8 +1642,7 @@
 			" wps" : "",
 			(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
 			 wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE))
-			? " p2p" : "",
-			osen ? " osen=1" : "");
+			? " p2p" : "");
 	}
 
 	bssid_ignore_count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid);
@@ -1728,7 +1705,7 @@
 
 	for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
 		if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
-				    bss, bssid_ignore_count, debug_print))
+				    bss, bssid_ignore_count, debug_print, link))
 			return ssid;
 	}
 
@@ -1737,7 +1714,7 @@
 }
 
 
-static struct wpa_bss *
+struct wpa_bss *
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
 			  struct wpa_ssid *group,
 			  struct wpa_ssid **selected_ssid,
@@ -1754,7 +1731,7 @@
 			struct wpa_bss *bss = wpa_s->last_scan_res[i];
 
 			ssid = wpa_scan_res_match(wpa_s, i, bss, group,
-						  only_first_ssid, 0);
+						  only_first_ssid, 0, false);
 			if (ssid != wpa_s->current_ssid)
 				continue;
 			wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR
@@ -1776,7 +1753,7 @@
 
 		wpa_s->owe_transition_select = 1;
 		*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
-						    only_first_ssid, 1);
+						    only_first_ssid, 1, false);
 		wpa_s->owe_transition_select = 0;
 		if (!*selected_ssid)
 			continue;
@@ -2167,7 +2144,8 @@
 
 int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
 					   struct wpa_bss *current_bss,
-					   struct wpa_bss *selected)
+					   struct wpa_bss *selected,
+					   bool poll_current)
 {
 	int min_diff, diff;
 	int cur_band_score, sel_band_score;
@@ -2222,7 +2200,7 @@
 	 * scan results may be a bit old, since we can very quickly get fresh
 	 * information about our currently associated AP.
 	 */
-	if (wpa_drv_signal_poll(wpa_s, &si) == 0 &&
+	if (poll_current && wpa_drv_signal_poll(wpa_s, &si) == 0 &&
 	    (si.data.avg_beacon_signal || si.data.avg_signal)) {
 		/*
 		 * Normalize avg_signal to the RSSI over 20 MHz, as the
@@ -2395,7 +2373,7 @@
 
 #ifndef CONFIG_NO_ROAMING
 	return wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss,
-						      selected);
+						      selected, true);
 #else /* CONFIG_NO_ROAMING */
 	return 0;
 #endif /* CONFIG_NO_ROAMING */
@@ -2557,7 +2535,27 @@
 	}
 #endif /* CONFIG_NO_RANDOM_POOL */
 
-	wpa_s->last_scan_external = data && data->scan_info.external_scan;
+	if (data) {
+		size_t idx;
+
+		wpa_s->last_scan_external = data->scan_info.external_scan;
+		wpa_s->last_scan_num_ssids = data->scan_info.num_ssids;
+		for (idx = 0; idx < wpa_s->last_scan_num_ssids; idx++) {
+			/* Copy the SSID and its length */
+			if (idx >= WPAS_MAX_SCAN_SSIDS ||
+			    data->scan_info.ssids[idx].ssid_len > SSID_MAX_LEN)
+				continue;
+
+			os_memcpy(wpa_s->last_scan_ssids[idx].ssid,
+				  data->scan_info.ssids[idx].ssid,
+				  data->scan_info.ssids[idx].ssid_len);
+			wpa_s->last_scan_ssids[idx].ssid_len =
+				data->scan_info.ssids[idx].ssid_len;
+		}
+	} else {
+		wpa_s->last_scan_external = false;
+		wpa_s->last_scan_num_ssids = 0;
+	}
 
 	if (update_only) {
 		ret = 1;
@@ -3085,7 +3083,7 @@
 		goto fail;
 	}
 	wpa_s->enabled_4addr_mode = 1;
-	wpa_msg(wpa_s, MSG_INFO, "Successfully set 4addr mode");
+	wpa_dbg(wpa_s, MSG_DEBUG, "Successfully set 4addr mode");
 	return;
 
 fail:
@@ -3246,8 +3244,7 @@
 	wpa_s->wpa_proto = ie.proto;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, wpa_s->wpa_proto);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
-			 !!(wpa_s->wpa_proto &
-			    (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
+			 !!(wpa_s->wpa_proto & WPA_PROTO_RSN));
 
 	/* Update AKMP suite from (Re)Association Request frame info */
 	sel = ie.key_mgmt;
@@ -3328,7 +3325,7 @@
 	/* Update GTK and IGTK from AP's RSNE */
 	found = false;
 
-	if (wpa_s->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) {
+	if (wpa_s->wpa_proto & WPA_PROTO_RSN) {
 		const u8 *bss_rsn;
 
 		bss_rsn = wpa_bss_get_rsne(wpa_s, bss, ssid,
@@ -3470,7 +3467,7 @@
 #endif /* CONFIG_WNM */
 		interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
 						data->assoc_info.resp_ies_len);
-		if (wpa_s->hw_capab == CAPAB_VHT &&
+		if ((wpa_s->hw_capab & BIT(CAPAB_VHT)) &&
 		    get_ie(data->assoc_info.resp_ies,
 			   data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
 			wpa_s->ieee80211ac = 1;
@@ -3566,8 +3563,7 @@
 			if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
 				break;
 			found = 1;
-			wpa_find_assoc_pmkid(wpa_s,
-					     data->assoc_info.authorized);
+			wpa_find_assoc_pmkid(wpa_s);
 		}
 #ifndef CONFIG_NO_WPA
 		if (!found_x && p[0] == WLAN_EID_RSNX) {
@@ -3994,10 +3990,9 @@
 	struct eht_ml_basic_common_info *common_info;
 	const u8 *pos;
 	u16 eml_capa = 0, mld_capa = 0;
-	const u16 control =
-		host_to_le16(MULTI_LINK_CONTROL_TYPE_BASIC |
-			     BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
-			     BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT);
+	const u16 control = MULTI_LINK_CONTROL_TYPE_BASIC |
+		BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+		BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT;
 	u8 expected_common_info_len = 9;
 	unsigned int i = 0;
 	u16 ml_control;
@@ -4054,7 +4049,7 @@
 	}
 
 	common_info = (struct eht_ml_basic_common_info *) ml->variable;
-	if (common_info->len != expected_common_info_len) {
+	if (common_info->len < expected_common_info_len) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: Invalid common info len=%u. expected=%u",
 			   common_info->len, expected_common_info_len);
@@ -4103,7 +4098,7 @@
 	ml_len -= sizeof(*ml) + common_info->len;
 	while (ml_len > 2 && i < MAX_NUM_MLD_LINKS) {
 		u8 sub_elem_len = pos[1];
-		u8 sta_info_len;
+		u8 sta_info_len, sta_info_len_min;
 		u8 nstr_bitmap_len = 0;
 		u16 ctrl;
 		const u8 *end;
@@ -4192,15 +4187,21 @@
 			goto out;
 		}
 
-		sta_info_len = 1 + ETH_ALEN + 8 + 2 + 2 + 1 + nstr_bitmap_len;
-		if (sta_info_len > ml_len || sta_info_len > end - pos ||
-		    sta_info_len + 2 > sub_elem_len ||
-		    sta_info_len != *pos) {
+		sta_info_len_min = 1 + ETH_ALEN + 8 + 2 + 2 + 1 +
+			nstr_bitmap_len;
+		if (sta_info_len_min > ml_len || sta_info_len_min > end - pos ||
+		    sta_info_len_min + 2 > sub_elem_len ||
+		    sta_info_len_min > *pos) {
 			wpa_printf(MSG_DEBUG,
-				   "MLD: Invalid STA info len=%u, len=%u",
-				   sta_info_len, *pos);
+				   "MLD: Invalid STA info min len=%u, len=%u",
+				   sta_info_len_min, *pos);
 			goto out;
 		}
+		sta_info_len = *pos;
+		/* Make static analyzers happier with an explicit check even
+		 * though this was already checked above with *pos.. */
+		if (sta_info_len < sta_info_len_min)
+			goto out;
 
 		/* Get the link address */
 		wpa_printf(MSG_DEBUG,
@@ -4839,10 +4840,6 @@
 			return; /* P2P group removed */
 		wpas_auth_failed(wpa_s, "WRONG_KEY", wpa_s->pending_bssid);
 		wpas_notify_psk_mismatch(wpa_s);
-#ifdef CONFIG_DPP2
-		wpas_dpp_send_conn_status_result(wpa_s,
-						 DPP_STATUS_AUTH_FAILURE);
-#endif /* CONFIG_DPP2 */
 	}
 	if (!wpa_s->disconnected &&
 	    (!wpa_s->auto_reconnect_disabled ||
@@ -4897,6 +4894,9 @@
 	else
 		wpa_s->disconnect_reason = reason_code;
 	wpas_notify_disconnect_reason(wpa_s);
+#ifdef CONFIG_DPP2
+	wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_AUTH_FAILURE);
+#endif /* CONFIG_DPP2 */
 	if (wpa_supplicant_dynamic_keys(wpa_s)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
 		wpa_clear_keys(wpa_s, wpa_s->bssid);
@@ -5698,7 +5698,8 @@
 	    WPA_GET_BE32(&payload[1]) == NAN_SDF_VENDOR_TYPE) {
 		payload += 5;
 		plen -= 5;
-		wpas_nan_usd_rx_sdf(wpa_s, mgmt->sa, freq, payload, plen);
+		wpas_nan_usd_rx_sdf(wpa_s, mgmt->sa, mgmt->bssid, freq,
+				    payload, plen);
 		return;
 	}
 #endif /* CONFIG_NAN_USD */
@@ -6271,6 +6272,37 @@
 #endif /* MAINLINE_SUPPLICANT */
 
 
+#ifdef CONFIG_PASN
+static int wpas_pasn_auth(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq)
+{
+#ifdef CONFIG_P2P
+	struct ieee802_11_elems elems;
+
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short Management frame");
+		return -2;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return -2;
+	}
+
+	if (elems.p2p2_ie && elems.p2p2_ie_len)
+		return wpas_p2p_pasn_auth_rx(wpa_s, mgmt, len, freq);
+#endif /* CONFIG_P2P */
+
+	return wpas_pasn_auth_rx(wpa_s, mgmt, len);
+}
+#endif /* CONFIG_PASN */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -6509,6 +6541,17 @@
 			break;
 #endif /* CONFIG_WNM */
 #ifdef CONFIG_PASN
+#ifdef CONFIG_P2P
+		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
+		    !wpa_s->pasn_auth_work &&
+		    wpa_s->p2p_pasn_auth_work &&
+		    wpas_p2p_pasn_auth_tx_status(wpa_s,
+						 data->tx_status.data,
+						 data->tx_status.data_len,
+						 data->tx_status.ack) == 0)
+			break;
+#endif /* CONFIG_P2P */
 		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
 		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
 		    wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data,
@@ -6792,8 +6835,8 @@
 			}
 #ifdef CONFIG_PASN
 			if (stype == WLAN_FC_STYPE_AUTH &&
-			    wpas_pasn_auth_rx(wpa_s, mgmt,
-					      data->rx_mgmt.frame_len) != -2)
+			    wpas_pasn_auth(wpa_s, mgmt, data->rx_mgmt.frame_len,
+					   data->rx_mgmt.freq) != -2)
 				break;
 #endif /* CONFIG_PASN */
 
@@ -6897,12 +6940,12 @@
 					data->eapol_rx.encrypted);
 		break;
 	case EVENT_SIGNAL_CHANGE:
-		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
-			"above=%d signal=%d noise=%d txrate=%lu",
-			data->signal_change.above_threshold,
-			data->signal_change.data.signal,
-			data->signal_change.current_noise,
-			data->signal_change.data.current_tx_rate);
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
+			     "above=%d signal=%d noise=%d txrate=%lu",
+			     data->signal_change.above_threshold,
+			     data->signal_change.data.signal,
+			     data->signal_change.current_noise,
+			     data->signal_change.data.current_tx_rate);
 		wpa_bss_update_level(wpa_s->current_bss,
 				     data->signal_change.data.signal);
 		bgscan_notify_signal_change(
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 7d29931..88564d5 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -147,6 +147,10 @@
 		gas->work = NULL;
 	}
 
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+
 	wpabuf_free(query->req);
 	wpabuf_free(query->adv_proto);
 	wpabuf_free(query->resp);
@@ -166,9 +170,6 @@
 		gas->current = NULL;
 	if (query->offchannel_tx_started)
 		offchannel_send_action_done(gas->wpa_s);
-	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
-	eloop_cancel_timeout(gas_query_timeout, gas, query);
-	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
 	dl_list_del(&query->list);
 	query->cb(query->ctx, query->addr, query->dialog_token, result,
 		  query->adv_proto, query->resp, query->status_code);
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index f14e6cb..d287b70 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -30,42 +30,6 @@
 #include "notify.h"
 
 
-#define OSU_MAX_ITEMS 10
-
-struct osu_lang_string {
-	char lang[4];
-	char text[253];
-};
-
-struct osu_icon {
-	u16 width;
-	u16 height;
-	char lang[4];
-	char icon_type[256];
-	char filename[256];
-	unsigned int id;
-	unsigned int failed:1;
-};
-
-struct osu_provider {
-	u8 bssid[ETH_ALEN];
-	u8 osu_ssid[SSID_MAX_LEN];
-	u8 osu_ssid_len;
-	u8 osu_ssid2[SSID_MAX_LEN];
-	u8 osu_ssid2_len;
-	char server_uri[256];
-	u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
-	char osu_nai[256];
-	char osu_nai2[256];
-	struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
-	size_t friendly_name_count;
-	struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
-	size_t serv_desc_count;
-	struct osu_icon icon[OSU_MAX_ITEMS];
-	size_t icon_count;
-};
-
-
 void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss = wpa_s->current_bss;
@@ -234,11 +198,6 @@
 		wpabuf_put_u8(buf, 0); /* Reserved */
 		if (payload)
 			wpabuf_put_data(buf, payload, payload_len);
-	} else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) {
-		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
-		wpabuf_put_u8(buf, 0); /* Reserved */
-		if (payload)
-			wpabuf_put_data(buf, payload, payload_len);
 	} else {
 		u8 i;
 		wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
@@ -270,14 +229,13 @@
 
 
 int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
-		       const u8 *payload, size_t payload_len, int inmem)
+		       const u8 *payload, size_t payload_len)
 {
 	struct wpabuf *buf;
 	int ret = 0;
 	int freq;
 	struct wpa_bss *bss;
 	int res;
-	struct icon_entry *icon_entry;
 
 	bss = wpa_bss_get_bssid(wpa_s, dst);
 	if (!bss) {
@@ -307,299 +265,10 @@
 		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
 			   "%u", res);
 
-	if (inmem) {
-		icon_entry = os_zalloc(sizeof(struct icon_entry));
-		if (!icon_entry)
-			return -1;
-		os_memcpy(icon_entry->bssid, dst, ETH_ALEN);
-		icon_entry->file_name = os_malloc(payload_len + 1);
-		if (!icon_entry->file_name) {
-			os_free(icon_entry);
-			return -1;
-		}
-		os_memcpy(icon_entry->file_name, payload, payload_len);
-		icon_entry->file_name[payload_len] = '\0';
-		icon_entry->dialog_token = res;
-
-		dl_list_add(&wpa_s->icon_head, &icon_entry->list);
-	}
-
 	return ret;
 }
 
 
-static struct icon_entry * hs20_find_icon(struct wpa_supplicant *wpa_s,
-					  const u8 *bssid,
-					  const char *file_name)
-{
-	struct icon_entry *icon;
-
-	dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
-		if (ether_addr_equal(icon->bssid, bssid) &&
-		    os_strcmp(icon->file_name, file_name) == 0 && icon->image)
-			return icon;
-	}
-
-	return NULL;
-}
-
-
-int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
-		  const char *file_name, size_t offset, size_t size,
-		  char *reply, size_t buf_len)
-{
-	struct icon_entry *icon;
-	size_t out_size;
-	char *b64;
-	size_t b64_size;
-	int reply_size;
-
-	wpa_printf(MSG_DEBUG, "HS20: Get icon " MACSTR " %s @ %u +%u (%u)",
-		   MAC2STR(bssid), file_name, (unsigned int) offset,
-		   (unsigned int) size, (unsigned int) buf_len);
-
-	icon = hs20_find_icon(wpa_s, bssid, file_name);
-	if (!icon || !icon->image || offset >= icon->image_len)
-		return -1;
-	if (size > icon->image_len - offset)
-		size = icon->image_len - offset;
-	out_size = buf_len - 3 /* max base64 padding */;
-	if (size * 4 > out_size * 3)
-		size = out_size * 3 / 4;
-	if (size == 0)
-		return -1;
-
-	b64 = base64_encode(&icon->image[offset], size, &b64_size);
-	if (b64 && buf_len >= b64_size) {
-		os_memcpy(reply, b64, b64_size);
-		reply_size = b64_size;
-	} else {
-		reply_size = -1;
-	}
-	os_free(b64);
-	return reply_size;
-}
-
-
-static void hs20_free_icon_entry(struct icon_entry *icon)
-{
-	wpa_printf(MSG_DEBUG, "HS20: Free stored icon from " MACSTR
-		   " dialog_token=%u file_name=%s image_len=%u",
-		   MAC2STR(icon->bssid), icon->dialog_token,
-		   icon->file_name ? icon->file_name : "N/A",
-		   (unsigned int) icon->image_len);
-	os_free(icon->file_name);
-	os_free(icon->image);
-	os_free(icon);
-}
-
-
-int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
-		  const char *file_name)
-{
-	struct icon_entry *icon, *tmp;
-	int count = 0;
-
-	if (!bssid)
-		wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons");
-	else if (!file_name)
-		wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons for "
-			   MACSTR, MAC2STR(bssid));
-	else
-		wpa_printf(MSG_DEBUG, "HS20: Delete stored icons for "
-			   MACSTR " file name %s", MAC2STR(bssid), file_name);
-
-	dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry,
-			      list) {
-		if ((!bssid || ether_addr_equal(icon->bssid, bssid)) &&
-		    (!file_name ||
-		     os_strcmp(icon->file_name, file_name) == 0)) {
-			dl_list_del(&icon->list);
-			hs20_free_icon_entry(icon);
-			count++;
-		}
-	}
-	return count == 0 ? -1 : 0;
-}
-
-
-static void hs20_set_osu_access_permission(const char *osu_dir,
-					   const char *fname)
-{
-	struct stat statbuf;
-
-	/* Get OSU directory information */
-	if (stat(osu_dir, &statbuf) < 0) {
-		wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s",
-			   osu_dir);
-		return;
-	}
-
-	if (chmod(fname, statbuf.st_mode) < 0) {
-		wpa_printf(MSG_WARNING,
-			   "Cannot change the permissions for %s", fname);
-		return;
-	}
-
-	if (lchown(fname, statbuf.st_uid, statbuf.st_gid) < 0) {
-		wpa_printf(MSG_WARNING, "Cannot change the ownership for %s",
-			   fname);
-	}
-}
-
-
-static void hs20_remove_duplicate_icons(struct wpa_supplicant *wpa_s,
-					struct icon_entry *new_icon)
-{
-	struct icon_entry *icon, *tmp;
-
-	dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry,
-			      list) {
-		if (icon == new_icon)
-			continue;
-		if (ether_addr_equal(icon->bssid, new_icon->bssid) &&
-		    os_strcmp(icon->file_name, new_icon->file_name) == 0) {
-			dl_list_del(&icon->list);
-			hs20_free_icon_entry(icon);
-		}
-	}
-}
-
-
-static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
-					 const u8 *sa, const u8 *pos,
-					 size_t slen, u8 dialog_token)
-{
-	char fname[256];
-	int png;
-	FILE *f;
-	u16 data_len;
-	struct icon_entry *icon;
-
-	dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
-		if (icon->dialog_token == dialog_token && !icon->image &&
-		    ether_addr_equal(icon->bssid, sa)) {
-			icon->image = os_memdup(pos, slen);
-			if (!icon->image)
-				return -1;
-			icon->image_len = slen;
-			hs20_remove_duplicate_icons(wpa_s, icon);
-			wpa_msg(wpa_s, MSG_INFO,
-				RX_HS20_ICON MACSTR " %s %u",
-				MAC2STR(sa), icon->file_name,
-				(unsigned int) icon->image_len);
-			wpas_notify_hs20_icon_query_done(wpa_s, sa,
-							 icon->file_name,
-							 icon->image,
-							 icon->image_len);
-			return 0;
-		}
-	}
-
-	wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Icon Binary File",
-		MAC2STR(sa));
-
-	if (slen < 4) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
-			"value from " MACSTR, MAC2STR(sa));
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
-	if (*pos != 0)
-		return -1;
-	pos++;
-	slen--;
-
-	if ((size_t) 1 + pos[0] > slen) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
-			"value from " MACSTR, MAC2STR(sa));
-		return -1;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
-	png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
-	slen -= 1 + pos[0];
-	pos += 1 + pos[0];
-
-	if (slen < 2) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
-			"value from " MACSTR, MAC2STR(sa));
-		return -1;
-	}
-	data_len = WPA_GET_LE16(pos);
-	pos += 2;
-	slen -= 2;
-
-	if (data_len > slen) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
-			"value from " MACSTR, MAC2STR(sa));
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
-	if (wpa_s->conf->osu_dir == NULL)
-		return -1;
-
-	wpa_s->osu_icon_id++;
-	if (wpa_s->osu_icon_id == 0)
-		wpa_s->osu_icon_id++;
-	snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
-		 wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
-		 png ? "png" : "icon");
-	f = fopen(fname, "wb");
-	if (f == NULL)
-		return -1;
-
-	hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
-
-	if (fwrite(pos, slen, 1, f) != 1) {
-		fclose(f);
-		unlink(fname);
-		return -1;
-	}
-	fclose(f);
-
-	wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP_ICON "%s", fname);
-	return 0;
-}
-
-
-static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
-{
-	struct wpa_supplicant *wpa_s = eloop_ctx;
-	if (wpa_s->fetch_osu_icon_in_progress)
-		hs20_next_osu_icon(wpa_s);
-}
-
-
-static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
-{
-	size_t i, j;
-	struct os_reltime now, tmp;
-	int dur;
-
-	os_get_reltime(&now);
-	os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
-	dur = tmp.sec * 1000 + tmp.usec / 1000;
-	wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
-		   dur, res);
-
-	for (i = 0; i < wpa_s->osu_prov_count; i++) {
-		struct osu_provider *osu = &wpa_s->osu_prov[i];
-		for (j = 0; j < osu->icon_count; j++) {
-			struct osu_icon *icon = &osu->icon[j];
-			if (icon->id || icon->failed)
-				continue;
-			if (res < 0)
-				icon->failed = 1;
-			else
-				icon->id = wpa_s->osu_icon_id;
-			return;
-		}
-	}
-}
-
-
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
 				  struct wpa_bss *bss, const u8 *sa,
 				  const u8 *data, size_t slen, u8 dialog_token)
@@ -607,7 +276,6 @@
 	const u8 *pos = data;
 	u8 subtype;
 	struct wpa_bss_anqp *anqp = NULL;
-	int ret;
 
 	if (slen < 2)
 		return;
@@ -678,46 +346,6 @@
 				wpabuf_alloc_copy(pos, slen);
 		}
 		break;
-	case HS20_STYPE_OSU_PROVIDERS_LIST:
-		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
-			" OSU Providers list", MAC2STR(sa));
-		wpa_s->num_prov_found++;
-		if (anqp) {
-			wpabuf_free(anqp->hs20_osu_providers_list);
-			anqp->hs20_osu_providers_list =
-				wpabuf_alloc_copy(pos, slen);
-		}
-		break;
-	case HS20_STYPE_ICON_BINARY_FILE:
-		ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen,
-						    dialog_token);
-		if (wpa_s->fetch_osu_icon_in_progress) {
-			hs20_osu_icon_fetch_result(wpa_s, ret);
-			eloop_cancel_timeout(hs20_continue_icon_fetch,
-					     wpa_s, NULL);
-			eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
-					       wpa_s, NULL);
-		}
-		break;
-	case HS20_STYPE_OPERATOR_ICON_METADATA:
-		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
-			" Operator Icon Metadata", MAC2STR(sa));
-		wpa_hexdump(MSG_DEBUG, "Operator Icon Metadata", pos, slen);
-		if (anqp) {
-			wpabuf_free(anqp->hs20_operator_icon_metadata);
-			anqp->hs20_operator_icon_metadata =
-				wpabuf_alloc_copy(pos, slen);
-		}
-		break;
-	case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
-		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
-			" OSU Providers NAI List", MAC2STR(sa));
-		if (anqp) {
-			wpabuf_free(anqp->hs20_osu_providers_nai_list);
-			anqp->hs20_osu_providers_nai_list =
-				wpabuf_alloc_copy(pos, slen);
-		}
-		break;
 	default:
 		wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
 		break;
@@ -725,582 +353,6 @@
 }
 
 
-void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s->fetch_osu_icon_in_progress)
-		return;
-	if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
-		return;
-	/*
-	 * We are going through icon fetch, but no icon response was received.
-	 * Assume this means the current AP could not provide an answer to avoid
-	 * getting stuck in fetch iteration.
-	 */
-	hs20_icon_fetch_failed(wpa_s);
-}
-
-
-static void hs20_free_osu_prov_entry(struct osu_provider *prov)
-{
-}
-
-
-void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
-{
-	size_t i;
-	for (i = 0; i < wpa_s->osu_prov_count; i++)
-		hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
-	os_free(wpa_s->osu_prov);
-	wpa_s->osu_prov = NULL;
-	wpa_s->osu_prov_count = 0;
-}
-
-
-static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
-{
-	char fname[256];
-	FILE *f;
-	size_t i, j;
-
-	wpa_s->fetch_osu_info = 0;
-	wpa_s->fetch_osu_icon_in_progress = 0;
-
-	if (wpa_s->conf->osu_dir == NULL) {
-		hs20_free_osu_prov(wpa_s);
-		wpa_s->fetch_anqp_in_progress = 0;
-		return;
-	}
-
-	snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
-		 wpa_s->conf->osu_dir);
-	f = fopen(fname, "w");
-	if (f == NULL) {
-		wpa_msg(wpa_s, MSG_INFO,
-			"Could not write OSU provider information");
-		hs20_free_osu_prov(wpa_s);
-		wpa_s->fetch_anqp_in_progress = 0;
-		return;
-	}
-
-	hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
-
-	for (i = 0; i < wpa_s->osu_prov_count; i++) {
-		struct osu_provider *osu = &wpa_s->osu_prov[i];
-		if (i > 0)
-			fprintf(f, "\n");
-		fprintf(f, "OSU-PROVIDER " MACSTR "\n"
-			"uri=%s\n"
-			"methods=%08x\n",
-			MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
-		if (osu->osu_ssid_len) {
-			fprintf(f, "osu_ssid=%s\n",
-				wpa_ssid_txt(osu->osu_ssid,
-					     osu->osu_ssid_len));
-		}
-		if (osu->osu_ssid2_len) {
-			fprintf(f, "osu_ssid2=%s\n",
-				wpa_ssid_txt(osu->osu_ssid2,
-					     osu->osu_ssid2_len));
-		}
-		if (osu->osu_nai[0])
-			fprintf(f, "osu_nai=%s\n", osu->osu_nai);
-		if (osu->osu_nai2[0])
-			fprintf(f, "osu_nai2=%s\n", osu->osu_nai2);
-		for (j = 0; j < osu->friendly_name_count; j++) {
-			fprintf(f, "friendly_name=%s:%s\n",
-				osu->friendly_name[j].lang,
-				osu->friendly_name[j].text);
-		}
-		for (j = 0; j < osu->serv_desc_count; j++) {
-			fprintf(f, "desc=%s:%s\n",
-				osu->serv_desc[j].lang,
-				osu->serv_desc[j].text);
-		}
-		for (j = 0; j < osu->icon_count; j++) {
-			struct osu_icon *icon = &osu->icon[j];
-			if (icon->failed)
-				continue; /* could not fetch icon */
-			fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
-				icon->id, icon->width, icon->height, icon->lang,
-				icon->icon_type, icon->filename);
-		}
-	}
-	fclose(f);
-	hs20_free_osu_prov(wpa_s);
-
-	wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
-	wpa_s->fetch_anqp_in_progress = 0;
-}
-
-
-void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
-{
-	size_t i, j;
-
-	wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
-
-	for (i = 0; i < wpa_s->osu_prov_count; i++) {
-		struct osu_provider *osu = &wpa_s->osu_prov[i];
-		for (j = 0; j < osu->icon_count; j++) {
-			struct osu_icon *icon = &osu->icon[j];
-			if (icon->id || icon->failed)
-				continue;
-
-			wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
-				   "from " MACSTR, icon->filename,
-				   MAC2STR(osu->bssid));
-			os_get_reltime(&wpa_s->osu_icon_fetch_start);
-			if (hs20_anqp_send_req(wpa_s, osu->bssid,
-					       BIT(HS20_STYPE_ICON_REQUEST),
-					       (u8 *) icon->filename,
-					       os_strlen(icon->filename),
-					       0) < 0) {
-				icon->failed = 1;
-				continue;
-			}
-			return;
-		}
-	}
-
-	wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
-	hs20_osu_fetch_done(wpa_s);
-}
-
-
-static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
-			      const u8 *osu_ssid, u8 osu_ssid_len,
-			      const u8 *osu_ssid2, u8 osu_ssid2_len,
-			      const u8 *pos, size_t len)
-{
-	struct osu_provider *prov;
-	const u8 *end = pos + len;
-	u16 len2;
-	const u8 *pos2;
-	u8 uri_len, osu_method_len, osu_nai_len;
-
-	wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
-	prov = os_realloc_array(wpa_s->osu_prov,
-				wpa_s->osu_prov_count + 1,
-				sizeof(*prov));
-	if (prov == NULL)
-		return;
-	wpa_s->osu_prov = prov;
-	prov = &prov[wpa_s->osu_prov_count];
-	os_memset(prov, 0, sizeof(*prov));
-
-	os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
-	os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
-	prov->osu_ssid_len = osu_ssid_len;
-	if (osu_ssid2)
-		os_memcpy(prov->osu_ssid2, osu_ssid2, osu_ssid2_len);
-	prov->osu_ssid2_len = osu_ssid2_len;
-
-	/* OSU Friendly Name Length */
-	if (end - pos < 2) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
-			   "Friendly Name Length");
-		return;
-	}
-	len2 = WPA_GET_LE16(pos);
-	pos += 2;
-	if (len2 > end - pos) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
-			   "Friendly Name Duples");
-		return;
-	}
-	pos2 = pos;
-	pos += len2;
-
-	/* OSU Friendly Name Duples */
-	while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) {
-		struct osu_lang_string *f;
-		u8 slen;
-
-		slen = pos2[0];
-		if (1 + slen > pos - pos2) {
-			wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
-			break;
-		}
-		if (slen < 3) {
-			wpa_printf(MSG_DEBUG,
-				   "Invalid OSU Friendly Name (no room for language)");
-			break;
-		}
-		f = &prov->friendly_name[prov->friendly_name_count++];
-		pos2++;
-		os_memcpy(f->lang, pos2, 3);
-		pos2 += 3;
-		slen -= 3;
-		os_memcpy(f->text, pos2, slen);
-		pos2 += slen;
-	}
-
-	/* OSU Server URI */
-	if (end - pos < 1) {
-		wpa_printf(MSG_DEBUG,
-			   "HS 2.0: Not enough room for OSU Server URI length");
-		return;
-	}
-	uri_len = *pos++;
-	if (uri_len > end - pos) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
-			   "URI");
-		return;
-	}
-	os_memcpy(prov->server_uri, pos, uri_len);
-	pos += uri_len;
-
-	/* OSU Method list */
-	if (end - pos < 1) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
-			   "list length");
-		return;
-	}
-	osu_method_len = pos[0];
-	if (osu_method_len > end - pos - 1) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
-			   "list");
-		return;
-	}
-	pos2 = pos + 1;
-	pos += 1 + osu_method_len;
-	while (pos2 < pos) {
-		if (*pos2 < 32)
-			prov->osu_methods |= BIT(*pos2);
-		pos2++;
-	}
-
-	/* Icons Available Length */
-	if (end - pos < 2) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
-			   "Available Length");
-		return;
-	}
-	len2 = WPA_GET_LE16(pos);
-	pos += 2;
-	if (len2 > end - pos) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
-			   "Available");
-		return;
-	}
-	pos2 = pos;
-	pos += len2;
-
-	/* Icons Available */
-	while (pos2 < pos) {
-		struct osu_icon *icon = &prov->icon[prov->icon_count];
-		u8 flen;
-
-		if (2 + 2 + 3 + 1 + 1 > pos - pos2) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
-			break;
-		}
-
-		icon->width = WPA_GET_LE16(pos2);
-		pos2 += 2;
-		icon->height = WPA_GET_LE16(pos2);
-		pos2 += 2;
-		os_memcpy(icon->lang, pos2, 3);
-		pos2 += 3;
-
-		flen = *pos2++;
-		if (flen > pos - pos2) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
-			break;
-		}
-		os_memcpy(icon->icon_type, pos2, flen);
-		pos2 += flen;
-
-		if (pos - pos2 < 1) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
-				   "Filename length");
-			break;
-		}
-		flen = *pos2++;
-		if (flen > pos - pos2) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
-				   "Filename");
-			break;
-		}
-		os_memcpy(icon->filename, pos2, flen);
-		pos2 += flen;
-
-		prov->icon_count++;
-	}
-
-	/* OSU_NAI */
-	if (end - pos < 1) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
-		return;
-	}
-	osu_nai_len = *pos++;
-	if (osu_nai_len > end - pos) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
-		return;
-	}
-	os_memcpy(prov->osu_nai, pos, osu_nai_len);
-	pos += osu_nai_len;
-
-	/* OSU Service Description Length */
-	if (end - pos < 2) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
-			   "Service Description Length");
-		return;
-	}
-	len2 = WPA_GET_LE16(pos);
-	pos += 2;
-	if (len2 > end - pos) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
-			   "Service Description Duples");
-		return;
-	}
-	pos2 = pos;
-	pos += len2;
-
-	/* OSU Service Description Duples */
-	while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) {
-		struct osu_lang_string *f;
-		u8 descr_len;
-
-		descr_len = *pos2++;
-		if (descr_len > pos - pos2 || descr_len < 3) {
-			wpa_printf(MSG_DEBUG, "Invalid OSU Service "
-				   "Description");
-			break;
-		}
-		f = &prov->serv_desc[prov->serv_desc_count++];
-		os_memcpy(f->lang, pos2, 3);
-		os_memcpy(f->text, pos2 + 3, descr_len - 3);
-		pos2 += descr_len;
-	}
-
-	wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
-		   MAC2STR(bss->bssid));
-	wpa_s->osu_prov_count++;
-}
-
-
-void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
-{
-	struct wpa_bss *bss;
-	struct wpabuf *prov_anqp;
-	const u8 *pos, *end;
-	u16 len;
-	const u8 *osu_ssid, *osu_ssid2;
-	u8 osu_ssid_len, osu_ssid2_len;
-	u8 num_providers;
-
-	hs20_free_osu_prov(wpa_s);
-
-	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		struct wpa_ie_data data;
-		const u8 *ie;
-
-		if (bss->anqp == NULL)
-			continue;
-		prov_anqp = bss->anqp->hs20_osu_providers_list;
-		if (prov_anqp == NULL)
-			continue;
-		ie = wpa_bss_get_rsne(wpa_s, bss, NULL, false);
-		if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 &&
-		    (data.key_mgmt & WPA_KEY_MGMT_OSEN)) {
-			osu_ssid2 = bss->ssid;
-			osu_ssid2_len = bss->ssid_len;
-		} else {
-			osu_ssid2 = NULL;
-			osu_ssid2_len = 0;
-		}
-		wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
-			   MACSTR, MAC2STR(bss->bssid));
-		wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
-				prov_anqp);
-		pos = wpabuf_head(prov_anqp);
-		end = pos + wpabuf_len(prov_anqp);
-
-		/* OSU SSID */
-		if (end - pos < 1)
-			continue;
-		if (1 + pos[0] > end - pos) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
-				   "OSU SSID");
-			continue;
-		}
-		osu_ssid_len = *pos++;
-		if (osu_ssid_len > SSID_MAX_LEN) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
-				   "Length %u", osu_ssid_len);
-			continue;
-		}
-		osu_ssid = pos;
-		pos += osu_ssid_len;
-
-		if (end - pos < 1) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
-				   "Number of OSU Providers");
-			continue;
-		}
-		num_providers = *pos++;
-		wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
-			   num_providers);
-
-		/* OSU Providers */
-		while (end - pos > 2 && num_providers > 0) {
-			num_providers--;
-			len = WPA_GET_LE16(pos);
-			pos += 2;
-			if (len > (unsigned int) (end - pos))
-				break;
-			hs20_osu_add_prov(wpa_s, bss, osu_ssid,
-					  osu_ssid_len, osu_ssid2,
-					  osu_ssid2_len, pos, len);
-			pos += len;
-		}
-
-		if (pos != end) {
-			wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
-				   "extra data after OSU Providers",
-				   (int) (end - pos));
-		}
-
-		prov_anqp = bss->anqp->hs20_osu_providers_nai_list;
-		if (!prov_anqp)
-			continue;
-		wpa_printf(MSG_DEBUG,
-			   "HS 2.0: Parsing OSU Providers NAI List from "
-			   MACSTR, MAC2STR(bss->bssid));
-		wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers NAI List",
-				prov_anqp);
-		pos = wpabuf_head(prov_anqp);
-		end = pos + wpabuf_len(prov_anqp);
-		num_providers = 0;
-		while (end - pos > 0) {
-			len = *pos++;
-			if (end - pos < len) {
-				wpa_printf(MSG_DEBUG,
-					   "HS 2.0: Not enough room for OSU_NAI");
-				break;
-			}
-			if (num_providers >= wpa_s->osu_prov_count) {
-				wpa_printf(MSG_DEBUG,
-					   "HS 2.0: Ignore unexpected OSU Provider NAI List entries");
-				break;
-			}
-			os_memcpy(wpa_s->osu_prov[num_providers].osu_nai2,
-				  pos, len);
-			pos += len;
-			num_providers++;
-		}
-	}
-
-	wpa_s->fetch_osu_icon_in_progress = 1;
-	hs20_next_osu_icon(wpa_s);
-}
-
-
-static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
-				      struct wpa_scan_results *scan_res)
-{
-	wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
-	if (!wpa_s->fetch_osu_waiting_scan) {
-		wpa_printf(MSG_DEBUG, "OSU fetch have been canceled");
-		return;
-	}
-	wpa_s->network_select = 0;
-	wpa_s->fetch_all_anqp = 1;
-	wpa_s->fetch_osu_info = 1;
-	wpa_s->fetch_osu_icon_in_progress = 0;
-
-	interworking_start_fetch_anqp(wpa_s);
-}
-
-
-int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan)
-{
-	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
-			   "interface disabled");
-		return -1;
-	}
-
-	if (wpa_s->scanning) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
-			   "scanning");
-		return -1;
-	}
-
-	if (wpa_s->conf->osu_dir == NULL) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
-			   "osu_dir not configured");
-		return -1;
-	}
-
-	if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
-			   "fetch in progress (%d, %d)",
-			   wpa_s->fetch_anqp_in_progress,
-			   wpa_s->network_select);
-		return -1;
-	}
-
-	wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
-	wpa_s->num_osu_scans = 0;
-	wpa_s->num_prov_found = 0;
-	if (skip_scan) {
-		wpa_s->network_select = 0;
-		wpa_s->fetch_all_anqp = 1;
-		wpa_s->fetch_osu_info = 1;
-		wpa_s->fetch_osu_icon_in_progress = 0;
-
-		interworking_start_fetch_anqp(wpa_s);
-	} else {
-		hs20_start_osu_scan(wpa_s);
-	}
-
-	return 0;
-}
-
-
-void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
-{
-	wpa_s->fetch_osu_waiting_scan = 1;
-	wpa_s->num_osu_scans++;
-	wpa_s->scan_req = MANUAL_SCAN_REQ;
-	wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
-	wpa_supplicant_req_scan(wpa_s, 0, 0);
-}
-
-
-void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
-{
-	wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
-	interworking_stop_fetch_anqp(wpa_s);
-	wpa_s->fetch_osu_waiting_scan = 0;
-	wpa_s->network_select = 0;
-	wpa_s->fetch_osu_info = 0;
-	wpa_s->fetch_osu_icon_in_progress = 0;
-}
-
-
-void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
-{
-	hs20_osu_icon_fetch_result(wpa_s, -1);
-	eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
-	eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
-}
-
-
-void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
-				      const char *url, u8 osu_method)
-{
-	if (url)
-		wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
-			osu_method, url);
-	else
-		wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
-	wpas_notify_hs20_rx_subscription_remediation(wpa_s, url, osu_method);
-}
-
-
 void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
 				    u16 reauth_delay, const char *url)
 {
@@ -1355,18 +407,3 @@
 
 	wpas_notify_hs20_t_c_acceptance(wpa_s, url);
 }
-
-
-void hs20_init(struct wpa_supplicant *wpa_s)
-{
-	dl_list_init(&wpa_s->icon_head);
-}
-
-
-void hs20_deinit(struct wpa_supplicant *wpa_s)
-{
-	eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
-	hs20_free_osu_prov(wpa_s);
-	if (wpa_s->icon_head.next)
-		hs20_del_icon(wpa_s, NULL, NULL);
-}
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 2d478f4..3ff766e 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -15,7 +15,7 @@
 				 const struct wpa_ssid *ssid);
 
 int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
-		       const u8 *payload, size_t payload_len, int inmem);
+		       const u8 *payload, size_t payload_len);
 void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
 		       struct wpabuf *buf);
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
@@ -26,27 +26,10 @@
 int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 		    struct wpa_bss *bss);
 int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
-void hs20_notify_parse_done(struct wpa_supplicant *wpa_s);
 
-void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
-				      const char *url, u8 osu_method);
 void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
 				    u16 reauth_delay, const char *url);
 void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url);
 
-void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
-void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
-void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
-int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan);
-void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
-void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
-void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
-void hs20_init(struct wpa_supplicant *wpa_s);
-void hs20_deinit(struct wpa_supplicant *wpa_s);
-int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
-		  const char *file_name, size_t offset, size_t size,
-		  char *reply, size_t buf_len);
-int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
-		  const char *file_name);
 
 #endif /* HS20_SUPPLICANT_H */
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 25039a0..06228d0 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -150,6 +150,12 @@
 {
 	struct ibss_rsn_peer *peer = ctx;
 
+	if (key_flag & KEY_FLAG_NEXT) {
+		wpa_printf(MSG_DEBUG,
+			   "SUPP: Ignore set_key with KEY_FLAG_NEXT");
+		return 0;
+	}
+
 	wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
 		   "set_tx=%d)",
 		   __func__, alg, MAC2STR(addr), key_idx, set_tx);
@@ -320,6 +326,12 @@
 	struct ibss_rsn *ibss_rsn = ctx;
 	u8 seq[6];
 
+	if (key_flag & KEY_FLAG_NEXT) {
+		wpa_printf(MSG_DEBUG,
+			   "AUTH: Ignore set_key with KEY_FLAG_NEXT");
+		return 0;
+	}
+
 	os_memset(seq, 0, sizeof(seq));
 
 	if (addr) {
@@ -485,7 +497,7 @@
 				"\x01\x00\x00\x0f\xac\x04"
 				"\x01\x00\x00\x0f\xac\x02"
 				"\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0,
-				NULL) != WPA_IE_OK) {
+				NULL, false) != WPA_IE_OK) {
 		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
 		return -1;
 	}
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 651907b..bc26006 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -303,10 +303,6 @@
 			wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
 		if (all)
 			wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
-		if (all) {
-			wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
-			wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
-		}
 		gas_anqp_set_element_len(extra, len_pos);
 	}
 #endif /* CONFIG_HS20 */
@@ -1502,18 +1498,18 @@
 		char *anon;
 		/* Use anonymous NAI in Phase 1 */
 		pos = os_strchr(cred->username, '@');
-		if (pos) {
-			size_t buflen = 9 + os_strlen(pos) + 1;
-			anon = os_malloc(buflen);
-			if (anon == NULL)
-				return -1;
-			os_snprintf(anon, buflen, "anonymous%s", pos);
-		} else if (cred->realm) {
+		if (cred->realm) {
 			size_t buflen = 10 + os_strlen(cred->realm) + 1;
 			anon = os_malloc(buflen);
 			if (anon == NULL)
 				return -1;
 			os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
+		} else if (pos) {
+			size_t buflen = 9 + os_strlen(pos) + 1;
+			anon = os_malloc(buflen);
+			if (anon == NULL)
+				return -1;
+			os_snprintf(anon, buflen, "anonymous%s", pos);
 		} else {
 			anon = os_strdup("anonymous");
 			if (anon == NULL)
@@ -2698,23 +2694,14 @@
 	int found = 0;
 
 	wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
-		   "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
-		   wpa_s->fetch_anqp_in_progress,
-		   wpa_s->fetch_osu_icon_in_progress);
+		   "fetch_anqp_in_progress=%d",
+		   wpa_s->fetch_anqp_in_progress);
 
 	if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
 		wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
 		return;
 	}
 
-#ifdef CONFIG_HS20
-	if (wpa_s->fetch_osu_icon_in_progress) {
-		wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
-		hs20_next_osu_icon(wpa_s);
-		return;
-	}
-#endif /* CONFIG_HS20 */
-
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (!(bss->caps & IEEE80211_CAP_ESS))
 			continue;
@@ -2747,20 +2734,6 @@
 	}
 
 	if (found == 0) {
-#ifdef CONFIG_HS20
-		if (wpa_s->fetch_osu_info) {
-			if (wpa_s->num_prov_found == 0 &&
-			    wpa_s->fetch_osu_waiting_scan &&
-			    wpa_s->num_osu_scans < 3) {
-				wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
-				hs20_start_osu_scan(wpa_s);
-				return;
-			}
-			wpa_printf(MSG_DEBUG, "Interworking: Next icon");
-			hs20_osu_icon_fetch(wpa_s);
-			return;
-		}
-#endif /* CONFIG_HS20 */
 		wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
 		wpa_s->fetch_anqp_in_progress = 0;
 		if (wpa_s->network_select)
@@ -2793,7 +2766,6 @@
 
 	wpa_s->network_select = 0;
 	wpa_s->fetch_all_anqp = 1;
-	wpa_s->fetch_osu_info = 0;
 
 	interworking_start_fetch_anqp(wpa_s);
 
@@ -3141,10 +3113,6 @@
 		   " dialog_token=%u result=%d status_code=%u",
 		   MAC2STR(dst), dialog_token, result, status_code);
 	if (result != GAS_QUERY_SUCCESS) {
-#ifdef CONFIG_HS20
-		if (wpa_s->fetch_osu_icon_in_progress)
-			hs20_icon_fetch_failed(wpa_s);
-#endif /* CONFIG_HS20 */
 		anqp_result = "FAILURE";
 		goto out;
 	}
@@ -3154,10 +3122,6 @@
 	    pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"ANQP: Unexpected Advertisement Protocol in response");
-#ifdef CONFIG_HS20
-		if (wpa_s->fetch_osu_icon_in_progress)
-			hs20_icon_fetch_failed(wpa_s);
-#endif /* CONFIG_HS20 */
 		anqp_result = "INVALID_FRAME";
 		goto out;
 	}
@@ -3208,9 +3172,6 @@
 out_parse_done:
 	if (bss)
 		wpas_notify_bss_anqp_changed(wpa_s, bss->id);
-#ifdef CONFIG_HS20
-	hs20_notify_parse_done(wpa_s);
-#endif /* CONFIG_HS20 */
 out:
 	wpas_notify_anqp_query_done(wpa_s, dst, anqp_result, bss ? bss->anqp : NULL);
 }
@@ -3233,7 +3194,6 @@
 	wpa_s->auto_network_select = 0;
 	wpa_s->auto_select = !!auto_select;
 	wpa_s->fetch_all_anqp = 0;
-	wpa_s->fetch_osu_info = 0;
 	wpa_msg(wpa_s, MSG_DEBUG,
 		"Interworking: Start scan for network selection");
 	wpa_s->scan_res_handler = interworking_scan_res_handler;
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 80fbe01..273bd25 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -460,6 +460,10 @@
 {
 	u8 *len;
 
+	if (wpa_s->drv_max_probe_req_ie_len <
+	    9 + ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
+		return;
+
 	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
 	len = wpabuf_put(ie, 1);
 
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 85c1ea8..869f0b3 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -464,6 +464,9 @@
 		case 160:
 			conf->op_class = 134;
 			break;
+		case 320:
+			conf->op_class = 137;
+			break;
 		default:
 			conf->op_class = 131;
 			break;
@@ -602,7 +605,8 @@
 	/* EID + 0-length (wildcard) mesh-id */
 	size_t ielen = 2;
 
-	if (wpabuf_resize(extra_ie, ielen) == 0) {
+	if (ielen <= wpa_s->drv_max_probe_req_ie_len &&
+	    wpabuf_resize(extra_ie, ielen) == 0) {
 		wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID);
 		wpabuf_put_u8(*extra_ie, 0);
 	}
diff --git a/wpa_supplicant/nan_usd.c b/wpa_supplicant/nan_usd.c
index 1125f95..946d62f 100644
--- a/wpa_supplicant/nan_usd.c
+++ b/wpa_supplicant/nan_usd.c
@@ -321,7 +321,8 @@
 	cb.process_p2p_usd_elems = wpas_nan_process_p2p_usd_elems;
 #endif /* CONFIG_P2P */
 
-	wpa_s->nan_de = nan_de_init(wpa_s->own_addr, offload, false, &cb);
+	wpa_s->nan_de = nan_de_init(wpa_s->own_addr, offload, false,
+				    wpa_s->max_remain_on_chan, &cb);
 	if (!wpa_s->nan_de)
 		return -1;
 	return 0;
@@ -336,11 +337,12 @@
 
 
 void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src,
+			 const u8 *a3,
 			 unsigned int freq, const u8 *buf, size_t len)
 {
 	if (!wpa_s->nan_de)
 		return;
-	nan_de_rx_sdf(wpa_s->nan_de, src, freq, buf, len);
+	nan_de_rx_sdf(wpa_s->nan_de, src, a3, freq, buf, len);
 }
 
 
@@ -367,7 +369,7 @@
 		return -1;
 
 	if (p2p) {
-		elems = wpas_p2p_usd_elems(wpa_s);
+		elems = wpas_p2p_usd_elems(wpa_s, service_name);
 		addr = wpa_s->global->p2p_dev_addr;
 	} else {
 		addr = wpa_s->own_addr;
@@ -409,12 +411,22 @@
 		return -1;
 	ret = nan_de_update_publish(wpa_s->nan_de, publish_id, ssi);
 	if (ret == 0 && (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_OFFLOAD) &&
-	    wpas_drv_nan_cancel_publish(wpa_s, publish_id) < 0)
+	    wpas_drv_nan_update_publish(wpa_s, publish_id, ssi) < 0)
 		return -1;
 	return ret;
 }
 
 
+int wpas_nan_usd_unpause_publish(struct wpa_supplicant *wpa_s, int publish_id,
+				 u8 peer_instance_id, const u8 *peer_addr)
+{
+	if (!wpa_s->nan_de)
+		return -1;
+	return nan_de_unpause_publish(wpa_s->nan_de, publish_id,
+				      peer_instance_id, peer_addr);
+}
+
+
 int wpas_nan_usd_subscribe(struct wpa_supplicant *wpa_s,
 			   const char *service_name,
 			   enum nan_service_protocol_type srv_proto_type,
@@ -429,7 +441,7 @@
 		return -1;
 
 	if (p2p) {
-		elems = wpas_p2p_usd_elems(wpa_s);
+		elems = wpas_p2p_usd_elems(wpa_s, service_name);
 		addr = wpa_s->global->p2p_dev_addr;
 	} else {
 		addr = wpa_s->own_addr;
diff --git a/wpa_supplicant/nan_usd.h b/wpa_supplicant/nan_usd.h
index ecb4973..6a43fb2 100644
--- a/wpa_supplicant/nan_usd.h
+++ b/wpa_supplicant/nan_usd.h
@@ -16,6 +16,7 @@
 int wpas_nan_usd_init(struct wpa_supplicant *wpa_s);
 void wpas_nan_usd_deinit(struct wpa_supplicant *wpa_s);
 void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src,
+			 const u8 *a3,
 			 unsigned int freq, const u8 *buf, size_t len);
 void wpas_nan_usd_flush(struct wpa_supplicant *wpa_s);
 int wpas_nan_usd_publish(struct wpa_supplicant *wpa_s, const char *service_name,
@@ -25,6 +26,8 @@
 void wpas_nan_usd_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id);
 int wpas_nan_usd_update_publish(struct wpa_supplicant *wpa_s, int publish_id,
 				const struct wpabuf *ssi);
+int wpas_nan_usd_unpause_publish(struct wpa_supplicant *wpa_s, int publish_id,
+				 u8 peer_instance_id, const u8 *peer_addr);
 int wpas_nan_usd_subscribe(struct wpa_supplicant *wpa_s,
 			   const char *service_name,
 			   enum nan_service_protocol_type srv_proto_type,
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 2dc68b0..aeff965 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -27,6 +27,7 @@
 #include "sme.h"
 #include "notify.h"
 #include "aidl/vendor/aidl.h"
+#include "aidl/mainline/callback_bridge.h"
 
 #ifdef MAINLINE_SUPPLICANT
 #include "aidl/mainline/service.h"
@@ -122,6 +123,8 @@
 			       enum wpa_states new_state,
 			       enum wpa_states old_state)
 {
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
 	if (wpa_s->p2p_mgmt)
 		return;
 
@@ -138,10 +141,14 @@
 	}
 #endif /* CONFIG_FST */
 
-	if (new_state == WPA_COMPLETED)
+	if (new_state == WPA_COMPLETED) {
 		wpas_p2p_notif_connected(wpa_s);
-	else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
+		if (ssid)
+			wpa_drv_roaming(wpa_s, !ssid->bssid_set,
+					ssid->bssid_set ? ssid->bssid : NULL);
+	} else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED) {
 		wpas_p2p_notif_disconnected(wpa_s);
+	}
 
 	sme_state_changed(wpa_s);
 
@@ -918,6 +925,22 @@
 						 id, op_freq);
 }
 
+void wpas_notify_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+				   const u8 *src, u16 bootstrap_method)
+{
+	wpas_dbus_signal_p2p_bootstrap_req(wpa_s, src, bootstrap_method);
+	wpas_aidl_notify_p2p_bootstrap_request(wpa_s, src, P2P_SC_SUCCESS, bootstrap_method, NULL);
+}
+
+void wpas_notify_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s,
+				   const u8 *src, int status,
+				   u16 bootstrap_method)
+{
+	wpas_dbus_signal_p2p_bootstrap_rsp(wpa_s, src, status,
+					   bootstrap_method);
+	wpas_aidl_notify_p2p_bootstrap_response(wpa_s, src, status, bootstrap_method, NULL);
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -1506,12 +1529,22 @@
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
-	wpa_msg(wpa_s, MSG_INFO, NAN_DISCOVERY_RESULT
-		"subscribe_id=%d publish_id=%d address=" MACSTR
-		" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
-		subscribe_id, peer_publish_id, MAC2STR(peer_addr),
-		fsd, fsd_gas, srv_proto_type, ssi_hex);
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_DISCOVERY_RESULT
+		       "subscribe_id=%d publish_id=%d address=" MACSTR
+		       " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
+		       subscribe_id, peer_publish_id, MAC2STR(peer_addr),
+		       fsd, fsd_gas, srv_proto_type, ssi_hex);
 	os_free(ssi_hex);
+
+	wpas_aidl_notify_usd_service_discovered(wpa_s, srv_proto_type,
+		subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+	mainline_aidl_notify_usd_service_discovered(wpa_s, srv_proto_type,
+		subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+
+	wpas_dbus_signal_nan_discovery_result(wpa_s, srv_proto_type,
+					      subscribe_id, peer_publish_id,
+					      peer_addr, fsd, fsd_gas,
+					      ssi, ssi_len);
 }
 
 
@@ -1528,12 +1561,21 @@
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
-	wpa_msg(wpa_s, MSG_INFO, NAN_REPLIED
-		"publish_id=%d address=" MACSTR
-		" subscribe_id=%d srv_proto_type=%u ssi=%s",
-		publish_id, MAC2STR(peer_addr), peer_subscribe_id,
-		srv_proto_type, ssi_hex);
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_REPLIED
+		       "publish_id=%d address=" MACSTR
+		       " subscribe_id=%d srv_proto_type=%u ssi=%s",
+		       publish_id, MAC2STR(peer_addr), peer_subscribe_id,
+		       srv_proto_type, ssi_hex);
 	os_free(ssi_hex);
+
+	wpas_aidl_notify_usd_publish_replied(wpa_s, srv_proto_type,
+		publish_id, peer_subscribe_id, peer_addr, ssi, ssi_len);
+	mainline_aidl_notify_usd_publish_replied(wpa_s, srv_proto_type,
+		publish_id, peer_subscribe_id, peer_addr, ssi, ssi_len);
+
+	wpas_dbus_signal_nan_replied(wpa_s, srv_proto_type, publish_id,
+				     peer_subscribe_id, peer_addr,
+				     ssi, ssi_len);
 }
 
 
@@ -1548,10 +1590,18 @@
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
-	wpa_msg(wpa_s, MSG_INFO, NAN_RECEIVE
-		"id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
-		id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_RECEIVE
+		       "id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
+		       id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
 	os_free(ssi_hex);
+
+	wpas_aidl_notify_usd_message_received(wpa_s, id, peer_instance_id,
+		peer_addr, ssi, ssi_len);
+	mainline_aidl_notify_usd_message_received(wpa_s, id, peer_instance_id,
+		peer_addr, ssi, ssi_len);
+
+	wpas_dbus_signal_nan_receive(wpa_s, id, peer_instance_id, peer_addr,
+				     ssi, ssi_len);
 }
 
 
@@ -1574,9 +1624,15 @@
 					int publish_id,
 					enum nan_de_reason reason)
 {
-	wpa_msg(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED
-		"publish_id=%d reason=%s",
-		publish_id, nan_reason_txt(reason));
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED
+		       "publish_id=%d reason=%s",
+		       publish_id, nan_reason_txt(reason));
+
+	wpas_aidl_notify_usd_publish_terminated(wpa_s, publish_id, reason);
+	mainline_aidl_notify_usd_publish_terminated(wpa_s, publish_id, reason);
+
+	wpas_dbus_signal_nan_publish_terminated(wpa_s, publish_id,
+						nan_reason_txt(reason));
 }
 
 
@@ -1584,9 +1640,15 @@
 					  int subscribe_id,
 					  enum nan_de_reason reason)
 {
-	wpa_msg(wpa_s, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
-		"subscribe_id=%d reason=%s",
-		subscribe_id, nan_reason_txt(reason));
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
+		       "subscribe_id=%d reason=%s",
+		       subscribe_id, nan_reason_txt(reason));
+
+	wpas_aidl_notify_usd_subscribe_terminated(wpa_s, subscribe_id, reason);
+	mainline_aidl_notify_usd_subscribe_terminated(wpa_s, subscribe_id, reason);
+
+	wpas_dbus_signal_nan_subscribe_terminated(wpa_s, subscribe_id,
+						  nan_reason_txt(reason));
 }
 
 #endif /* CONFIG_NAN_USD */
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 4e172de..dc8ceaf 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -166,6 +166,11 @@
 void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
 					 const u8 *sa, const u8 *go_dev_addr,
 					 const u8 *bssid, int id, int op_freq);
+void wpas_notify_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+				   const u8 *src, u16 bootstrap_method);
+void wpas_notify_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s,
+				   const u8 *src, int status,
+				   u16 bootstrap_method);
 void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
 				    struct wpa_ssid *ssid);
 void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 768b917..f924dde 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -23,8 +23,10 @@
 #include "ap/wps_hostapd.h"
 #include "ap/p2p_hostapd.h"
 #include "ap/dfs.h"
+#include "ap/wpa_auth.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "ap.h"
@@ -154,7 +156,8 @@
 static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
 					enum wpa_driver_if_type type);
 static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
-					    int already_deleted);
+					    int already_deleted,
+					    const char *reason);
 static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
 					     struct wpa_used_freq_data *freqs,
 					     unsigned int num);
@@ -1014,7 +1017,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
 			   "timeout");
 		wpa_s->p2p_in_provisioning = 0;
-		wpas_p2p_group_formation_failed(wpa_s, 1);
+		wpas_p2p_group_formation_failed(wpa_s, 1, reason);
 	}
 
 	wpa_s->p2p_in_invitation = 0;
@@ -1180,7 +1183,7 @@
 
 static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
 					   struct wpa_ssid *ssid,
-					   const u8 *go_dev_addr)
+					   const u8 *go_dev_addr, int dik_id)
 {
 	struct wpa_ssid *s;
 	int changed = 0;
@@ -1193,6 +1196,9 @@
 		    s->ssid_len == ssid->ssid_len &&
 		    os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
 			break;
+
+		if (dik_id && s->go_dik_id == dik_id)
+			break;
 	}
 
 	if (s) {
@@ -1229,12 +1235,19 @@
 	s->bssid_set = 1;
 	os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
 	s->mode = ssid->mode;
-	s->auth_alg = WPA_AUTH_ALG_OPEN;
-	s->key_mgmt = WPA_KEY_MGMT_PSK;
-	s->proto = WPA_PROTO_RSN;
+	s->auth_alg = ssid->auth_alg;
+	s->key_mgmt = ssid->key_mgmt;
+	s->proto = ssid->proto;
 	s->pbss = ssid->pbss;
+	s->pmk_valid = ssid->pmk_valid;
 	s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
 	s->export_keys = 1;
+	s->go_dik_id = dik_id;
+
+	if (ssid->sae_password) {
+		os_free(s->sae_password);
+		s->sae_password = os_strdup(ssid->sae_password);
+	}
 	if (ssid->passphrase) {
 		os_free(s->passphrase);
 		s->passphrase = os_strdup(ssid->passphrase);
@@ -1268,8 +1281,43 @@
 }
 
 
+static void wpas_p2p2_add_group_client_dik_id(struct wpa_ssid *s, int dik_id)
+{
+	size_t i;
+	bool found = false;
+	size_t num = int_array_len(s->p2p2_client_list);
+
+	for (i = 0; i < num; i++) {
+		if (s->p2p2_client_list[i] != dik_id)
+			continue;
+
+		if (i == num - 1)
+			return; /* already the most recent entry */
+
+		/* Move the entry to mark it most recent */
+		os_memmove(s->p2p2_client_list + i,
+			   s->p2p2_client_list + i + 1,
+			   (num - i - 1) * sizeof(int));
+		s->p2p2_client_list[num - 1] = dik_id;
+		found = true;
+		break;
+	}
+
+	if (!found && num < P2P_MAX_STORED_CLIENTS) {
+		int_array_add_unique(&s->p2p2_client_list, dik_id);
+	} else if (!found && s->p2p2_client_list) {
+		/* Not enough room for an additional entry - drop the oldest
+		 * entry
+		 */
+		os_memmove(s->p2p2_client_list, s->p2p2_client_list + 1,
+			   (num - 1) * sizeof(int));
+		s->p2p2_client_list[num - 1] = dik_id;
+	}
+}
+
+
 static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
-						 const u8 *addr)
+						 const u8 *addr, int dik_id)
 {
 	struct wpa_ssid *ssid, *s;
 	u8 *n;
@@ -1294,6 +1342,11 @@
 	if (s == NULL)
 		return;
 
+	if (dik_id) {
+		wpas_p2p2_add_group_client_dik_id(s, dik_id);
+		goto done;
+	}
+
 	for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
 		if (!ether_addr_equal(s->p2p_client_list + i * 2 * ETH_ALEN,
 				      addr))
@@ -1341,6 +1394,7 @@
 			  0xff, ETH_ALEN);
 	}
 
+done:
 	if (p2p_wpa_s->conf->update_config &&
 	    wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
@@ -1392,8 +1446,112 @@
 }
 
 
+int wpas_p2p_remove_all_identity(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_dev_ik *ik;
+
+	for (ik = wpa_s->conf->identity; ik; ik = ik->next)
+		wpa_config_remove_identity(wpa_s->conf, ik->id);
+
+	if (wpa_s->conf->update_config &&
+	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int wpas_p2p_store_identity(struct wpa_supplicant *wpa_s, u8 cipher,
+				   const u8 *dik_data, size_t dik_len,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *pmkid)
+{
+	struct wpa_dev_ik *ik;
+
+	for (ik = wpa_s->conf->identity; ik; ik = ik->next) {
+		if (dik_len == wpabuf_len(ik->dik) &&
+		    os_memcmp(dik_data, wpabuf_head(ik->dik), dik_len) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Remove previous device identity entry for matching DIK");
+			wpa_config_remove_identity(wpa_s->conf, ik->id);
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Create a new device identity entry");
+	ik = wpa_config_add_identity(wpa_s->conf);
+	if (!ik)
+		return 0;
+
+	ik->dik = wpabuf_alloc_copy(dik_data, dik_len);
+	if (!ik->dik)
+		goto fail;
+	ik->pmk = wpabuf_alloc_copy(pmk, pmk_len);
+	if (!ik->pmk)
+		goto fail;
+	ik->pmkid = wpabuf_alloc_copy(pmkid, PMKID_LEN);
+	if (!ik->pmkid)
+		goto fail;
+
+	ik->dik_cipher = cipher;
+
+	if (wpa_s->conf->update_config &&
+	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+		return 0;
+	}
+	return ik->id;
+
+fail:
+	wpa_config_remove_identity(wpa_s->conf, ik->id);
+	return 0;
+}
+
+
+static int wpas_p2p_store_go_identity(struct wpa_supplicant *wpa_s,
+				      const u8 *go_dev_addr, const u8 *bssid)
+{
+	int ret;
+	u8 cipher;
+	const u8 *dik_data, *pmk, *pmkid;
+	size_t dik_len, pmk_len;
+	u8 iface_addr[ETH_ALEN];
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (!wpa_s->p2p2)
+		return 0;
+
+	ret = p2p_get_dev_identity_key(p2p_wpa_s->global->p2p, go_dev_addr,
+				       &dik_data, &dik_len, &cipher);
+	if (ret)
+		return 0;
+
+	ret = p2p_get_interface_addr(p2p_wpa_s->global->p2p, go_dev_addr,
+				     iface_addr);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Fetch PMK for GO BSSID " MACSTR,
+			   MAC2STR(bssid));
+		os_memcpy(iface_addr, bssid, ETH_ALEN);
+	}
+	ret = wpa_sm_pmksa_get_pmk(wpa_s->wpa, iface_addr, &pmk, &pmk_len,
+				   &pmkid);
+	if (ret)
+		return 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Storing Device identity of GO (Interface Addr " MACSTR
+		   ")",
+		   MAC2STR(iface_addr));
+	return wpas_p2p_store_identity(p2p_wpa_s, cipher, dik_data, dik_len,
+				       pmk, pmk_len, pmkid);
+}
+
+
 static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
-					   int success, int already_deleted)
+					   int already_deleted,
+					   const char *failure_reason)
 {
 	struct wpa_ssid *ssid;
 	int client;
@@ -1410,7 +1568,7 @@
 	if (wpa_s->p2p_go_group_formation_completed) {
 		wpa_s->global->p2p_group_formation = NULL;
 		wpa_s->p2p_in_provisioning = 0;
-	} else if (wpa_s->p2p_in_provisioning && !success) {
+	} else if (wpa_s->p2p_in_provisioning && failure_reason) {
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"P2P: Stop provisioning state due to failure");
 		wpa_s->p2p_in_provisioning = 0;
@@ -1419,10 +1577,10 @@
 	wpa_s->p2p_retry_limit = 0;
 	wpa_s->group_formation_reported = 1;
 
-	if (!success) {
+	if (failure_reason) {
 		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_GROUP_FORMATION_FAILURE);
-		wpas_notify_p2p_group_formation_failure(wpa_s, "");
+		wpas_notify_p2p_group_formation_failure(wpa_s, failure_reason);
 		if (already_deleted)
 			return;
 		wpas_p2p_group_delete(wpa_s,
@@ -1481,7 +1639,7 @@
 
 	if (persistent)
 		wpas_p2p_store_persistent_group(wpa_s->p2pdev,
-						ssid, go_dev_addr);
+						ssid, go_dev_addr, 0);
 	else {
 		os_free(wpa_s->global->add_psk);
 		wpa_s->global->add_psk = NULL;
@@ -1690,8 +1848,11 @@
 	if (listen_freq != (int) freq && send_freq != (int) freq) {
 		int res;
 
-		wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)",
-			   listen_freq, send_freq, freq);
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u dst="
+			   MACSTR " src=" MACSTR " bssid=" MACSTR,
+			   listen_freq, send_freq, freq, MAC2STR(dst),
+			   MAC2STR(src), MAC2STR(bssid));
 		res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
 					    len, wait_time);
 		if (res == 0 && scheduled)
@@ -1722,6 +1883,125 @@
 }
 
 
+#ifdef CONFIG_PASN
+
+struct wpa_p2p_pasn_auth_work {
+	u8 peer_addr[ETH_ALEN];
+	int freq;
+	bool verify;
+	int force_freq;
+	int pref_freq;
+	enum p2p_invite_role role;
+	u8 *ssid;
+	size_t ssid_len;
+	u8 bssid[ETH_ALEN];
+	u8 go_dev_addr[ETH_ALEN];
+};
+
+
+static void wpas_p2p_pasn_free_auth_work(struct wpa_p2p_pasn_auth_work *awork)
+{
+	if (!awork)
+		return;
+	os_free(awork->ssid);
+	os_free(awork);
+}
+
+
+static void wpas_p2p_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "P2P PASN: Cancel p2p-pasn-start-auth work");
+
+	/* Remove pending/started work */
+	radio_remove_works(wpa_s, "p2p-pasn-start-auth", 0);
+}
+
+
+static void wpas_p2p_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+	int ret;
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpa_p2p_pasn_auth_work *awork = work->ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	const u8 *peer_addr = NULL;
+	const u8 *bssid = NULL;
+	const u8 *go_dev_addr = NULL;
+
+	if (deinit) {
+		if (!work->started) {
+			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+					     wpa_s->p2pdev, NULL);
+		}
+		wpas_p2p_pasn_free_auth_work(awork);
+		return;
+	}
+
+	if (!is_zero_ether_addr(awork->peer_addr))
+		peer_addr = awork->peer_addr;
+	if (!is_zero_ether_addr(awork->bssid))
+		bssid = awork->bssid;
+	if (!is_zero_ether_addr(awork->go_dev_addr))
+		go_dev_addr = awork->go_dev_addr;
+
+
+	if (awork->verify)
+		ret = p2p_initiate_pasn_verify(p2p, peer_addr, awork->freq,
+					       awork->role, bssid, awork->ssid,
+					       awork->ssid_len,
+					       awork->force_freq, go_dev_addr,
+					       awork->pref_freq);
+	else
+		ret = p2p_initiate_pasn_auth(p2p, peer_addr, awork->freq);
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P PASN: Failed to start PASN authentication");
+		goto fail;
+	}
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+			     wpa_s->p2pdev, NULL);
+	eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+			       wpas_p2p_group_formation_timeout,
+			       wpa_s->p2pdev, NULL);
+	wpa_s->p2p_pasn_auth_work = work;
+	return;
+
+fail:
+	wpas_p2p_pasn_free_auth_work(awork);
+	work->ctx = NULL;
+	radio_work_done(work);
+}
+
+
+static int wpas_p2p_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
+				       const u8 *peer_addr, int freq)
+{
+	struct wpa_p2p_pasn_auth_work *awork;
+
+	wpas_p2p_pasn_cancel_auth_work(wpa_s);
+	wpa_s->p2p_pasn_auth_work = NULL;
+
+	awork = os_zalloc(sizeof(*awork));
+	if (!awork)
+		return -1;
+
+	awork->freq = freq;
+	os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
+
+	if (radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1,
+			   wpas_p2p_pasn_auth_start_cb, awork) < 0) {
+		wpas_p2p_pasn_free_auth_work(awork);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P PASN: Authentication work successfully added");
+	return 0;
+}
+
+#endif /* CONFIG_PASN */
+
+
 static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *params)
 {
@@ -1735,6 +2015,101 @@
 }
 
 
+static void wpas_start_gc(struct wpa_supplicant *wpa_s,
+			  struct p2p_go_neg_results *res)
+{
+	struct os_reltime now;
+	struct wpa_ssid *ssid;
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (!res->ssid_len) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: SSID info not present");
+		return;
+	}
+
+	wpa_s->group_formation_reported = 0;
+	wpa_printf(MSG_DEBUG, "P2P: Start connect for peer " MACSTR
+		   " dev_addr " MACSTR,
+		   MAC2STR(res->peer_interface_addr),
+		   MAC2STR(res->peer_device_addr));
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start connect for SSID",
+			  res->ssid, res->ssid_len);
+	wpa_supplicant_ap_deinit(wpa_s);
+	wpas_copy_go_neg_results(wpa_s, res);
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (!ssid) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Could not add network for client");
+		return;
+	}
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+	wpa_config_set_network_defaults(ssid);
+	ssid->temporary = 1;
+	ssid->p2p_group = 1;
+
+	ssid->ssid = os_memdup(res->ssid, res->ssid_len);
+	if (!ssid->ssid)
+		return;
+	ssid->ssid_len = res->ssid_len;
+
+	os_memcpy(ssid->bssid, res->peer_interface_addr, ETH_ALEN);
+
+	if (res->akmp == WPA_KEY_MGMT_PASN && res->sae_password[0]) {
+		ssid->auth_alg = WPA_AUTH_ALG_SAE;
+		ssid->sae_password = os_strdup(res->sae_password);
+		if (!ssid->sae_password)
+			return;
+	} else if (res->akmp == WPA_KEY_MGMT_SAE && res->pmk_len) {
+		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		entry = os_zalloc(sizeof(*entry));
+		if (!entry)
+			return;
+		os_memcpy(entry->aa, res->peer_interface_addr, ETH_ALEN);
+		os_memcpy(entry->pmkid, res->pmkid, PMKID_LEN);
+		entry->pmk_len = res->pmk_len;
+		os_memcpy(entry->pmk, res->pmk, res->pmk_len);
+		entry->akmp = res->akmp;
+		os_get_reltime(&now);
+		entry->expiration = now.sec + 43200;
+		entry->reauth_time = now.sec + 43200 * 70 / 100;
+		entry->network_ctx = ssid;
+		os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
+
+		wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+		ssid->pmk_valid = true;
+	} else if (res->akmp == WPA_KEY_MGMT_SAE && res->sae_password[0]) {
+		ssid->auth_alg = WPA_AUTH_ALG_SAE;
+		ssid->sae_password = os_strdup(res->sae_password);
+		if (!ssid->sae_password)
+			return;
+	}
+
+	if (res->psk_set) {
+		os_memcpy(ssid->psk, res->psk, 32);
+		ssid->psk_set = 1;
+	}
+	ssid->proto = WPA_PROTO_RSN;
+	ssid->key_mgmt = WPA_KEY_MGMT_SAE;
+	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+	ssid->group_cipher = WPA_CIPHER_CCMP;
+	if (res->cipher)
+		ssid->pairwise_cipher |= res->cipher;
+	ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+	ssid->disabled = 0;
+	wpa_s->show_group_started = 1;
+	wpa_s->p2p_in_invitation = 1;
+	wpa_s->p2p_go_group_formation_completed = 0;
+	wpa_s->global->p2p_group_formation = wpa_s;
+	ssid->rsn_overriding = RSN_OVERRIDING_ENABLED;
+
+	wpa_s->current_ssid = ssid;
+	wpa_supplicant_update_scan_results(wpa_s, res->peer_interface_addr);
+	wpa_supplicant_select_network(wpa_s, ssid);
+}
+
+
 static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *res)
 {
@@ -1879,6 +2254,20 @@
 		return;
 	}
 
+	if (wpa_s->ap_iface && params->p2p2 &&
+	    params->akmp == WPA_KEY_MGMT_SAE) {
+		struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
+
+		wpa_auth_pmksa_add_sae(hapd->wpa_auth,
+				       params->peer_device_addr,
+				       params->pmk, params->pmk_len,
+				       params->pmkid, WPA_KEY_MGMT_SAE,
+				       false);
+		hostapd_add_pmkid(hapd, params->peer_device_addr,
+				  params->pmk, params->pmk_len,
+				  params->pmkid, WPA_KEY_MGMT_SAE);
+	}
+
 	p2p_go_save_group_common_freqs(wpa_s, params);
 	p2p_go_dump_common_freqs(wpa_s);
 
@@ -1917,7 +2306,7 @@
 		if (params->persistent_group) {
 			wpas_p2p_store_persistent_group(
 				wpa_s->p2pdev, ssid,
-				wpa_s->global->p2p_dev_addr);
+				wpa_s->global->p2p_dev_addr, 0);
 			wpas_p2p_add_psk_list(wpa_s, ssid);
 		}
 
@@ -1944,13 +2333,21 @@
 		return;
 	}
 
-	wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
 	if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
 					      params->peer_interface_addr)) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
 			   "filtering");
 		return;
 	}
+
+	if (params->p2p2) {
+		wpas_group_formation_completed(wpa_s, 0, NULL);
+		wpa_printf(MSG_DEBUG,
+			   "P2P2: Group formation completed - first connection in progress");
+		goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
 	if (params->wps_method == WPS_PBC) {
 		wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
 					  params->peer_device_addr);
@@ -1971,6 +2368,7 @@
 	} else if (wpa_s->p2p_pin[0])
 		wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
 					  wpa_s->p2p_pin, NULL, 0, 0);
+out:
 	os_free(wpa_s->go_params);
 	wpa_s->go_params = NULL;
 }
@@ -2053,9 +2451,9 @@
 }
 
 
-static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
-			      struct p2p_go_neg_results *params,
-			      int group_formation)
+static void wpas_start_go(struct wpa_supplicant *wpa_s,
+			  struct p2p_go_neg_results *params,
+			  int group_formation, enum wpa_p2p_mode p2p_mode)
 {
 	struct wpa_ssid *ssid;
 
@@ -2150,6 +2548,24 @@
 		wpa_config_update_psk(ssid);
 	ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity;
 
+	ssid->p2p_mode = p2p_mode;
+	if (params->p2p2) {
+		if (params->akmp == WPA_KEY_MGMT_SAE)
+			ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		else
+			ssid->auth_alg |= WPA_AUTH_ALG_SAE;
+
+		ssid->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PASN;
+		ssid->sae_password = os_strdup(params->sae_password);
+		/* In PCC, RSNE indicates PMF to be disabled while RSNOE/RSNO2E
+		 * requires PMF for SAE. */
+		if (ssid->p2p_mode != WPA_P2P_MODE_WFD_PCC)
+			ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+		ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+		if (params->cipher)
+			ssid->pairwise_cipher |= params->cipher;
+	}
+
 	wpa_s->ap_configured_cb = p2p_go_configured;
 	wpa_s->ap_configured_cb_ctx = wpa_s;
 	wpa_s->ap_configured_cb_data = wpa_s->go_params;
@@ -2364,6 +2780,7 @@
 	wpa_s->global->pending_group_iface_for_p2ps = 0;
 
 	wpas_p2p_clone_config(group_wpa_s, wpa_s);
+	group_wpa_s->p2p2 = wpa_s->p2p2;
 
 	if (wpa_s->conf->p2p_interface_random_mac_addr) {
 		if (wpa_drv_set_mac_addr(group_wpa_s,
@@ -2396,19 +2813,34 @@
 					     void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
+	}
+#endif /* CONFIG_PASN */
+
 	wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
-	wpas_p2p_group_formation_failed(wpa_s, 0);
+	wpas_p2p_group_formation_failed(wpa_s, 0, "Group formation timed out");
 }
 
 
 static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
-					    int already_deleted)
+					    int already_deleted,
+					    const char *reason)
 {
+	 /* reason == NULL would indicate success in
+	  * wpas_group_formation_completed(), so make sure that is not the case
+	  * here. */
+	if (!reason)
+		reason = "";
+
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 			     wpa_s->p2pdev, NULL);
 	if (wpa_s->global->p2p)
 		p2p_group_formation_failed(wpa_s->global->p2p);
-	wpas_group_formation_completed(wpa_s, 0, already_deleted);
+	wpas_group_formation_completed(wpa_s, already_deleted, reason);
 }
 
 
@@ -2450,6 +2882,50 @@
 }
 
 
+static void wpas_set_go_security_config(void *ctx,
+					struct p2p_go_neg_results *params)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_supplicant *tmp, *ifs = NULL;
+	struct hostapd_data *hapd;
+
+	if (!params->p2p2)
+		return;
+
+	dl_list_for_each(tmp, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		struct wpa_ssid *ssid = tmp->current_ssid;
+
+		if (ssid && ssid->mode == WPAS_MODE_P2P_GO &&
+		    ssid->ssid && ssid->ssid_len == params->ssid_len &&
+		    os_memcmp(ssid->ssid, params->ssid, params->ssid_len) == 0)
+		{
+			ifs = tmp;
+			break;
+		}
+	}
+
+	if (!ifs || !ifs->ap_iface)
+		return;
+
+	hapd = ifs->ap_iface->bss[0];
+	hapd->conf->wps_state = 0;
+
+	if (params->akmp == WPA_KEY_MGMT_SAE) {
+		wpa_printf(MSG_DEBUG, "P2P: Adding PMK for peer: " MACSTR,
+			   MAC2STR(params->peer_device_addr));
+		wpa_auth_pmksa_add_sae(hapd->wpa_auth,
+				       params->peer_device_addr,
+				       params->pmk, params->pmk_len,
+				       params->pmkid, WPA_KEY_MGMT_SAE,
+				       false);
+		hostapd_add_pmkid(hapd, params->peer_device_addr,
+				  params->pmk, params->pmk_len,
+				  params->pmkid, WPA_KEY_MGMT_SAE);
+	}
+}
+
+
 static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -2461,6 +2937,13 @@
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
 
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
+	}
+#endif /* CONFIG_PASN */
+
 	if (res->status) {
 		wpa_msg_global(wpa_s, MSG_INFO,
 			       P2P_EVENT_GO_NEG_FAILURE "status=%d",
@@ -2516,7 +2999,8 @@
 			wpas_p2p_remove_pending_group_interface(wpa_s);
 			eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
 					     wpa_s, NULL);
-			wpas_p2p_group_formation_failed(wpa_s, 1);
+			wpas_p2p_group_formation_failed(wpa_s, 1,
+							"Could not initialize group interface");
 			return;
 		}
 		os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
@@ -2534,12 +3018,18 @@
 		os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
 			  sizeof(group_wpa_s->p2p_pin));
 		group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
+		group_wpa_s->p2p2 = res->p2p2;
+		group_wpa_s->p2p_bootstrap = wpa_s->p2p_bootstrap;
 	}
+
 	if (res->role_go) {
-		wpas_start_wps_go(group_wpa_s, res, 1);
+		wpas_start_go(group_wpa_s, res, 1, group_wpa_s->p2p_mode);
 	} else {
 		os_get_reltime(&group_wpa_s->scan_min_time);
-		wpas_start_wps_enrollee(group_wpa_s, res);
+		if (res->p2p2)
+			wpas_start_gc(group_wpa_s, res);
+		else
+			wpas_start_wps_enrollee(group_wpa_s, res);
 	}
 
 	wpa_s->global->p2p_long_listen = 0;
@@ -2646,8 +3136,7 @@
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
 		       " p2p_dev_addr=" MACSTR
-		       " pri_dev_type=%s name='%s' config_methods=0x%x "
-		       "dev_capab=0x%x group_capab=0x%x%s%s%s%s%s new=%d",
+		       " pri_dev_type=%s name='%s' config_methods=0x%x dev_capab=0x%x group_capab=0x%x%s%s%s%s%s new=%d pcea_cap_info=0x%x bootstrap_methods=0x%x pasn_type=0x%x",
 		       MAC2STR(addr), MAC2STR(info->p2p_device_addr),
 		       wps_dev_type_bin2str(info->pri_dev_type, devtype,
 					    sizeof(devtype)),
@@ -2658,7 +3147,9 @@
 		       wfd_r2_dev_info_hex ? " wfd_r2_dev_info=0x" : "",
 		       wfd_r2_dev_info_hex ? wfd_r2_dev_info_hex : "",
 		       info->vendor_elems ? " vendor_elems=1" : "",
-		       new_device);
+		       new_device, info->pcea_cap_info,
+		       info->pairing_config.bootstrap_methods,
+		       info->pairing_config.pasn_type);
 
 done:
 	os_free(wfd_dev_info_hex);
@@ -2733,7 +3224,7 @@
 	unsigned int duration;
 
 	if (deinit) {
-		if (work->started) {
+		if (work->started && !wpa_s->p2p_removing_listen_work) {
 			wpa_s->p2p_listen_work = NULL;
 			wpas_stop_listen(wpa_s);
 		}
@@ -2748,6 +3239,7 @@
 	if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
 			   "report received Probe Request frames");
+		p2p_listen_failed(wpa_s->global->p2p, lwork->freq);
 		wpas_p2p_listen_work_done(wpa_s);
 		return;
 	}
@@ -2768,6 +3260,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
 			   "to remain on channel (%u MHz) for Listen "
 			   "state", lwork->freq);
+		p2p_listen_failed(wpa_s->global->p2p, lwork->freq);
 		wpas_p2p_listen_work_done(wpa_s);
 		wpa_s->pending_listen_freq = 0;
 		return;
@@ -2831,10 +3324,13 @@
 
 	wpas_p2p_listen_work_done(wpa_s);
 
-	if (radio_work_pending(wpa_s, "p2p-listen")) {
+	if (!wpa_s->p2p_removing_listen_work &&
+	    radio_work_pending(wpa_s, "p2p-listen")) {
+		wpa_s->p2p_removing_listen_work = true;
 		wpa_printf(MSG_DEBUG,
 			   "P2P: p2p-listen is still pending - remove it");
 		radio_remove_works(wpa_s, "p2p-listen", 0);
+		wpa_s->p2p_removing_listen_work = false;
 	}
 }
 
@@ -3217,7 +3713,8 @@
 				  size_t ssid_len, int *go, u8 *group_bssid,
 				  int *force_freq, int persistent_group,
 				  const struct p2p_channels *channels,
-				  int dev_pw_id)
+				  int dev_pw_id, bool p2p2, const u8 **new_ssid,
+				  size_t *new_ssid_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
@@ -3281,13 +3778,47 @@
 
 	for (s = wpa_s->conf->ssid; s; s = s->next) {
 		if (s->disabled == 2 &&
-		    ether_addr_equal(s->bssid, go_dev_addr) &&
+		    (p2p2 || ether_addr_equal(s->bssid, go_dev_addr)) &&
 		    s->ssid_len == ssid_len &&
 		    os_memcmp(ssid, s->ssid, ssid_len) == 0)
 			break;
 	}
 
-	if (!s) {
+	if (p2p2) {
+		int dik_id;
+		u8 go_ssid[SSID_MAX_LEN];
+
+		dik_id = p2p_get_dik_id(wpa_s->global->p2p, sa);
+		s = wpa_config_get_network_with_dik_id(wpa_s->conf, dik_id);
+		if (!s) {
+			wpa_printf(MSG_DEBUG, "P2P2: Invitation from " MACSTR
+				   " requested reinvocation of an unknown group",
+				   MAC2STR(sa));
+			return P2P_SC_FAIL_UNKNOWN_GROUP;
+		}
+		os_free(s->ssid);
+		if (s->mode == WPAS_MODE_P2P_GO) {
+			p2p_build_ssid(wpa_s->global->p2p, go_ssid,
+				       &s->ssid_len);
+			s->ssid = os_memdup(go_ssid, s->ssid_len);
+			if (!s->ssid) {
+				s->ssid_len = 0;
+				return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+			}
+			wpa_printf(MSG_DEBUG,
+				   "P2P: New random SSID for the group: %s",
+				   wpa_ssid_txt(s->ssid, s->ssid_len));
+			*new_ssid = s->ssid;
+			*new_ssid_len = s->ssid_len;
+		} else {
+			s->ssid_len = ssid_len;
+			s->ssid = os_memdup(ssid, ssid_len);
+			if (!s->ssid) {
+				s->ssid_len = 0;
+				return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+			}
+		}
+	} else if (!s) {
 		wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
 			   " requested reinvocation of an unknown group",
 			   MAC2STR(sa));
@@ -3376,7 +3907,8 @@
 static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
 				     const u8 *ssid, size_t ssid_len,
 				     const u8 *go_dev_addr, u8 status,
-				     int op_freq)
+				     int op_freq, const u8 *pmkid,
+				     const u8 *pmk, size_t pmk_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
@@ -3393,19 +3925,25 @@
 			   " was accepted; op_freq=%d MHz, SSID=%s",
 			   MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len));
 		if (s) {
+			const char *ssid_txt;
+
+			ssid_txt = wpa_ssid_txt(s->ssid, s->ssid_len);
 			int go = s->mode == WPAS_MODE_P2P_GO;
 			if (go) {
 				wpa_msg_global(wpa_s, MSG_INFO,
 					       P2P_EVENT_INVITATION_ACCEPTED
 					       "sa=" MACSTR
-					       " persistent=%d freq=%d",
-					       MAC2STR(sa), s->id, op_freq);
+					       " persistent=%d freq=%d ssid=\"%s\" go_dev_addr="
+					       MACSTR, MAC2STR(sa), s->id,
+					       op_freq, ssid_txt,
+					       MAC2STR(go_dev_addr));
 			} else {
 				wpa_msg_global(wpa_s, MSG_INFO,
 					       P2P_EVENT_INVITATION_ACCEPTED
 					       "sa=" MACSTR
-					       " persistent=%d",
-					       MAC2STR(sa), s->id);
+					       " persistent=%d ssid=\"%s\" go_dev_addr=" MACSTR,
+					       MAC2STR(sa), s->id, ssid_txt,
+					       MAC2STR(go_dev_addr));
 			}
 			wpas_p2p_group_add_persistent(
 				wpa_s, s, go, 0, op_freq, 0,
@@ -3416,7 +3954,7 @@
 				wpa_s->conf->p2p_go_edmg, NULL,
 				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
 				1, is_p2p_allow_6ghz(wpa_s->global->p2p), 0,
-				NULL);
+				bssid, sa, pmkid, pmk, pmk_len);
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
 			wpa_msg_global(wpa_s, MSG_INFO,
@@ -3531,23 +4069,87 @@
 }
 
 
-static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
+static void wpas_msg_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+					   int status, const u8 *new_ssid,
+					   size_t new_ssid_len, const u8 *bssid,
+					   const u8 *go_dev_addr)
+{
+	int res;
+	char buf[500];
+	char *pos, *end;
+	const char *ssid_txt = NULL;
+
+	pos = buf;
+	end = buf + sizeof(buf);
+
+	if (go_dev_addr && new_ssid && new_ssid_len) {
+		ssid_txt = wpa_ssid_txt(new_ssid, new_ssid_len);
+	} else if (go_dev_addr) {
+		struct wpa_ssid *ssid;
+
+		ssid = wpa_config_get_network(wpa_s->conf,
+					      wpa_s->pending_invite_ssid_id);
+		if (ssid)
+			ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+	}
+
+	res = os_snprintf(pos, end - pos, "status=%d", status);
+	if (os_snprintf_error(end - pos, res))
+		goto fail;
+	pos += res;
+
+	if (bssid) {
+		res = os_snprintf(pos, end - pos, " " MACSTR, MAC2STR(bssid));
+		if (os_snprintf_error(end - pos, res))
+			goto fail;
+		pos += res;
+	}
+
+	if (ssid_txt) {
+		res = os_snprintf(pos, end - pos, " ssid=\"%s\"", ssid_txt);
+		if (os_snprintf_error(end - pos, res))
+			goto fail;
+		pos += res;
+	}
+
+	if (go_dev_addr) {
+		res = os_snprintf(pos, end - pos, " go_dev_addr=" MACSTR,
+				  MAC2STR(go_dev_addr));
+		if (os_snprintf_error(end - pos, res))
+			goto fail;
+		pos += res;
+	}
+
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT "%s", buf);
+	return;
+
+fail:
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Failed to send P2P-INVITATION-RESULT message");
+}
+
+
+static void wpas_invitation_result(void *ctx, int status, const u8 *new_ssid,
+				   size_t new_ssid_len, const u8 *bssid,
 				   const struct p2p_channels *channels,
 				   const u8 *peer, int neg_freq,
-				   int peer_oper_freq)
+				   int peer_oper_freq, const u8 *pmkid,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *go_dev_addr)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *ssid;
 	int freq;
 
-	if (bssid) {
-		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
-			       "status=%d " MACSTR,
-			       status, MAC2STR(bssid));
-	} else {
-		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
-			       "status=%d ", status);
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
 	}
+#endif /* CONFIG_PASN */
+
+	wpas_msg_p2p_invitation_result(wpa_s, status, new_ssid, new_ssid_len,
+				       bssid, go_dev_addr);
 	wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
 
 	wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
@@ -3610,6 +4212,16 @@
 		return;
 	}
 
+	if (new_ssid && new_ssid_len) {
+		os_free(ssid->ssid);
+		ssid->ssid = os_memdup(new_ssid, new_ssid_len);
+		if (!ssid->ssid) {
+			ssid->ssid_len = 0;
+			return;
+		}
+		ssid->ssid_len = new_ssid_len;
+	}
+
 	/*
 	 * The peer could have missed our ctrl::ack frame for Invitation
 	 * Response and continue retransmitting the frame. To reduce the
@@ -3647,7 +4259,7 @@
 				      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
 				      0, 1,
 				      is_p2p_allow_6ghz(wpa_s->global->p2p), 0,
-				      NULL);
+				      bssid, peer, pmkid, pmk, pmk_len);
 }
 
 
@@ -4716,10 +5328,13 @@
 					persistent_go->mode ==
 					WPAS_MODE_P2P_GO ?
 					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-					0, 0, false, 0, NULL);
+					0, 0, false, 0, NULL, NULL, NULL, NULL,
+					0);
 			} else if (response_done) {
 				wpas_p2p_group_add(wpa_s, 1, freq,
-						   0, 0, 0, 0, 0, 0, false);
+						   0, 0, 0, 0, 0, 0, false,
+						   wpa_s->p2p2,
+						   WPA_P2P_MODE_WFD_R1);
 			}
 
 			if (passwd_id == DEV_PW_P2PS_DEFAULT) {
@@ -4839,10 +5454,12 @@
 			NULL,
 			persistent_go->mode == WPAS_MODE_P2P_GO ?
 			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0,
-			is_p2p_allow_6ghz(wpa_s->global->p2p), 0, NULL);
+			is_p2p_allow_6ghz(wpa_s->global->p2p), 0, NULL, NULL,
+			NULL, NULL, 0);
 	} else {
 		wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0,
-				   is_p2p_allow_6ghz(wpa_s->global->p2p));
+				   is_p2p_allow_6ghz(wpa_s->global->p2p),
+				   wpa_s->p2p2, WPA_P2P_MODE_WFD_R1);
 	}
 
 	return 1;
@@ -4865,19 +5482,27 @@
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 
 	wpa_printf(MSG_DEBUG, "P2P2: Send bootstrapping comeback PD Request");
-	wpas_p2p_connect(wpa_s, wpa_s->p2p_bootstrap_dev_addr, wpa_s->p2p_pin,
-			 wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
-			 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
-			 wpa_s->p2p_go_vht_center_freq2,
-			 wpa_s->p2p_persistent_id,
-			 wpa_s->p2p_pd_before_go_neg,
-			 wpa_s->p2p_go_ht40,
-			 wpa_s->p2p_go_vht,
-			 wpa_s->p2p_go_max_oper_chwidth,
-			 wpa_s->p2p_go_he,
-			 wpa_s->p2p_go_edmg,
-			 NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p),
-			 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL);
+
+	if (wpa_s->p2p_pd_before_go_neg) {
+		wpas_p2p_connect(wpa_s, wpa_s->p2p_bootstrap_dev_addr,
+				 wpa_s->p2p_pin, wpa_s->p2p_wps_method,
+				 wpa_s->p2p_persistent_group, 0, 0, 0,
+				 wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+				 wpa_s->p2p_go_vht_center_freq2,
+				 wpa_s->p2p_persistent_id, 1,
+				 wpa_s->p2p_go_ht40,
+				 wpa_s->p2p_go_vht,
+				 wpa_s->p2p_go_max_oper_chwidth,
+				 wpa_s->p2p_go_he,
+				 wpa_s->p2p_go_edmg,
+				 NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p),
+				 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL,
+				 false);
+	} else {
+		p2p_prov_disc_req(wpa_s->global->p2p,
+				  wpa_s->p2p_bootstrap_dev_addr, NULL,
+				  0, 0, 0, 1);
+	}
 }
 
 
@@ -4903,26 +5528,185 @@
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_BOOTSTRAP_REQUEST MACSTR
 		       " bootstrap_method=%u", MAC2STR(addr), bootstrap_method);
+
+	wpas_notify_p2p_bootstrap_req(wpa_s, addr, bootstrap_method);
 }
 
 
-static void wpas_bootstrap_completed(void *ctx, const u8 *addr,
-				     enum p2p_status_code status, int freq)
+static void wpas_bootstrap_rsp_rx(void *ctx, const u8 *addr,
+				  enum p2p_status_code status, int freq,
+				  u16 bootstrap_method)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
+	wpas_notify_p2p_bootstrap_rsp(wpa_s, addr, status, bootstrap_method);
+
 	if (status) {
 		wpa_msg_global(wpa_s, MSG_INFO,
 			       P2P_EVENT_BOOTSTRAP_FAILURE MACSTR " status=%d",
 			       MAC2STR(addr), status);
-	} else {
-		wpa_msg_global(wpa_s, MSG_INFO,
-			       P2P_EVENT_BOOTSTRAP_SUCCESS MACSTR " status=%d",
-			       MAC2STR(addr), status);
+		return;
 	}
+
+	wpa_msg_global(wpa_s, MSG_INFO,
+		       P2P_EVENT_BOOTSTRAP_SUCCESS MACSTR " status=%d",
+		       MAC2STR(addr), status);
+
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p_pd_before_go_neg)
+		wpas_p2p_initiate_pasn_auth(wpa_s, addr, freq);
+#endif /* CONFIG_PASN */
 }
 
 
+static int wpas_validate_dira(void *ctx, const u8 *peer_addr,
+			      const u8 *dira_nonce, const u8 *dira_tag)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	int ret;
+	u8 tag[DEVICE_MAX_HASH_LEN];
+	struct wpa_dev_ik *ik;
+	const u8 *addr[3];
+	size_t len[3];
+	const char *label = "DIR";
+
+	addr[0] = (const u8 *) label;
+	len[0] = DIR_STR_LEN;
+	addr[1] = peer_addr;
+	len[1] = ETH_ALEN;
+	addr[2] = dira_nonce;
+	len[2] = DEVICE_IDENTITY_NONCE_LEN;
+
+	for (ik = wpa_s->conf->identity; ik; ik = ik->next) {
+		if (wpabuf_len(ik->dik) != DEVICE_IDENTITY_KEY_LEN ||
+		    ik->dik_cipher != DIRA_CIPHER_VERSION_128)
+			continue;
+
+		ret = hmac_sha256_vector(wpabuf_head(ik->dik),
+					 DEVICE_IDENTITY_KEY_LEN,
+					 3, addr, len, tag);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR,
+				   "P2P2: Failed to derive DIRA Tag");
+			return 0;
+		}
+
+		if (os_memcmp(tag, dira_tag, DEVICE_IDENTITY_TAG_LEN) == 0) {
+			wpa_printf(MSG_DEBUG, "P2P2: DIRA Tag matched");
+			break;
+		}
+	}
+
+	if (!ik)
+		return 0;
+
+#ifdef CONFIG_PASN
+	p2p_pasn_pmksa_set_pmk(wpa_s->global->p2p, wpa_s->global->p2p_dev_addr,
+			       peer_addr,
+			       wpabuf_head(ik->pmk), wpabuf_len(ik->pmk),
+			       wpabuf_head(ik->pmkid));
+#endif /* CONFIG_PASN */
+
+	return ik->id;
+}
+
+
+#ifdef CONFIG_PASN
+
+static int wpas_p2p_initiate_pasn_verify(struct wpa_supplicant *wpa_s,
+					 const u8 *peer,
+					 enum p2p_invite_role role,
+					 const u8 *bssid, const u8 *ssid,
+					 size_t ssid_len,
+					 unsigned int force_freq,
+					 const u8 *go_dev_addr,
+					 unsigned int pref_freq)
+{
+	int freq;
+	struct wpa_p2p_pasn_auth_work *awork;
+
+	wpas_p2p_pasn_cancel_auth_work(wpa_s);
+	wpa_s->p2p_pasn_auth_work = NULL;
+
+	freq = p2p_get_listen_freq(wpa_s->global->p2p, peer);
+	if (freq == -1)
+		return -1;
+
+	awork = os_zalloc(sizeof(*awork));
+	if (!awork)
+		return -1;
+
+	awork->verify = 1;
+	awork->role = role;
+	awork->freq = freq;
+	awork->force_freq = force_freq;
+	awork->pref_freq = pref_freq;
+	os_memcpy(awork->peer_addr, peer, ETH_ALEN);
+	if (go_dev_addr)
+		os_memcpy(awork->go_dev_addr, go_dev_addr, ETH_ALEN);
+	if (bssid)
+		os_memcpy(awork->bssid, bssid, ETH_ALEN);
+	if (ssid_len) {
+		awork->ssid = os_zalloc(ssid_len);
+		if (!awork->ssid) {
+			os_free(awork);
+			return -1;
+		}
+		os_memcpy(awork->ssid, ssid, ssid_len);
+		awork->ssid_len = ssid_len;
+	}
+
+	if (radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1,
+			   wpas_p2p_pasn_auth_start_cb, awork) < 0) {
+		wpas_p2p_pasn_free_auth_work(awork);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P PASN: Auth work successfully added");
+	return 0;
+}
+
+
+static int wpas_p2p_pasn_send_mgmt(void *ctx, const u8 *data, size_t data_len,
+				   int noack, unsigned int freq,
+				   unsigned int wait)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+}
+
+
+static int wpas_p2p_prepare_data_element(void *ctx, const u8 *peer_addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	return p2p_prepare_data_element(p2p, peer_addr);
+}
+
+
+static int wpas_p2p_parse_data_element(void *ctx, const u8 *data, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	return p2p_parse_data_element(p2p, data, len);
+}
+
+
+static int wpas_p2p_pasn_validate_pmkid(void *ctx, const u8 *addr,
+					const u8 *rsn_pmkid)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return p2p_pasn_validate_and_update_pmkid(wpa_s->global->p2p, addr,
+						  rsn_pmkid);
+}
+
+#endif /* CONFIG_PASN */
+
+
 int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s)
 {
 	int ret = 0;
@@ -5018,6 +5802,7 @@
 	p2p.send_action = wpas_send_action;
 	p2p.send_action_done = wpas_send_action_done;
 	p2p.go_neg_completed = wpas_go_neg_completed;
+	p2p.set_go_security_config = wpas_set_go_security_config;
 	p2p.go_neg_req_rx = wpas_go_neg_req_rx;
 	p2p.dev_found = wpas_dev_found;
 	p2p.dev_lost = wpas_dev_lost;
@@ -5049,7 +5834,14 @@
 	p2p.p2p_dfs_chan_enable = wpa_s->conf->p2p_dfs_chan_enable;
 	p2p.register_bootstrap_comeback = wpas_p2p_register_bootstrap_comeback;
 	p2p.bootstrap_req_rx = wpas_bootstrap_req_rx;
-	p2p.bootstrap_completed = wpas_bootstrap_completed;
+	p2p.bootstrap_rsp_rx = wpas_bootstrap_rsp_rx;
+	p2p.validate_dira = wpas_validate_dira;
+#ifdef CONFIG_PASN
+	p2p.pasn_send_mgmt = wpas_p2p_pasn_send_mgmt;
+	p2p.prepare_data_element = wpas_p2p_prepare_data_element;
+	p2p.parse_data_element = wpas_p2p_parse_data_element;
+	p2p.pasn_validate_pmkid = wpas_p2p_pasn_validate_pmkid;
+#endif /* CONFIG_PASN */
 
 	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -5193,6 +5985,18 @@
 				   "P2P: Failed to update configuration");
 	}
 
+	p2p.pairing_config.enable_pairing_setup =
+		wpa_s->conf->p2p_pairing_setup;
+	p2p.pairing_config.enable_pairing_cache =
+		wpa_s->conf->p2p_pairing_cache;
+	p2p.pairing_config.bootstrap_methods =
+		wpa_s->conf->p2p_bootstrap_methods;
+	p2p.pairing_config.pasn_type = wpa_s->conf->p2p_pasn_type;
+	p2p.comeback_after = wpa_s->conf->p2p_comeback_after;
+	p2p.reg_info = wpa_s->conf->p2p_reg_info;
+	p2p.twt_power_mgmt = wpa_s->conf->p2p_twt_power_mgmt;
+	p2p.chan_switch_req_enable = wpa_s->conf->p2p_chan_switch_req_enable;
+
 	global->p2p = p2p_init(&p2p);
 	if (global->p2p == NULL)
 		return -1;
@@ -5325,6 +6129,20 @@
 }
 
 
+#ifdef CONFIG_PASN
+static int wpas_p2p_config_sae_password(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p || !ssid->sae_password)
+		return -2;
+
+	return p2p_config_sae_password(p2p, ssid->sae_password);
+}
+#endif /* CONFIG_PASN */
+
+
 static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
 				 const u8 *peer_addr,
 				 enum p2p_wps_method wps_method,
@@ -5489,7 +6307,7 @@
 	if (scan_res)
 		wpas_p2p_scan_res_handler(wpa_s, scan_res);
 
-	if (wpa_s->p2p_auto_pd) {
+	if (!wpa_s->p2p2 && wpa_s->p2p_auto_pd) {
 		int join = wpas_p2p_peer_go(wpa_s,
 					    wpa_s->pending_join_dev_addr);
 		if (join == 0 &&
@@ -5530,15 +6348,22 @@
 		return;
 	}
 
-	if (wpa_s->p2p_auto_join) {
+	if (wpa_s->p2p2 || wpa_s->p2p_auto_join) {
 		int join = wpas_p2p_peer_go(wpa_s,
 					    wpa_s->pending_join_dev_addr);
-		if (join < 0) {
-			wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
-				   "running a GO -> use GO Negotiation");
-			wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
-				       P2P_EVENT_FALLBACK_TO_GO_NEG
-				       "reason=peer-not-running-GO");
+
+		if (wpa_s->p2p2 || join < 0) {
+			if (join < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Peer was not found to be running a GO -> use GO Negotiation");
+				wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
+					       P2P_EVENT_FALLBACK_TO_GO_NEG
+					       "reason=peer-not-running-GO");
+			}
+
+			if (wpa_s->p2p2)
+				wpa_printf(MSG_DEBUG,
+					   "P2P2: Initiate GO negotiation and provisioning using PASN Authentication");
 			wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
 					 wpa_s->p2p_pin, wpa_s->p2p_wps_method,
 					 wpa_s->p2p_persistent_group, 0, 0, 0,
@@ -5555,7 +6380,9 @@
 					 NULL, 0,
 					 is_p2p_allow_6ghz(wpa_s->global->p2p),
 					 wpa_s->p2p2, wpa_s->p2p_bootstrap,
-					 NULL);
+					 wpa_s->pending_join_password[0] ?
+					 wpa_s->pending_join_password : NULL,
+					 false);
 			return;
 		}
 
@@ -5709,7 +6536,7 @@
 {
 	int ret;
 	struct wpa_driver_scan_params params;
-	struct wpabuf *wps_ie, *ies;
+	struct wpabuf *wps_ie = NULL, *ies;
 	size_t ielen;
 	int freqs[2] = { 0, 0 };
 	unsigned int bands;
@@ -5729,13 +6556,16 @@
 		wpa_s->p2p_join_ssid_len = 0;
 	}
 
-	wpa_s->wps->dev.p2p = 1;
-	wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
-					wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
-					NULL);
-	if (wps_ie == NULL) {
-		wpas_p2p_scan_res_join(wpa_s, NULL);
-		return;
+	if (!wpa_s->p2p2) {
+		wpa_s->wps->dev.p2p = 1;
+		wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT,
+						&wpa_s->wps->dev,
+						wpa_s->wps->uuid,
+						WPS_REQ_ENROLLEE, 0, NULL);
+		if (!wps_ie) {
+			wpas_p2p_scan_res_join(wpa_s, NULL);
+			return;
+		}
 	}
 
 	if (!freq) {
@@ -5757,14 +6587,21 @@
 	}
 
 	ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
-	ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
-	if (ies == NULL) {
+
+	if (wps_ie)
+		ielen += wpabuf_len(wps_ie);
+
+	ies = wpabuf_alloc(ielen);
+	if (!ies) {
 		wpabuf_free(wps_ie);
 		wpas_p2p_scan_res_join(wpa_s, NULL);
 		return;
 	}
-	wpabuf_put_buf(ies, wps_ie);
-	wpabuf_free(wps_ie);
+
+	if (wps_ie) {
+		wpabuf_put_buf(ies, wps_ie);
+		wpabuf_free(wps_ie);
+	}
 
 	bands = wpas_get_bands(wpa_s, freqs);
 	p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
@@ -5847,6 +6684,7 @@
 	struct wpa_supplicant *group;
 	struct p2p_go_neg_results res;
 	struct wpa_bss *bss;
+	const u8 *iface_addr = NULL;
 
 	group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
 	if (group == NULL)
@@ -5874,15 +6712,27 @@
 	os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
 	os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
 		  ETH_ALEN);
+	if (!is_zero_ether_addr(wpa_s->pending_join_iface_addr))
+		iface_addr = wpa_s->pending_join_iface_addr;
+
+	if (wpa_s->pending_join_password[0]) {
+		res.akmp = WPA_KEY_MGMT_SAE;
+		os_strlcpy(res.sae_password, wpa_s->pending_join_password,
+			   sizeof(res.sae_password));
+		os_memset(wpa_s->pending_join_password, 0,
+			  sizeof(wpa_s->pending_join_password));
+	}
 	res.wps_method = wpa_s->pending_join_wps_method;
+	res.p2p2 = wpa_s->p2p2;
+	res.cipher = WPA_CIPHER_CCMP;
+
 	if (freq && ssid && ssid_len) {
 		res.freq = freq;
 		res.ssid_len = ssid_len;
 		os_memcpy(res.ssid, ssid, ssid_len);
 	} else {
 		if (ssid && ssid_len) {
-			bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
-					  ssid, ssid_len);
+			bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
 		} else {
 			bss = wpa_bss_get_bssid_latest(
 				wpa_s, wpa_s->pending_join_iface_addr);
@@ -5891,6 +6741,8 @@
 			res.freq = bss->freq;
 			res.ssid_len = bss->ssid_len;
 			os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+			os_memcpy(res.peer_interface_addr, bss->bssid,
+				  ETH_ALEN);
 			wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
 				   bss->freq,
 				   wpa_ssid_txt(bss->ssid, bss->ssid_len));
@@ -5909,7 +6761,10 @@
 		wpa_s->off_channel_freq = 0;
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
-	wpas_start_wps_enrollee(group, &res);
+	if (res.p2p2)
+		wpas_start_gc(group, &res);
+	else
+		wpas_start_wps_enrollee(group, &res);
 
 	/*
 	 * Allow a longer timeout for join-a-running-group than normal 15
@@ -6127,8 +6982,9 @@
  * @allow_6ghz: Allow P2P connection on 6 GHz channels
  * @p2p2: Whether device is in P2P R2 mode
  * @bootstrap: Requested bootstrap method for pairing in P2P2
- * @password: Password for pairing setup or NULL for oppurtunistic method
+ * @password: Password for pairing setup or NULL for opportunistic method
  *	in P2P2
+ * @skip_prov: Connect without provisioning
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *	failure, -2 on failure due to channel not currently available,
  *	-3 if forced channel is not supported
@@ -6141,7 +6997,7 @@
 		     unsigned int vht_chwidth, int he, int edmg,
 		     const u8 *group_ssid, size_t group_ssid_len,
 		     bool allow_6ghz, bool p2p2, u16 bootstrap,
-		     const char *password)
+		     const char *password, bool skip_prov)
 {
 	int force_freq = 0, pref_freq = 0;
 	int ret = 0, res;
@@ -6162,6 +7018,7 @@
 	}
 
 	wpa_s->p2p2 = p2p2;
+	wpa_s->p2p_mode = p2p2 ? WPA_P2P_MODE_WFD_R2 : WPA_P2P_MODE_WFD_R1;
 
 	if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq))
 		return -2;
@@ -6169,6 +7026,7 @@
 	os_free(wpa_s->global->add_psk);
 	wpa_s->global->add_psk = NULL;
 
+	p2p_set_go_role(wpa_s->global->p2p, false);
 	wpa_s->global->p2p_fail_on_wps_complete = 0;
 	wpa_s->global->pending_p2ps_group = 0;
 	wpa_s->global->pending_p2ps_group_freq = 0;
@@ -6212,14 +7070,64 @@
 	} else
 		wpa_s->p2p_pin[0] = '\0';
 
+	if (!password)
+		os_memset(wpa_s->pending_join_password, 0,
+			  sizeof(wpa_s->pending_join_password));
+
 	if (join || auto_join) {
 		u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
 		if (auth) {
+#ifdef CONFIG_PASN
+			struct wpa_supplicant *ifs;
+#endif /* CONFIG_PASN */
+
 			wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
 				   "connect a running group from " MACSTR,
 				   MAC2STR(peer_addr));
 			os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+
+#ifdef CONFIG_PASN
+			if (!wpa_s->p2p2)
+				return ret;
+
+			wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+			if (wpa_s->create_p2p_iface) {
+				if_addr = wpa_s->pending_interface_addr;
+			} else {
+				if (wpa_s->p2p_mgmt)
+					if_addr = wpa_s->parent->own_addr;
+				else
+					if_addr = wpa_s->own_addr;
+				os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+			}
+
+			dl_list_for_each(ifs, &wpa_s->radio->ifaces,
+					 struct wpa_supplicant, radio_list) {
+				if (!ifs->current_ssid ||
+				    ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
+					continue;
+
+				ssid = ifs->current_ssid;
+
+				if (bootstrap == P2P_PBMA_OPPORTUNISTIC &&
+				    wpas_p2p_config_sae_password(wpa_s, ssid)) {
+					ssid = NULL;
+					continue;
+				}
+
+				force_freq = ifs->ap_iface->freq;
+				break;
+			}
+			p2p_set_go_role(wpa_s->global->p2p, true);
+			return wpas_p2p_auth_go_neg(wpa_s, peer_addr,
+						    wps_method, 15, if_addr,
+						    force_freq,
+						    persistent_group, ssid,
+						    pref_freq, bootstrap,
+						    password);
+#else /* CONFIG_PASN */
 			return ret;
+#endif /* CONFIG_PASN */
 		}
 		os_memcpy(dev_addr, peer_addr, ETH_ALEN);
 		if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
@@ -6236,6 +7144,20 @@
 				   wpa_s->p2p_auto_started.usec);
 		}
 		wpa_s->user_initiated_pd = 1;
+		if (password)
+			os_strlcpy(wpa_s->pending_join_password, password,
+				   sizeof(wpa_s->pending_join_password));
+
+		if (skip_prov) {
+			if (!wpa_s->p2p2) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Join without provisioning not supported");
+				return -1;
+			}
+			/* Start join operation immediately */
+			return wpas_p2p_join_start(wpa_s, 0, group_ssid,
+						   group_ssid_len);
+		}
 		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
 				  auto_join, freq,
 				  group_ssid, group_ssid_len) < 0)
@@ -6292,6 +7214,12 @@
 			wpas_p2p_remove_pending_group_interface(wpa_s);
 		return -1;
 	}
+
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p2 && !wpa_s->p2p_pd_before_go_neg)
+		wpas_p2p_initiate_pasn_auth(wpa_s, peer_addr, force_freq);
+#endif /* CONFIG_PASN */
+
 	return ret;
 }
 
@@ -6592,6 +7520,10 @@
 				      const struct p2p_channels *channels,
 				      int freq)
 {
+	if (is_6ghz_freq(freq) &&
+	    !is_p2p_6ghz_capable(wpa_s->global->p2p))
+		return 0;
+
 	if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
 	    p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
 	    freq_included(wpa_s, channels, freq))
@@ -6699,6 +7631,7 @@
 	params->max_oper_chwidth = max_oper_chwidth;
 	params->vht_center_freq2 = vht_center_freq2;
 	params->edmg = edmg;
+	params->p2p2 = wpa_s->p2p2;
 
 	freqs = os_calloc(wpa_s->num_multichan_concurrent,
 			  sizeof(struct wpa_used_freq_data));
@@ -7087,6 +8020,7 @@
  * @vht_chwidth: channel bandwidth for GO operating with VHT support
  * @edmg: Start GO with EDMG support
  * @allow_6ghz: Allow P2P group creation on a 6 GHz channel
+ * @p2p_mode: Operation mode for GO (R1/R2/PCC)
  * Returns: 0 on success, -1 on failure
  *
  * This function creates a new P2P group with the local end as the Group Owner,
@@ -7095,7 +8029,7 @@
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 		       int freq, int vht_center_freq2, int ht40, int vht,
 		       int max_oper_chwidth, int he, int edmg,
-		       bool allow_6ghz)
+		       bool allow_6ghz, bool p2p2, enum wpa_p2p_mode p2p_mode)
 {
 	struct p2p_go_neg_results params;
 	int selected_freq = 0;
@@ -7107,6 +8041,8 @@
 
 	os_free(wpa_s->global->add_psk);
 	wpa_s->global->add_psk = NULL;
+	wpa_s->p2p2 = p2p2;
+	wpa_s->p2p_mode = p2p_mode;
 
 	/* Make sure we are not running find during connection establishment */
 	wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
@@ -7132,7 +8068,8 @@
 		return -1;
 	if (freq > 0)
 		wpa_s->p2p_go_no_pri_sec_switch = 1;
-	wpas_start_wps_go(wpa_s, &params, 0);
+	params.p2p2 = wpa_s->p2p2;
+	wpas_start_go(wpa_s, &params, 0, p2p_mode);
 
 	return 0;
 }
@@ -7141,11 +8078,14 @@
 static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 				 struct wpa_ssid *params, int addr_allocated,
 				 int freq, int force_scan, int retry_limit,
-				 const u8 *go_bssid)
+				 const u8 *go_bssid, bool p2p2, const u8 *pmkid,
+				 const u8 *pmk, size_t pmk_len)
 {
+	struct os_reltime now;
 	struct wpa_ssid *ssid;
 	int other_iface_found = 0;
 	struct wpa_supplicant *ifs;
+	struct rsn_pmksa_cache_entry *entry;
 
 	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
 	if (wpa_s == NULL)
@@ -7199,6 +8139,34 @@
 		os_memcpy(ssid->bssid, go_bssid, ETH_ALEN);
 	}
 
+	if (p2p2) {
+		ssid->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PASN;
+		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+		ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+		ssid->disabled = 0;
+
+		if (pmk && pmk_len && pmkid) {
+			entry = os_zalloc(sizeof(*entry));
+			if (!entry)
+				return -1;
+			os_memcpy(entry->aa, ssid->bssid, ETH_ALEN);
+			os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+			entry->pmk_len = pmk_len;
+			os_memcpy(entry->pmk, pmk, pmk_len);
+			entry->akmp = WPA_KEY_MGMT_SAE;
+			os_get_reltime(&now);
+			entry->expiration = now.sec + 43200;
+			entry->reauth_time = now.sec + 43200 * 70 / 100;
+			entry->network_ctx = ssid;
+			os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
+
+			wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+			ssid->pmk_valid = true;
+		}
+		wpa_s->current_ssid = ssid;
+	}
+
 	wpa_s->show_group_started = 1;
 	wpa_s->p2p_in_invitation = 1;
 	wpa_s->p2p_retry_limit = retry_limit;
@@ -7246,7 +8214,9 @@
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan,
 				  bool allow_6ghz, int retry_limit,
-				  const u8 *go_bssid)
+				  const u8 *go_bssid, const u8 *dev_addr,
+				  const u8 *pmkid, const u8 *pmk,
+				  size_t pmk_len)
 {
 	struct p2p_go_neg_results params;
 	int go = 0, freq;
@@ -7315,7 +8285,8 @@
 		}
 
 		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
-					     force_scan, retry_limit, go_bssid);
+					     force_scan, retry_limit, go_bssid,
+					     wpa_s->p2p2, pmkid, pmk, pmk_len);
 	} else {
 		return -1;
 	}
@@ -7342,6 +8313,15 @@
 	params.ssid_len = ssid->ssid_len;
 	params.persistent_group = 1;
 
+	if (wpa_s->p2p2 && pmk_len && pmk && pmkid) {
+		os_memcpy(params.peer_device_addr, dev_addr, ETH_ALEN);
+		os_memcpy(params.pmkid, pmkid, PMKID_LEN);
+		os_memcpy(params.pmk, pmk, pmk_len);
+		params.pmk_len = pmk_len;
+		params.akmp = WPA_KEY_MGMT_SAE;
+		params.p2p2 = true;
+	}
+
 	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
 	if (wpa_s == NULL)
 		return -1;
@@ -7349,7 +8329,8 @@
 	p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS);
 
 	wpa_s->p2p_first_connection_timeout = connection_timeout;
-	wpas_start_wps_go(wpa_s, &params, 0);
+	params.p2p2 = wpa_s->p2p2;
+	wpas_start_go(wpa_s, &params, 0, wpa_s->p2p_mode);
 
 	return 0;
 }
@@ -7372,6 +8353,12 @@
 		}
 		wpabuf_free(hapd->p2p_probe_resp_ie);
 		hapd->p2p_probe_resp_ie = proberesp_ies;
+
+		if (wpa_s->p2p2) {
+			hapd->iconf->peer_to_peer_twt = true;
+			hapd->iconf->channel_usage = true;
+		}
+
 	} else {
 		wpabuf_free(beacon_ies);
 		wpabuf_free(proberesp_ies);
@@ -7430,6 +8417,7 @@
 	cfg->idle_update = wpas_p2p_idle_update;
 	cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start)
 		!= 0;
+	cfg->p2p2 = wpa_s->p2p2;
 
 	group = p2p_group_init(wpa_s->global->p2p, cfg);
 	if (group == NULL)
@@ -7498,7 +8486,7 @@
 	}
 	if (wpa_s->global->p2p)
 		p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
-	wpas_group_formation_completed(wpa_s, 1, 0);
+	wpas_group_formation_completed(wpa_s, 0, NULL);
 }
 
 
@@ -7547,7 +8535,7 @@
 
 
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-		       const char *config_method,
+		       const char *config_method, u16 bootstrap,
 		       enum wpas_p2p_prov_disc_use use,
 		       struct p2ps_provision *p2ps_prov)
 {
@@ -7569,6 +8557,12 @@
 			   p2ps_prov->status, p2ps_prov->info);
 
 		config_methods = 0;
+	} else if (bootstrap) {
+		wpa_s->p2p2 = true;
+		config_methods = 0;
+		wpa_s->p2p_bootstrap = bootstrap;
+		p2p_set_req_bootstrap_method(wpa_s->global->p2p, peer_addr,
+					     bootstrap);
 	} else if (os_strncmp(config_method, "display", 7) == 0)
 		config_methods = WPS_CONFIG_DISPLAY;
 	else if (os_strncmp(config_method, "keypad", 6) == 0)
@@ -7879,7 +8873,7 @@
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
 		    int vht_center_freq2, int ht40, int vht, int max_chwidth,
-		    int pref_freq, int he, int edmg, bool allow_6ghz)
+		    int pref_freq, int he, int edmg, bool allow_6ghz, bool p2p2)
 {
 	enum p2p_invite_role role;
 	u8 *bssid = NULL;
@@ -7898,6 +8892,22 @@
 	else
 		os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 
+	if (wpa_s->global->p2p && p2p2 && !ssid && peer_addr) {
+		int dik_id;
+
+		dik_id = p2p_get_dik_id(wpa_s->global->p2p, peer_addr);
+		ssid = wpa_config_get_network_with_dik_id(wpa_s->conf, dik_id);
+		if (!ssid) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Could not find SSID for P2P2 peer "
+				   MACSTR, MAC2STR(peer_addr));
+			return -1;
+		}
+	}
+
+	if (!ssid)
+		return -1;
+
 	wpa_s->p2p_persistent_go_freq = freq;
 	wpa_s->p2p_go_ht40 = !!ht40;
 	wpa_s->p2p_go_vht = !!vht;
@@ -7905,6 +8915,7 @@
 	wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
 	wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
 	wpa_s->p2p_go_edmg = !!edmg;
+	wpa_s->p2p2 = p2p2;
 	if (ssid->mode == WPAS_MODE_P2P_GO) {
 		role = P2P_INVITE_ROLE_GO;
 		if (peer_addr == NULL) {
@@ -7927,7 +8938,8 @@
 			bssid = wpa_s->own_addr;
 	} else {
 		role = P2P_INVITE_ROLE_CLIENT;
-		peer_addr = ssid->bssid;
+		if (!wpa_s->p2p2)
+			peer_addr = ssid->bssid;
 	}
 	wpa_s->pending_invite_ssid_id = ssid->id;
 
@@ -7958,9 +8970,24 @@
 	 */
 	wpas_p2p_stop_find_oper(wpa_s);
 
+#ifdef CONFIG_PASN
+	if (p2p2) {
+		if (peer_addr &&
+		    wpas_p2p_initiate_pasn_verify(wpa_s, peer_addr, role, bssid,
+						  ssid->ssid, ssid->ssid_len,
+						  force_freq, go_dev_addr,
+						  pref_freq) < 0) {
+			if (wpa_s->create_p2p_iface)
+				wpas_p2p_remove_pending_group_interface(wpa_s);
+			return -1;
+		}
+		return 0;
+	}
+#endif /* CONFIG_PASN */
+
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
-			  1, pref_freq, -1);
+			  1, pref_freq, -1, false);
 }
 
 
@@ -8044,15 +9071,16 @@
 
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq,
-			  go_dev_addr, persistent, pref_freq, -1);
+			  go_dev_addr, persistent, pref_freq, -1, false);
 }
 
 
 void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	const u8 *bssid;
 	u8 go_dev_addr[ETH_ALEN];
-	int persistent;
+	int persistent, dik_id;
 	int freq;
 	u8 ip[3 * 4], *ip_ptr = NULL;
 	char ip_addr[100];
@@ -8065,6 +9093,11 @@
 	if (!wpa_s->show_group_started || !ssid)
 		return;
 
+	if (wpa_s->go_params)
+		bssid = wpa_s->go_params->peer_interface_addr;
+	else
+		bssid = wpa_s->bssid;
+
 	wpa_s->show_group_started = 0;
 	if (!wpa_s->p2p_go_group_formation_completed &&
 	    wpa_s->global->p2p_group_formation == wpa_s) {
@@ -8111,9 +9144,11 @@
 			       ssid->passphrase, go_dev_addr, persistent,
 			       ip_addr);
 
-	if (persistent)
+	if (persistent) {
+		dik_id = wpas_p2p_store_go_identity(wpa_s, go_dev_addr, bssid);
 		wpas_p2p_store_persistent_group(wpa_s->p2pdev,
-						ssid, go_dev_addr);
+						ssid, go_dev_addr, dik_id);
+	}
 
 	wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr);
 }
@@ -8579,7 +9614,7 @@
 		   "session overlap");
 	if (wpa_s != wpa_s->p2pdev)
 		wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP);
-	wpas_p2p_group_formation_failed(wpa_s, 0);
+	wpas_p2p_group_formation_failed(wpa_s, 0, "WPS PBC session overlap");
 	return 1;
 }
 
@@ -8688,7 +9723,8 @@
 			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 					     wpa_s->p2pdev, NULL);
 			if (wpa_s->p2p_in_provisioning) {
-				wpas_group_formation_completed(wpa_s, 0, 0);
+				wpas_group_formation_completed(wpa_s, 0,
+							       "Canceled");
 				break;
 			}
 			wpas_p2p_group_delete(wpa_s,
@@ -8698,7 +9734,7 @@
 			wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
 				   wpa_s->ifname);
 			found = 1;
-			wpas_p2p_group_formation_failed(wpa_s, 0);
+			wpas_p2p_group_formation_failed(wpa_s, 0, "Canceled");
 			break;
 		}
 	}
@@ -8872,9 +9908,57 @@
 }
 
 
+static int wpas_p2p_store_client_identity(struct wpa_supplicant *wpa_s,
+					  const u8 *addr)
+{
+	u8 cipher;
+	size_t dik_len;
+	const u8 *dik_data;
+	const u8 *pmk, *pmkid;
+	size_t pmk_len;
+	u8 iface_addr[ETH_ALEN];
+	struct hostapd_data *hapd;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (!wpa_s->p2p2 || !wpa_s->ap_iface)
+		return 0;
+
+	hapd = wpa_s->ap_iface->bss[0];
+	if (!hapd)
+		return 0;
+
+	if (p2p_get_dev_identity_key(p2p_wpa_s->global->p2p, addr,
+				     &dik_data, &dik_len, &cipher))
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "P2P: Fetch PMK from client (Device Addr " MACSTR
+		   ")", MAC2STR(addr));
+	if (wpa_auth_pmksa_get_pmk(hapd->wpa_auth, addr, &pmk, &pmk_len,
+				   &pmkid)) {
+		if (p2p_get_interface_addr(p2p_wpa_s->global->p2p, addr,
+					   iface_addr))
+			return 0;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Fetch PMK from client (Interface Addr " MACSTR
+			   ")", MAC2STR(iface_addr));
+		if (wpa_auth_pmksa_get_pmk(hapd->wpa_auth, iface_addr, &pmk,
+					   &pmk_len, &pmkid))
+			return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Storing device identity of client (Device Addr "
+		   MACSTR ")", MAC2STR(addr));
+	return wpas_p2p_store_identity(p2p_wpa_s, cipher, dik_data, dik_len,
+				       pmk, pmk_len, pmkid);
+}
+
+
 void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 				       const u8 *addr)
 {
+	int dik_id;
+
 	if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 				 wpa_s->p2pdev, NULL) > 0) {
 		/*
@@ -8899,7 +9983,7 @@
 			 */
 			if (wpa_s->global->p2p)
 				p2p_wps_success_cb(wpa_s->global->p2p, addr);
-			wpas_group_formation_completed(wpa_s, 1, 0);
+			wpas_group_formation_completed(wpa_s, 0, NULL);
 		}
 	}
 	if (!wpa_s->p2p_go_group_formation_completed) {
@@ -8913,7 +9997,9 @@
 	wpa_s->global->p2p_go_wait_client.sec = 0;
 	if (addr == NULL)
 		return;
-	wpas_p2p_add_persistent_group_client(wpa_s, addr);
+
+	dik_id = wpas_p2p_store_client_identity(wpa_s, addr);
+	wpas_p2p_add_persistent_group_client(wpa_s, addr, dik_id);
 }
 
 
@@ -8942,7 +10028,7 @@
 			 wpa_s->p2p_go_he,
 			 wpa_s->p2p_go_edmg,
 			 NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p),
-			 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL);
+			 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL, false);
 	return ret;
 }
 
@@ -9410,6 +10496,8 @@
 		return NULL;
 	}
 
+	wpa_s->p2p2 = false;
+
 	if (cli_freq == 0) {
 		wsc = wps_build_nfc_handover_req_p2p(
 			wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
@@ -9439,6 +10527,8 @@
 			   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
 		return NULL;
 
+	wpa_s->p2p2 = false;
+
 	if (cli_freq == 0) {
 		wsc = wps_build_nfc_handover_sel_p2p(
 			wpa_s->parent->wps,
@@ -9481,7 +10571,7 @@
 				wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
 				params->go_ssid_len ? params->go_ssid : NULL,
 				params->go_ssid_len, false, wpa_s->p2p2,
-				wpa_s->p2p_bootstrap, NULL);
+				wpa_s->p2p_bootstrap, NULL, false);
 }
 
 
@@ -9545,7 +10635,7 @@
 			  P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
 			  ssid->ssid, ssid->ssid_len, ssid->frequency,
 			  wpa_s->global->p2p_dev_addr, persistent, 0,
-			  wpa_s->p2pdev->p2p_oob_dev_pw_id);
+			  wpa_s->p2pdev->p2p_oob_dev_pw_id, false);
 }
 
 
@@ -9561,7 +10651,7 @@
 				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
 				wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
 				NULL, 0, false, wpa_s->p2p2,
-				wpa_s->p2p_bootstrap, NULL);
+				wpa_s->p2p_bootstrap, NULL, false);
 }
 
 
@@ -9579,7 +10669,7 @@
 			       -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
 			       wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
 			       NULL, 0, false, wpa_s->p2p2,
-			       wpa_s->p2p_bootstrap, NULL);
+			       wpa_s->p2p_bootstrap, NULL, false);
 	if (res)
 		return res;
 
@@ -9707,6 +10797,8 @@
 		  params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
 	wpa_s->p2p_peer_oob_pk_hash_known = 1;
 
+	wpa_s->p2p2 = false;
+
 	if (tag) {
 		if (id < 0x10) {
 			wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
@@ -10449,12 +11541,14 @@
 }
 
 
-struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s)
+struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s,
+				   const char *service_name)
 {
 	struct p2p_data *p2p = wpa_s->global->p2p;
 
 	if (wpa_s->global->p2p_disabled || !p2p)
 		return NULL;
+	p2p_usd_service_hash(p2p, service_name);
 	return p2p_usd_elems(p2p);
 }
 
@@ -10469,3 +11563,77 @@
 		return;
 	p2p_process_usd_elems(p2p, buf, buf_len, peer_addr, freq);
 }
+
+
+#ifdef CONFIG_PASN
+
+int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p)
+		return -2;
+	return p2p_pasn_auth_rx(p2p, mgmt, len, freq);
+}
+
+
+int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+				 size_t data_len, bool acked)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	struct wpa_p2p_pasn_auth_work *awork;
+
+	if (!wpa_s->p2p_pasn_auth_work)
+		return -1;
+	awork = wpa_s->p2p_pasn_auth_work->ctx;
+
+	return p2p_pasn_auth_tx_status(p2p, data, data_len, acked,
+				       awork->verify);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s, const u8 **ptk,
+			  size_t *ptk_len)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p)
+		return -2;
+	return p2p_pasn_get_ptk(p2p, ptk, ptk_len);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#endif /* CONFIG_PASN */
+
+
+int wpas_p2p_get_dira(struct wpa_supplicant *wpa_s, char *buf, size_t buf_len)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p)
+		return 0;
+	return p2p_get_dira_info(p2p, buf, buf_len);
+}
+
+
+int wpas_p2p_validate_dira(struct wpa_supplicant *wpa_s, const u8 *addr,
+			   u8 cipher, const u8 *nonce, const u8 *tag)
+{
+	if (cipher != DIRA_CIPHER_VERSION_128) {
+		wpa_printf(MSG_INFO, "P2P2: Unsupported DIRA cipher version %d",
+			   cipher);
+		return 0;
+	}
+
+	return wpas_validate_dira(wpa_s, addr, nonce, tag);
+}
+
+
+void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s)
+{
+	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
+	p2p_set_dev_addr(wpa_s->global->p2p, wpa_s->own_addr);
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index a0fbddc..c5f2f9c 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -40,12 +40,13 @@
 		     unsigned int vht_chwidth, int he, int edmg,
 		     const u8 *group_ssid, size_t group_ssid_len,
 		     bool allow_6ghz, bool p2p2, u16 bootstrap,
-		     const char *password);
+		     const char *password, bool skip_prov);
 int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
                                           int freq, struct wpa_ssid *ssid);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 		       int freq, int vht_center_freq2, int ht40, int vht,
-		       int max_oper_chwidth, int he, int edmg, bool allow_6ghz);
+		       int max_oper_chwidth, int he, int edmg, bool allow_6ghz,
+		       bool p2p2, enum wpa_p2p_mode p2p_mode);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
 				  int force_freq, int neg_freq,
@@ -54,7 +55,9 @@
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan,
 				  bool allow_6ghz, int retry_limit,
-				  const u8 *go_bssid);
+				  const u8 *go_bssid, const u8 *dev_addr,
+				  const u8 *pmkid, const u8 *pmk,
+				  size_t pmk_len);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 				       struct wpa_ssid *ssid);
 enum wpas_p2p_prov_disc_use {
@@ -64,7 +67,7 @@
 	WPAS_P2P_PD_FOR_ASP
 };
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-		       const char *config_method,
+		       const char *config_method, u16 bootstrap,
 		       enum wpas_p2p_prov_disc_use use,
 		       struct p2ps_provision *p2ps_prov);
 void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
@@ -122,7 +125,8 @@
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
 		    int vht_center_freq2, int ht40, int vht, int max_chwidth,
-		    int pref_freq, int he, int edmg, bool allow_6ghz);
+		    int pref_freq, int he, int edmg, bool allow_6ghz,
+		    bool p2p2);
 int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 			  const u8 *peer_addr, const u8 *go_dev_addr,
 			  bool allow_6ghz);
@@ -182,6 +186,9 @@
 void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf,
 				u16 buf_len, const u8 *peer_addr,
 				unsigned int freq);
+int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+				 size_t data_len, bool acked);
+int wpas_p2p_remove_all_identity(struct wpa_supplicant *wpa_s);
 
 #ifdef CONFIG_P2P
 
@@ -229,7 +236,17 @@
 		      unsigned int count);
 int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s);
 int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s);
-struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s,
+				   const char *service_name);
+void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s);
+int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq);
+int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s, const u8 **ptk,
+			  size_t *ptk_len);
+int wpas_p2p_get_dira(struct wpa_supplicant *wpa_s, char *buf, size_t buf_len);
+int wpas_p2p_validate_dira(struct wpa_supplicant *wpa_s, const u8 *addr,
+			   u8 cipher, const u8 *nonce, const u8 *tag);
 
 #else /* CONFIG_P2P */
 
@@ -356,11 +373,24 @@
 	return 0;
 }
 
-static inline struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s)
+static inline struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s,
+					const char *service_name)
 {
 	return NULL;
 }
 
+static inline void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s)
+{
+}
+
+#ifdef CONFIG_TESTING_OPTIONS
+static inline int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s,
+					const u8 **ptk, size_t *ptk_len)
+{
+	return 0;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #endif /* CONFIG_P2P */
 
 #endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 89edad4..b290e30 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -251,13 +251,13 @@
 #endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_SAE
-	} else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) &&
+	} else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) && ssid &&
 		   (ieee802_11_rsnx_capab(rsnxe,
 					   WLAN_RSNX_CAPAB_SAE_H2E)) &&
 		   (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
 		key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
 		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE (ext key)");
-	} else if ((sel & WPA_KEY_MGMT_SAE) &&
+	} else if ((sel & WPA_KEY_MGMT_SAE) && ssid &&
 		   (ieee802_11_rsnx_capab(rsnxe,
 					   WLAN_RSNX_CAPAB_SAE_H2E)) &&
 		   (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
@@ -583,6 +583,10 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
 	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) &&
+	    ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SPP_A_MSDU))
+		capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
+
 	pasn_set_rsnxe_caps(pasn, capab);
 	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
@@ -806,6 +810,9 @@
 	if (!wpa_s->pasn_auth_work)
 		return -2;
 
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
 	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
 	if (ret == 0) {
diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
index 658103d..9156dae 100644
--- a/wpa_supplicant/robust_av.c
+++ b/wpa_supplicant/robust_av.c
@@ -541,7 +541,7 @@
 	if (wpa_s->connection_eht && eht_ie &&
 	    eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN) {
 		eht = (const struct ieee80211_eht_capabilities *) &eht_ie[3];
-		if (eht->mac_cap & EHT_MACCAP_SCS_TRAFFIC_DESC)
+		if (le_to_host16(eht->mac_cap) & EHT_MACCAP_SCS_TRAFFIC_DESC)
 			allow_scs_traffic_desc = true;
 	}
 
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index f0ab122..ccedcc9 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -750,17 +750,20 @@
 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
 					     sizeof(ext_capab), NULL);
 	if (ext_capab_len > 0 &&
+	    (size_t) ext_capab_len < wpa_s->drv_max_probe_req_ie_len &&
 	    wpabuf_resize(&extra_ie, ext_capab_len) == 0)
 		wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
 
 #ifdef CONFIG_INTERWORKING
 	if (wpa_s->conf->interworking &&
+	    wpa_s->drv_max_probe_req_ie_len >= 2 &&
 	    wpabuf_resize(&extra_ie, 100) == 0)
 		wpas_add_interworking_elements(wpa_s, extra_ie);
 #endif /* CONFIG_INTERWORKING */
 
 #ifdef CONFIG_MBO
-	if (wpa_s->enable_oce & OCE_STA)
+	if ((wpa_s->enable_oce & OCE_STA) &&
+	    wpa_s->drv_max_probe_req_ie_len >= 5)
 		wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie);
 #endif /* CONFIG_MBO */
 
@@ -774,17 +777,19 @@
 						&wpa_s->wps->dev,
 						wpa_s->wps->uuid, req_type,
 						0, NULL);
-		if (wps_ie) {
-			if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
-				wpabuf_put_buf(extra_ie, wps_ie);
-			wpabuf_free(wps_ie);
-		}
+		if (wps_ie &&
+		    wpabuf_len(wps_ie) <= wpa_s->drv_max_probe_req_ie_len &&
+		    wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
+			wpabuf_put_buf(extra_ie, wps_ie);
+		wpabuf_free(wps_ie);
 	}
 
 #ifdef CONFIG_P2P
 	if (wps) {
 		size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
-		if (wpabuf_resize(&extra_ie, ielen) == 0)
+
+		if (ielen <= wpa_s->drv_max_probe_req_ie_len &&
+		    wpabuf_resize(&extra_ie, ielen) == 0)
 			wpas_p2p_scan_ie(wpa_s, extra_ie);
 	}
 #endif /* CONFIG_P2P */
@@ -794,12 +799,14 @@
 #endif /* CONFIG_WPS */
 
 #ifdef CONFIG_HS20
-	if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0)
+	if (wpa_s->conf->hs20 && wpa_s->drv_max_probe_req_ie_len >= 9 &&
+	    wpabuf_resize(&extra_ie, 9) == 0)
 		wpas_hs20_add_indication(extra_ie, -1, 0);
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_FST
 	if (wpa_s->fst_ies &&
+	    wpa_s->drv_max_probe_req_ie_len >= wpabuf_len(wpa_s->fst_ies) &&
 	    wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
 		wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
 #endif /* CONFIG_FST */
@@ -813,7 +820,8 @@
 	if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) {
 		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ];
 
-		if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
+		if (wpa_s->drv_max_probe_req_ie_len >= wpabuf_len(buf) &&
+		    wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
 			wpabuf_put_buf(extra_ie, buf);
 	}
 
@@ -2368,7 +2376,9 @@
 	int wpa_a, wpa_b;
 	int snr_a, snr_b, snr_a_full, snr_b_full;
 	size_t ies_len;
+#ifndef CONFIG_NO_WPA
 	const u8 *rsne_a, *rsne_b;
+#endif /* CONFIG_NO_WPA */
 
 	/* WPA/WPA2 support preferred */
 	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -2412,6 +2422,7 @@
 		snr_b = snr_b_full = wb->level;
 	}
 
+#ifndef CONFIG_NO_WPA
 	/* If SNR of a SAE BSS is good or at least as high as the PSK BSS,
 	 * prefer SAE over PSK for mixed WPA3-Personal transition mode and
 	 * WPA2-Personal deployments */
@@ -2437,6 +2448,7 @@
 		    (snr_b >= GREAT_SNR || snr_b >= snr_a))
 			return 1;
 	}
+#endif /* CONFIG_NO_WPA */
 
 	/* If SNR is close, decide by max rate or frequency band. For cases
 	 * involving the 6 GHz band, use the throughput estimate irrespective
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index e4be388..c6b4242 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -105,6 +105,7 @@
 	int key_mgmt = external ? wpa_s->sme.ext_auth_key_mgmt :
 		wpa_s->key_mgmt;
 	const u8 *addr = mld_addr ? mld_addr : bssid;
+	enum sae_pwe sae_pwe;
 
 	if (ret_use_pt)
 		*ret_use_pt = 0;
@@ -198,14 +199,16 @@
 			rsnxe_capa = rsnxe[2];
 	}
 
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+
 	if (ssid->sae_password_id &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 	if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 	if (bss && is_6ghz_freq(bss->freq) &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 #ifdef CONFIG_SAE_PK
 	if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
@@ -225,14 +228,14 @@
 	}
 #endif /* CONFIG_SAE_PK */
 
-	if (use_pt || wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-	    wpa_s->conf->sae_pwe == SAE_PWE_BOTH) {
+	if (use_pt || sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	    sae_pwe == SAE_PWE_BOTH) {
 		use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
 
-		if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		if ((sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
 		     ssid->sae_password_id ||
 		     wpa_key_mgmt_sae_ext_key(key_mgmt)) &&
-		    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+		    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 		    !use_pt) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Cannot use H2E with the selected AP");
@@ -241,7 +244,7 @@
 	}
 
 	if (use_pt && !ssid->pt)
-		wpa_s_setup_sae_pt(wpa_s->conf, ssid, true);
+		wpa_s_setup_sae_pt(wpa_s, ssid, true);
 	if (use_pt &&
 	    sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
 				  wpa_s->own_addr, addr,
@@ -548,6 +551,28 @@
 }
 
 
+static void sme_add_assoc_req_ie(struct wpa_supplicant *wpa_s,
+				 const struct wpabuf *buf)
+{
+	size_t len;
+	u8 *pos, *end;
+
+	if (!buf)
+		return;
+
+	pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+	end = wpa_s->sme.assoc_req_ie + sizeof(wpa_s->sme.assoc_req_ie);
+	if (pos >= end)
+		return;
+
+	len = wpabuf_len(buf);
+	if (len < (size_t) (end - pos)) {
+		os_memcpy(pos, wpabuf_head(buf), len);
+		wpa_s->sme.assoc_req_ie_len += len;
+	}
+}
+
+
 static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 				    struct wpa_bss *bss, struct wpa_ssid *ssid,
 				    int start)
@@ -716,21 +741,6 @@
 			wpas_connect_work_done(wpa_s);
 			return;
 		}
-#ifdef CONFIG_HS20
-	} else if (wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) &&
-		   (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) {
-		/* No PMKSA caching, but otherwise similar to RSN/WPA */
-		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
-		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
-					      wpa_s->sme.assoc_req_ie,
-					      &wpa_s->sme.assoc_req_ie_len,
-					      false)) {
-			wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
-				"key management and encryption suites");
-			wpas_connect_work_done(wpa_s);
-			return;
-		}
-#endif /* CONFIG_HS20 */
 	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
 		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
 		/*
@@ -755,14 +765,10 @@
 #ifdef CONFIG_WPS
 	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
 		struct wpabuf *wps_ie;
+
+		wpa_s->sme.assoc_req_ie_len = 0;
 		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
-		if (wps_ie && wpabuf_len(wps_ie) <=
-		    sizeof(wpa_s->sme.assoc_req_ie)) {
-			wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie);
-			os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie),
-				  wpa_s->sme.assoc_req_ie_len);
-		} else
-			wpa_s->sme.assoc_req_ie_len = 0;
+		sme_add_assoc_req_ie(wpa_s, wps_ie);
 		wpabuf_free(wps_ie);
 		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 #endif /* CONFIG_WPS */
@@ -810,7 +816,6 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
 			md[0], md[1]);
 
-		omit_rsnxe = !wpa_bss_get_rsnxe(wpa_s, bss, ssid, false);
 		if (wpa_s->sme.assoc_req_ie_len + 5 <
 		    sizeof(wpa_s->sme.assoc_req_ie)) {
 			struct rsn_mdie *mdie;
@@ -830,6 +835,8 @@
 		    wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
 				"over-the-air");
+			omit_rsnxe = !wpa_bss_get_rsnxe(wpa_s, bss, ssid,
+							false);
 			params.auth_alg = WPA_AUTH_ALG_FT;
 			params.ie = wpa_s->sme.ft_ies;
 			params.ie_len = wpa_s->sme.ft_ies_len;
@@ -850,6 +857,8 @@
 		}
 	}
 
+	wpa_s->sme.spp_amsdu = wpa_sm_uses_spp_amsdu(wpa_s->wpa);
+
 #ifdef CONFIG_P2P
 	if (wpa_s->global->p2p) {
 		u8 *pos;
@@ -866,18 +875,7 @@
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_FST
-	if (wpa_s->fst_ies) {
-		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
-
-		if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
-		    sizeof(wpa_s->sme.assoc_req_ie)) {
-			os_memcpy(wpa_s->sme.assoc_req_ie +
-				  wpa_s->sme.assoc_req_ie_len,
-				  wpabuf_head(wpa_s->fst_ies),
-				  fst_ies_len);
-			wpa_s->sme.assoc_req_ie_len += fst_ies_len;
-		}
-	}
+	sme_add_assoc_req_ie(wpa_s, wpa_s->fst_ies);
 #endif /* CONFIG_FST */
 
 	sme_auth_handle_rrm(wpa_s, bss);
@@ -920,15 +918,9 @@
 	}
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (wpa_s->rsnxe_override_assoc &&
-	    wpabuf_len(wpa_s->rsnxe_override_assoc) <=
-	    sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) {
+	if (wpa_s->rsnxe_override_assoc) {
 		wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override");
-		os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
-			  wpabuf_head(wpa_s->rsnxe_override_assoc),
-			  wpabuf_len(wpa_s->rsnxe_override_assoc));
-		wpa_s->sme.assoc_req_ie_len +=
-			wpabuf_len(wpa_s->rsnxe_override_assoc);
+		sme_add_assoc_req_ie(wpa_s, wpa_s->rsnxe_override_assoc);
 	} else
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (wpa_s->rsnxe_len > 0 &&
@@ -940,6 +932,22 @@
 		wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_len;
 	}
 
+	if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION &&
+	    wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_KNOWN_STA_IDENTIFICATION)) {
+		struct wpabuf *e;
+
+		e = wpa_sm_known_sta_identification(
+			wpa_s->wpa,
+			params.mld ? params.ap_mld_addr : bss->bssid,
+			bss->tsf);
+		if (e) {
+			wpa_printf(MSG_DEBUG,
+				   "SME: Add Known STA Identification element");
+			sme_add_assoc_req_ie(wpa_s, e);
+			wpabuf_free(e);
+		}
+	}
+
 #ifdef CONFIG_HS20
 	if (is_hs20_network(wpa_s, ssid, bss)
 #ifndef ANDROID /* Android does not use the native HS 2.0 config */
@@ -951,19 +959,11 @@
 		hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN);
 		if (hs20) {
 			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
-			size_t len;
 
 			wpas_hs20_add_indication(hs20, pps_mo_id,
 						 get_hs20_version(bss));
 			wpas_hs20_add_roam_cons_sel(hs20, ssid);
-			len = sizeof(wpa_s->sme.assoc_req_ie) -
-				wpa_s->sme.assoc_req_ie_len;
-			if (wpabuf_len(hs20) <= len) {
-				os_memcpy(wpa_s->sme.assoc_req_ie +
-					  wpa_s->sme.assoc_req_ie_len,
-					  wpabuf_head(hs20), wpabuf_len(hs20));
-				wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
-			}
+			sme_add_assoc_req_ie(wpa_s, hs20);
 			wpabuf_free(hs20);
 		}
 	}
@@ -989,19 +989,7 @@
 		os_free(wpa_ie);
 	}
 
-	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
-		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
-		size_t len;
-
-		len = sizeof(wpa_s->sme.assoc_req_ie) -
-			wpa_s->sme.assoc_req_ie_len;
-		if (wpabuf_len(buf) <= len) {
-			os_memcpy(wpa_s->sme.assoc_req_ie +
-				  wpa_s->sme.assoc_req_ie_len,
-				  wpabuf_head(buf), wpabuf_len(buf));
-			wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
-		}
-	}
+	sme_add_assoc_req_ie(wpa_s, wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]);
 
 #ifdef CONFIG_MBO
 	mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
@@ -1453,7 +1441,7 @@
 		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
 		    wpa_key_mgmt_sae(ssid->key_mgmt)) {
 			/* Make sure PT is derived */
-			wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
+			wpa_s_setup_sae_pt(wpa_s, ssid, false);
 			wpa_s->sme.ext_auth_wpa_ssid = ssid;
 			break;
 		}
@@ -1729,6 +1717,30 @@
 				return -1;
 			}
 			token_len = elen - 1;
+#ifdef CONFIG_IEEE80211BE
+		} else if ((wpa_s->valid_links ||
+			    (external && wpa_s->sme.ext_ml_auth)) &&
+			   token_len > 12 &&
+			   token_pos[token_len - 12] == WLAN_EID_EXTENSION &&
+			   token_pos[token_len - 11] == 10 &&
+			   token_pos[token_len - 10] ==
+			   WLAN_EID_EXT_MULTI_LINK) {
+			/* IEEE P802.11be requires H2E to be used whenever SAE
+			 * is used for ML association. However, some early
+			 * Wi-Fi 7 APs enable MLO without H2E. Recognize this
+			 * special case based on the fixed length Basic
+			 * Multi-Link element being at the end of the data that
+			 * would contain the unknown variable length
+			 * Anti-Clogging Token field. The Basic Multi-Link
+			 * element in Authentication frames include the MLD MAC
+			 * addreess in the Common Info field and all subfields
+			 * of the Presence Bitmap subfield of the Multi-Link
+			 * Control field of the element zero and consequently,
+			 * has a fixed length of 12 octets. */
+			wpa_printf(MSG_DEBUG,
+				   "SME: Detected Basic Multi-Link element at the end of Anti-Clogging Token field");
+			token_len -= 12;
+#endif /* CONFIG_IEEE80211BE */
 		}
 
 		*ie_offset = token_pos + token_len - data;
@@ -2451,6 +2463,12 @@
 mscs_fail:
 #endif /* CONFIG_NO_ROBUST_AV */
 
+	wpa_s->sme.assoc_req_ie_len =
+		wpas_populate_wfa_capa(wpa_s, wpa_s->current_bss,
+				       wpa_s->sme.assoc_req_ie,
+				       wpa_s->sme.assoc_req_ie_len,
+				       sizeof(wpa_s->sme.assoc_req_ie));
+
 	if (ssid && ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
 		struct multi_ap_params multi_ap = { 0 };
@@ -2472,10 +2490,10 @@
 	}
 
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE_SUPPORT,
-			 wpas_rsn_overriding(wpa_s));
+			 wpas_rsn_overriding(wpa_s, ssid));
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
 			 RSN_OVERRIDE_NOT_USED);
-	if (wpas_rsn_overriding(wpa_s) &&
+	if (wpas_rsn_overriding(wpa_s, ssid) &&
 	    wpas_ap_supports_rsn_overriding(wpa_s, wpa_s->current_bss) &&
 	    wpa_s->sme.assoc_req_ie_len + 2 + 4 <=
 	    sizeof(wpa_s->sme.assoc_req_ie)) {
@@ -2630,6 +2648,7 @@
 #endif /* CONFIG_IEEE80211R */
 	params.mode = mode;
 	params.mgmt_frame_protection = wpa_s->sme.mfp;
+	params.spp_amsdu = wpa_s->sme.spp_amsdu;
 	params.rrm_used = wpa_s->rrm.rrm_used;
 	if (wpa_s->sme.prev_bssid_set)
 		params.prev_bssid = wpa_s->sme.prev_bssid;
@@ -2655,10 +2674,6 @@
 		params.wpa_proto = WPA_PROTO_WPA;
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
 					elems.wpa_ie_len + 2);
-	} else if (elems.osen) {
-		params.wpa_proto = WPA_PROTO_OSEN;
-		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2,
-					elems.osen_len + 2);
 	} else
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 	if (elems.rsnxe)
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 3bb621d..0ae27a7 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -614,38 +614,37 @@
 }
 
 
-static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
-{
-	unsigned int i;
-
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
-		wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
-}
-
-#ifdef CONFIG_MBO
-static struct wpa_bss *
-get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
+static void
+fetch_drv_mbo_candidate_info(struct wpa_supplicant *wpa_s,
 			     enum mbo_transition_reject_reason *reason)
 {
-	struct wpa_bss *target = NULL;
+#ifdef CONFIG_MBO
 	struct wpa_bss_trans_info params;
 	struct wpa_bss_candidate_info *info = NULL;
-	struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
-	u8 *first_candidate_bssid = NULL, *pos;
+	struct neighbor_report *nei;
+	u8 *pos;
 	unsigned int i;
 
+	if (!wpa_s->wnm_mbo_trans_reason_present)
+		return;
+
 	params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
 	params.n_candidates = 0;
 	params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
 	if (!params.bssid)
-		return NULL;
+		return;
 
 	pos = params.bssid;
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
-		if (nei->is_first)
-			first_candidate_bssid = nei->bssid;
-		if (!nei->acceptable)
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+		nei->drv_mbo_reject = 0;
+
+		if (nei->preference_present && nei->preference == 0)
 			continue;
+
+		/* Should we query BSSIDs that we reject for other reasons? */
+
 		os_memcpy(pos, nei->bssid, ETH_ALEN);
 		pos += ETH_ALEN;
 		params.n_candidates++;
@@ -655,185 +654,36 @@
 		goto end;
 
 	info = wpa_drv_get_bss_trans_status(wpa_s, &params);
-	if (!info) {
-		/* If failed to get candidate BSS transition status from driver,
-		 * get the first acceptable candidate from wpa_supplicant.
-		 */
-		target = wpa_bss_get_bssid(wpa_s, params.bssid);
+	if (!info)
 		goto end;
-	}
 
-	/* Get the first acceptable candidate from driver */
 	for (i = 0; i < info->num; i++) {
-		if (info->candidates[i].is_accept) {
-			target = wpa_bss_get_bssid(wpa_s,
-						   info->candidates[i].bssid);
-			goto end;
-		}
-	}
+		int j;
 
-	/* If Disassociation Imminent is set and driver rejects all the
-	 * candidate select first acceptable candidate which has
-	 * rssi > disassoc_imminent_rssi_threshold
-	 */
-	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
-		for (i = 0; i < info->num; i++) {
-			target = wpa_bss_get_bssid(wpa_s,
-						   info->candidates[i].bssid);
-			if (target &&
-			    (target->level <
-			     wpa_s->conf->disassoc_imminent_rssi_threshold))
+		for (j = 0; j < wpa_s->wnm_num_neighbor_report; j++) {
+			nei = &wpa_s->wnm_neighbor_report_elements[j];
+
+			if (!ether_addr_equal(info->candidates[i].bssid,
+					      nei->bssid))
 				continue;
-			goto end;
-		}
-	}
 
-	/* While sending BTM reject use reason code of the first candidate
-	 * received in BTM request frame
-	 */
-	if (reason) {
-		for (i = 0; i < info->num; i++) {
-			if (first_candidate_bssid &&
-			    ether_addr_equal(first_candidate_bssid,
-					     info->candidates[i].bssid)) {
+			nei->drv_mbo_reject = !info->candidates[i].is_accept;
+
+			/* Use the reject reason from the first candidate */
+			if (i == 0 && nei->drv_mbo_reject)
 				*reason = info->candidates[i].reject_reason;
-				break;
-			}
+
+			break;
 		}
 	}
 
-	target = NULL;
-
 end:
 	os_free(params.bssid);
 	if (info) {
 		os_free(info->candidates);
 		os_free(info);
 	}
-	return target;
-}
 #endif /* CONFIG_MBO */
-
-
-static struct wpa_bss * find_better_target(struct wpa_bss *a,
-					   struct wpa_bss *b)
-{
-	if (!a)
-		return b;
-	if (!b)
-		return a;
-
-	if (a->est_throughput > b->est_throughput) {
-		wpa_printf(MSG_DEBUG, "WNM: A is better: " MACSTR
-			   " est-tput: %d  B: " MACSTR " est-tput: %d",
-			   MAC2STR(a->bssid), a->est_throughput,
-			   MAC2STR(b->bssid), b->est_throughput);
-		return a;
-	}
-
-	wpa_printf(MSG_DEBUG, "WNM: B is better, A: " MACSTR
-		   " est-tput: %d  B: " MACSTR " est-tput: %d",
-		   MAC2STR(a->bssid), a->est_throughput,
-		   MAC2STR(b->bssid), b->est_throughput);
-	return b;
-}
-
-static struct wpa_bss *
-compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
-			      enum mbo_transition_reject_reason *reason)
-{
-	u8 i;
-	struct wpa_bss *bss = wpa_s->current_bss;
-	struct wpa_bss *target;
-	struct wpa_bss *best_target = NULL;
-	struct wpa_bss *bss_in_list = NULL;
-
-	if (!bss)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
-		   MAC2STR(wpa_s->bssid), bss->level);
-
-	wnm_clear_acceptable(wpa_s);
-
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
-		struct neighbor_report *nei;
-
-		nei = &wpa_s->wnm_neighbor_report_elements[i];
-
-		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
-		if (!target) {
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) not found in scan results",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1);
-			continue;
-		}
-
-		/*
-		 * TODO: Could consider allowing transition to another ESS if
-		 * PMF was enabled for the association.
-		 */
-		if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
-					1, 0)) {
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) does not match the current network profile",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1);
-			continue;
-		}
-
-		if (target->level < bss->level && target->level < -80) {
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) does not have sufficient signal level (%d)",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1,
-				   target->level);
-			continue;
-		}
-
-		nei->acceptable = 1;
-
-		best_target = find_better_target(target, best_target);
-		if (target == bss)
-			bss_in_list = bss;
-	}
-
-#ifdef CONFIG_MBO
-	if (wpa_s->wnm_mbo_trans_reason_present)
-		target = get_mbo_transition_candidate(wpa_s, reason);
-	else
-		target = best_target;
-#else /* CONFIG_MBO */
-	target = best_target;
-#endif /* CONFIG_MBO */
-
-	if (!target)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG,
-		   "WNM: Found an acceptable preferred transition candidate BSS "
-		   MACSTR " (RSSI %d, tput: %d  bss-tput: %d)",
-		   MAC2STR(target->bssid), target->level,
-		   target->est_throughput, bss->est_throughput);
-
-	if (!bss_in_list)
-		return target;
-
-	if ((!target->est_throughput && !bss_in_list->est_throughput) ||
-	    (target->est_throughput > bss_in_list->est_throughput &&
-	     target->est_throughput - bss_in_list->est_throughput >
-	     bss_in_list->est_throughput >> 4)) {
-		/* It is more than 100/16 percent better, so switch. */
-		return target;
-	}
-
-	wpa_printf(MSG_DEBUG,
-		   "WNM: Stay with our current BSS, not enough change in estimated throughput to switch");
-	return bss_in_list;
 }
 
 
@@ -997,7 +847,7 @@
 		struct wpa_bss *bss = wpa_s->last_scan_res[i];
 		int res;
 
-		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
+		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0, false)) {
 			res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
 			if (res == -2)
 				continue; /* could not build entry for BSS */
@@ -1154,13 +1004,14 @@
 
 int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
 {
-	struct wpa_bss *bss;
+	struct wpa_bss *bss, *current_bss = wpa_s->current_bss;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 	enum mbo_transition_reject_reason reason =
 		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+	struct wpa_ssid *selected_ssid = NULL;
 
-	if (!wpa_s->wnm_dialog_token)
+	if (!ssid || !wpa_s->wnm_dialog_token)
 		return 0;
 
 	wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1178,8 +1029,45 @@
 
 	wpa_s->wnm_transition_scan = false;
 
+	/* Fetch MBO transition candidate rejection information from driver */
+	fetch_drv_mbo_candidate_info(wpa_s, &reason);
+
 	/* Compare the Neighbor Report and scan results */
-	bss = compare_scan_neighbor_results(wpa_s, &reason);
+	bss = wpa_supplicant_select_bss(wpa_s, ssid, &selected_ssid, 1);
+#ifdef CONFIG_MBO
+	if (!bss && wpa_s->wnm_mbo_trans_reason_present &&
+	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)) {
+		int i;
+		bool changed = false;
+
+		/*
+		 * We didn't find any candidate, the driver had a say about
+		 * which targets to reject and disassociation is immiment.
+		 *
+		 * We should still try to roam, so retry after ignoring the
+		 * driver reject for any BSS that has an RSSI better than
+		 * disassoc_imminent_rssi_threshold.
+		 */
+		for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+			struct neighbor_report *nei;
+
+			nei = &wpa_s->wnm_neighbor_report_elements[i];
+			bss = wpa_bss_get_bssid(wpa_s, nei->bssid);
+			if (bss && bss->level >
+			    wpa_s->conf->disassoc_imminent_rssi_threshold) {
+				nei->drv_mbo_reject = 0;
+				changed = true;
+			}
+		}
+
+		if (changed) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Ignore driver rejection due to imminent disassociation and acceptable RSSI");
+			bss = wpa_supplicant_select_bss(wpa_s, ssid,
+							&selected_ssid, 1);
+		}
+	}
+#endif /* CONFIG_MBO */
 
 	/*
 	 * If this is a pre-scan check, returning 0 will trigger a scan and
@@ -1202,20 +1090,35 @@
 			return 0;
 
 #ifndef CONFIG_NO_ROAMING
-		if (wpa_s->current_bss && bss != wpa_s->current_bss &&
-		    wpa_supplicant_need_to_roam_within_ess(wpa_s,
-							   wpa_s->current_bss,
-							   bss))
+		if (current_bss && bss != current_bss &&
+		    wpa_supplicant_need_to_roam_within_ess(wpa_s, bss,
+							   current_bss, false))
 			return 0;
 #endif /* CONFIG_NO_ROAMING */
 	}
 
+#ifndef CONFIG_NO_ROAMING
+	/* Apply normal roaming rules if we can stay with the current BSS */
+	if (current_bss && bss != current_bss &&
+	    wpa_scan_res_match(wpa_s, 0, current_bss, wpa_s->current_ssid,
+			       1, 0, false) &&
+	    !wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, bss,
+						    true))
+		bss = current_bss;
+#endif /* CONFIG_NO_ROAMING */
+
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
 		goto send_bss_resp_fail;
 	}
 
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Found an acceptable preferred transition candidate BSS "
+		   MACSTR " (RSSI %d, tput: %d  bss-tput: %d)",
+		   MAC2STR(bss->bssid), bss->level, bss->est_throughput,
+		   current_bss ? (int) current_bss->est_throughput : -1);
+
 	/* Associate to the network */
 	wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
 	return 1;
@@ -1409,15 +1312,6 @@
 				*num_valid_candidates += 1;
 
 			wpa_s->wnm_num_neighbor_report++;
-#ifdef CONFIG_MBO
-			if (wpa_s->wnm_mbo_trans_reason_present &&
-			    wpa_s->wnm_num_neighbor_report == 1) {
-				rep->is_first = 1;
-				wpa_printf(MSG_DEBUG,
-					   "WNM: First transition candidate is "
-					   MACSTR, MAC2STR(rep->bssid));
-			}
-#endif /* CONFIG_MBO */
 		}
 
 		pos += len;
@@ -1806,45 +1700,6 @@
 			   WPA_GET_BE24(pos), pos[3]);
 
 #ifdef CONFIG_HS20
-		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
-		    WPA_GET_BE24(pos) == OUI_WFA &&
-		    pos[3] == HS20_WNM_SUB_REM_NEEDED) {
-			/* Subscription Remediation subelement */
-			const u8 *ie_end;
-			u8 url_len;
-			char *url;
-			u8 osu_method;
-
-			wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
-				   "subelement");
-			ie_end = pos + ie_len;
-			pos += 4;
-			url_len = *pos++;
-			if (url_len == 0) {
-				wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
-				url = NULL;
-				osu_method = 1;
-			} else {
-				if (url_len + 1 > ie_end - pos) {
-					wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
-						   url_len,
-						   (int) (ie_end - pos));
-					break;
-				}
-				url = os_malloc(url_len + 1);
-				if (url == NULL)
-					break;
-				os_memcpy(url, pos, url_len);
-				url[url_len] = '\0';
-				osu_method = pos[url_len];
-			}
-			hs20_rx_subscription_remediation(wpa_s, url,
-							 osu_method);
-			os_free(url);
-			pos = next;
-			continue;
-		}
-
 		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
 		    WPA_GET_BE24(pos) == OUI_WFA &&
 		    pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
@@ -2133,6 +1988,11 @@
 		if (nei->preference_present && nei->preference == 0)
 			return true;
 
+#ifdef CONFIG_MBO
+		if (nei->drv_mbo_reject)
+			return true;
+#endif /* CONFIG_MBO */
+
 		break;
 	}
 
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 235a838..80928f7 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -44,9 +44,8 @@
 	unsigned int rm_capab_present:1;
 	unsigned int bearing_present:1;
 	unsigned int bss_term_present:1;
-	unsigned int acceptable:1;
 #ifdef CONFIG_MBO
-	unsigned int is_first:1;
+	unsigned int drv_mbo_reject:1;
 #endif /* CONFIG_MBO */
 	struct measurement_pilot *meas_pilot;
 	struct multiple_bssid *mul_bssid;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index af00e79..2ab2917 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -507,7 +507,7 @@
 		"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
 		"scan_cur_freq", "scan_res_valid_for_connect",
 		"sched_scan_interval",
-		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"tdls_external_control", "wowlan_triggers",
 		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
 		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
 		"reassoc_same_bss_optim", "wps_priority",
@@ -614,7 +614,7 @@
 		"scan_cur_freq", "scan_res_valid_for_connect",
 		"sched_scan_interval",
 		"sched_scan_start_delay",
-		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"tdls_external_control", "wowlan_triggers",
 		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
 		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
 		"reassoc_same_bss_optim", "extended_key_id"
@@ -2778,37 +2778,6 @@
 	return wpa_ctrl_command(ctrl, cmd);
 }
 
-
-static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc,
-					 char *argv[])
-{
-	char cmd[512];
-
-	if (argc < 2) {
-		printf("Command needs two arguments (dst mac addr and "
-		       "icon name)\n");
-		return -1;
-	}
-
-	if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0)
-		return -1;
-
-	return wpa_ctrl_command(ctrl, cmd);
-}
-
-
-static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[])
-{
-	return wpa_ctrl_command(ctrl, "FETCH_OSU");
-}
-
-
-static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc,
-					char *argv[])
-{
-	return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU");
-}
-
 #endif /* CONFIG_HS20 */
 
 
@@ -3335,6 +3304,59 @@
 }
 
 
+#ifdef CONFIG_NAN_USD
+
+static int wpa_cli_cmd_nan_publish(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_PUBLISH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_cancel_publish(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_CANCEL_PUBLISH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_update_publish(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_UPDATE_PUBLISH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_subscribe(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_SUBSCRIBE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_cancel_subscribe(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_CANCEL_SUBSCRIBE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_transmit(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_TRANSMIT", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_flush(struct wpa_ctrl *ctrl, int argc,
+				 char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "NAN_FLUSH");
+}
+
+#endif /* CONFIG_NAN_USD */
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -3869,14 +3891,6 @@
 	{ "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
 	  wpa_cli_complete_bss, cli_cmd_flag_none,
 	  "<addr> <home realm> = get HS20 nai home realm list" },
-	{ "hs20_icon_request", wpa_cli_cmd_hs20_icon_request,
-	  wpa_cli_complete_bss, cli_cmd_flag_none,
-	  "<addr> <icon name> = get Hotspot 2.0 OSU icon" },
-	{ "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none,
-	  "= fetch OSU provider information from all APs" },
-	{ "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL,
-	  cli_cmd_flag_none,
-	  "= cancel fetch_osu command" },
 #endif /* CONFIG_HS20 */
 	{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
 	  cli_cmd_flag_none,
@@ -4084,6 +4098,24 @@
 	{ "mlo_signal_poll", wpa_cli_cmd_mlo_signal_poll, NULL,
 	  cli_cmd_flag_none,
 	  "= get mlo signal parameters" },
+#ifdef CONFIG_NAN_USD
+	{ "nan_publish", wpa_cli_cmd_nan_publish, NULL,
+	  cli_cmd_flag_none,
+	  "service_name=<name> [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [freq_list=<comma separate list of MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>] [solicited=0] [unsolicited=0] [fsd=0] [p2p=1] = Publish NAN service" },
+	{ "nan_cancel_publish", wpa_cli_cmd_nan_cancel_publish, NULL,
+	  cli_cmd_flag_none, "publish_id=<id from NAN_PUBLISH> = Cancel NAN USD publish instance" },
+	{ "nan_update_publish", wpa_cli_cmd_nan_update_publish, NULL,
+	  cli_cmd_flag_none, "publish_id=<id from NAN_PUBLISH> [ssi=<service specific information (hexdump)> = Update publish" },
+	{ "nan_subscribe", wpa_cli_cmd_nan_subscribe, NULL,
+	  cli_cmd_flag_none,
+	  "service_name=<name> [active=1] [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>] [p2p=1] = Subscribe to NAN service" },
+	{ "nan_cancel_subscribe", wpa_cli_cmd_nan_cancel_subscribe, NULL,
+	  cli_cmd_flag_none, "subscribe_id=<id from NAN_PUBLISH> = Cancel NAN USD subscribe instance" },
+	{ "nan_transmit", wpa_cli_cmd_nan_transmit, NULL,
+	  cli_cmd_flag_none, "handle=<id from NAN_PUBLISH or NAN_SUBSCRIBE> req_instance_id=<peer's id> address=<peer's MAC address> [ssi=<service specific information (hexdump)>] = Transmit NAN follow up" },
+	{ "nan_flush", wpa_cli_cmd_nan_flush, NULL,
+	  cli_cmd_flag_none, "= Flush all NAN USD services" },
+#endif /* CONFIG_NAN_USD */
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -4424,8 +4456,6 @@
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, ESS_DISASSOC_IMMINENT)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_starts(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
-		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, HS20_T_C_ACCEPTANCE)) {
diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.h b/wpa_supplicant/wpa_gui-qt4/eventhistory.h
index afd7b63..2122ab4 100644
--- a/wpa_supplicant/wpa_gui-qt4/eventhistory.h
+++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.h
@@ -40,7 +40,7 @@
 
 public:
 	EventHistory(QWidget *parent = 0, const char *name = 0,
-		     bool modal = false, Qt::WindowFlags fl = 0);
+		     bool modal = false, Qt::WindowFlags fl = Qt::Widget);
 	~EventHistory();
 
 public slots:
diff --git a/wpa_supplicant/wpa_gui-qt4/main.cpp b/wpa_supplicant/wpa_gui-qt4/main.cpp
index bbd45c6..d395aa1 100644
--- a/wpa_supplicant/wpa_gui-qt4/main.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/main.cpp
@@ -40,10 +40,10 @@
 	int ret;
 
 	locale = QLocale::system().name();
-	resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
-	if (!translator.load("wpa_gui_" + locale, resourceDir))
-		translator.load("wpa_gui_" + locale, "lang");
-	app.installTranslator(&translator);
+	resourceDir = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
+	if (translator.load("wpa_gui_" + locale, resourceDir) ||
+	    translator.load("wpa_gui_" + locale, "lang"))
+		app.installTranslator(&translator);
 
 	WpaGui w(&app);
 
diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
index 2727318..59af845 100644
--- a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
@@ -37,7 +37,7 @@
 		SLOT(authChanged(int)));
 	connect(cancelButton, SIGNAL(clicked()), this, SLOT(close()));
 	connect(addButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
-	connect(encrSelect, SIGNAL(activated(const QString &)), this,
+	connect(encrSelect, SIGNAL(textActivated(const QString &)), this,
 		SLOT(encrChanged(const QString &)));
 	connect(removeButton, SIGNAL(clicked()), this, SLOT(removeNetwork()));
 	connect(eapSelect, SIGNAL(activated(int)), this,
@@ -204,8 +204,8 @@
 	}
 
 	if (idstrEdit->isEnabled() && !idstrEdit->text().isEmpty()) {
-		QRegExp rx("^(\\w|-)+$");
-		if (rx.indexIn(idstrEdit->text()) < 0) {
+		QRegularExpression rx("^(\\w|-)+$");
+		if (!rx.match(idstrEdit->text()).hasMatch()) {
 			QMessageBox::warning(
 				this, tr("Network ID Error"),
 				tr("Network ID String contains non-word "
@@ -797,7 +797,7 @@
 		    tr("This will permanently remove the network\n"
 		       "from the configuration. Do you really want\n"
 		       "to remove this network?"),
-		    tr("Yes"), tr("No")) != 0)
+		    QMessageBox::Yes, QMessageBox::No) != 0)
 		return;
 
 	snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id);
diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.h b/wpa_supplicant/wpa_gui-qt4/networkconfig.h
index fd09dec..a3a7d97 100644
--- a/wpa_supplicant/wpa_gui-qt4/networkconfig.h
+++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.h
@@ -20,7 +20,7 @@
 
 public:
 	NetworkConfig(QWidget *parent = 0, const char *name = 0,
-		      bool modal = false, Qt::WindowFlags fl = 0);
+		      bool modal = false, Qt::WindowFlags fl = Qt::Widget);
 	~NetworkConfig();
 
 	virtual void paramsFromScanResults(QTreeWidgetItem *sel);
diff --git a/wpa_supplicant/wpa_gui-qt4/peers.cpp b/wpa_supplicant/wpa_gui-qt4/peers.cpp
index 0a0b3ff..268aba8 100644
--- a/wpa_supplicant/wpa_gui-qt4/peers.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/peers.cpp
@@ -403,7 +403,7 @@
 
 void Peers::add_station(QString info)
 {
-	QStringList lines = info.split(QRegExp("\\n"));
+	QStringList lines = info.split(QRegularExpression("\\n"));
 	QString name;
 
 	for (QStringList::Iterator it = lines.begin();
@@ -518,7 +518,7 @@
 	 */
 
 	QStringList items =
-		params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+		params.split(QRegularExpression(" (?=[^']*('[^']*'[^']*)*$)"));
 	QString addr = "";
 	QString name = "";
 	int config_methods = 0;
@@ -591,7 +591,7 @@
 	QString ssid, bssid, flags, wps_name, pri_dev_type;
 	int id = -1;
 
-	QStringList lines = bss.split(QRegExp("\\n"));
+	QStringList lines = bss.split(QRegularExpression("\\n"));
 	for (QStringList::Iterator it = lines.begin();
 	     it != lines.end(); it++) {
 		int pos = (*it).indexOf('=') + 1;
@@ -643,7 +643,7 @@
 			item->setData(ssid, peer_role_ssid);
 		model.appendRow(item);
 
-		lines = bss.split(QRegExp("\\n"));
+		lines = bss.split(QRegularExpression("\\n"));
 		for (QStringList::Iterator it = lines.begin();
 		     it != lines.end(); it++) {
 			if ((*it).startsWith("p2p_group_client:"))
@@ -903,7 +903,7 @@
 		 * group_capab=0x0
 		 */
 		QStringList items =
-			text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+			text.split(QRegularExpression(" (?=[^']*('[^']*'[^']*)*$)"));
 		QString addr = items[1];
 		QString name = "";
 		QString pri_dev_type;
diff --git a/wpa_supplicant/wpa_gui-qt4/peers.h b/wpa_supplicant/wpa_gui-qt4/peers.h
index bb73737..c44bba9 100644
--- a/wpa_supplicant/wpa_gui-qt4/peers.h
+++ b/wpa_supplicant/wpa_gui-qt4/peers.h
@@ -22,7 +22,7 @@
 
 public:
 	Peers(QWidget *parent = 0, const char *name = 0,
-		    bool modal = false, Qt::WindowFlags fl = 0);
+		    bool modal = false, Qt::WindowFlags fl = Qt::Widget);
 	~Peers();
 	void setWpaGui(WpaGui *_wpagui);
 	void event_notify(WpaMsg msg);
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
index a2e3072..ba04b4f 100644
--- a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
@@ -77,7 +77,7 @@
 
 		QString ssid, bssid, freq, signal, flags;
 
-		QStringList lines = bss.split(QRegExp("\\n"));
+		QStringList lines = bss.split(QRegularExpression("\\n"));
 		for (QStringList::Iterator it = lines.begin();
 		     it != lines.end(); it++) {
 			int pos = (*it).indexOf('=') + 1;
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.h b/wpa_supplicant/wpa_gui-qt4/scanresults.h
index 2cddd13..39bba90 100644
--- a/wpa_supplicant/wpa_gui-qt4/scanresults.h
+++ b/wpa_supplicant/wpa_gui-qt4/scanresults.h
@@ -20,7 +20,7 @@
 
 public:
 	ScanResults(QWidget *parent = 0, const char *name = 0,
-		    bool modal = false, Qt::WindowFlags fl = 0);
+		    bool modal = false, Qt::WindowFlags fl = Qt::Widget);
 	~ScanResults();
 
 public slots:
diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
index b6d1ad2..3f7dccb 100644
--- a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
+++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
@@ -20,7 +20,7 @@
 
 public:
 	UserDataRequest(QWidget *parent = 0, const char *name = 0,
-			bool modal = false, Qt::WindowFlags fl = 0);
+			bool modal = false, Qt::WindowFlags fl = Qt::Widget);
 	~UserDataRequest();
 
 	int setParams(WpaGui *_wpagui, const char *reqMsg);
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
index 9404ab4..0c125d9 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -99,9 +99,9 @@
 	connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect()));
 	connect(scanButton, SIGNAL(clicked()), this, SLOT(scan()));
 	connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB()));
-	connect(adapterSelect, SIGNAL(activated(const QString&)), this,
+	connect(adapterSelect, SIGNAL(textActivated(const QString&)), this,
 		SLOT(selectAdapter(const QString&)));
-	connect(networkSelect, SIGNAL(activated(const QString&)), this,
+	connect(networkSelect, SIGNAL(textActivated(const QString&)), this,
 		SLOT(selectNetwork(const QString&)));
 	connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
 	connect(editNetworkButton, SIGNAL(clicked()), this,
@@ -1078,7 +1078,7 @@
 	char reply[10];
 	size_t reply_len = sizeof(reply);
 
-	if (cmd.contains(QRegExp("^\\d+:")))
+	if (cmd.contains(QRegularExpression("^\\d+:")))
 		cmd.truncate(cmd.indexOf(':'));
 	else
 		cmd = "any";
@@ -1095,7 +1095,7 @@
 	char reply[10];
 	size_t reply_len = sizeof(reply);
 
-	if (cmd.contains(QRegExp("^\\d+:")))
+	if (cmd.contains(QRegularExpression("^\\d+:")))
 		cmd.truncate(cmd.indexOf(':'));
 	else if (!cmd.startsWith("all")) {
 		debug("Invalid editNetwork '%s'",
@@ -1114,7 +1114,7 @@
 	char reply[10];
 	size_t reply_len = sizeof(reply);
 
-	if (cmd.contains(QRegExp("^\\d+:")))
+	if (cmd.contains(QRegularExpression("^\\d+:")))
 		cmd.truncate(cmd.indexOf(':'));
 	else if (!cmd.startsWith("all")) {
 		debug("Invalid editNetwork '%s'",
@@ -1132,7 +1132,7 @@
 	QString cmd(sel);
 	int id = -1;
 
-	if (cmd.contains(QRegExp("^\\d+:"))) {
+	if (cmd.contains(QRegularExpression("^\\d+:"))) {
 		cmd.truncate(cmd.indexOf(':'));
 		id = cmd.toInt();
 	}
@@ -1204,7 +1204,7 @@
 	char reply[10];
 	size_t reply_len = sizeof(reply);
 
-	if (cmd.contains(QRegExp("^\\d+:")))
+	if (cmd.contains(QRegularExpression("^\\d+:")))
 		cmd.truncate(cmd.indexOf(':'));
 	else if (!cmd.startsWith("all")) {
 		debug("Invalid editNetwork '%s'",
@@ -1476,7 +1476,7 @@
 
 	QString msg, status(buf);
 
-	QStringList lines = status.split(QRegExp("\\n"));
+	QStringList lines = status.split(QRegularExpression("\\n"));
 	for (QStringList::Iterator it = lines.begin();
 	     it != lines.end(); it++) {
 		int pos = (*it).indexOf('=') + 1;
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.h b/wpa_supplicant/wpa_gui-qt4/wpagui.h
index f0a34c9..898722b 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.h
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.h
@@ -49,7 +49,7 @@
 	};
 
 	WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0,
-	       Qt::WindowFlags fl = 0);
+	       Qt::WindowFlags fl = Qt::Widget);
 	~WpaGui();
 
 	virtual int ctrlRequest(const char *cmd, char *buf, size_t *buflen);
diff --git a/wpa_supplicant/wpa_gui-qt4/wpamsg.h b/wpa_supplicant/wpa_gui-qt4/wpamsg.h
index 8f2fcdc..fe36e20 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpamsg.h
+++ b/wpa_supplicant/wpa_gui-qt4/wpamsg.h
@@ -10,7 +10,7 @@
 #define WPAMSG_H
 
 #include <QDateTime>
-#include <QLinkedList>
+#include <QList>
 
 class WpaMsg {
 public:
@@ -30,6 +30,6 @@
 	QDateTime timestamp;
 };
 
-typedef QLinkedList<WpaMsg> WpaMsgList;
+typedef QList<WpaMsg> WpaMsgList;
 
 #endif /* WPAMSG_H */
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index e3ed858..b8c6f55 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -507,6 +507,102 @@
 }
 
 
+static struct wpabuf * wpas_wfa_gen_capab_attr(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *attr;
+	size_t gen_len, supp_len;
+	const u8 *supp;
+	u8 supp_buf[1];
+	bool add_cert;
+
+	if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_DISABLED)
+		return NULL;
+
+	if (!wpa_s->conf->wfa_gen_capa_supp ||
+	    wpabuf_len(wpa_s->conf->wfa_gen_capa_supp) == 0) {
+		supp_len = 1;
+		supp_buf[0] = 0;
+		if (wpa_s->hw_capab & BIT(CAPAB_HT))
+			supp_buf[0] |= BIT(0); /* Wi-Fi 4 */
+		if (wpa_s->hw_capab & BIT(CAPAB_VHT))
+			supp_buf[0] |= BIT(1); /* Wi-Fi 5 */
+		if (wpa_s->hw_capab & BIT(CAPAB_HE))
+			supp_buf[0] |= BIT(2); /* Wi-Fi 6 */
+		if (wpa_s->hw_capab & BIT(CAPAB_EHT))
+			supp_buf[0] |= BIT(3); /* Wi-Fi 7 */
+		supp = supp_buf;
+	} else {
+		supp_len = wpabuf_len(wpa_s->conf->wfa_gen_capa_supp);
+		supp = wpabuf_head(wpa_s->conf->wfa_gen_capa_supp);
+	}
+
+	add_cert = wpa_s->conf->wfa_gen_capa_cert &&
+		wpabuf_len(wpa_s->conf->wfa_gen_capa_cert) == supp_len;
+
+	gen_len = 1 + supp_len;
+	if (add_cert) {
+		gen_len++;
+		gen_len += wpabuf_len(wpa_s->conf->wfa_gen_capa_cert);
+	}
+
+	attr = wpabuf_alloc(2 + gen_len);
+	if (!attr)
+		return NULL;
+
+	wpabuf_put_u8(attr, WFA_CAPA_ATTR_GENERATIONAL_CAPAB);
+	wpabuf_put_u8(attr, gen_len);
+	wpabuf_put_u8(attr, supp_len);
+	wpabuf_put_data(attr, supp, supp_len);
+	if (add_cert) {
+		wpabuf_put_u8(attr,
+			      wpabuf_len(wpa_s->conf->wfa_gen_capa_cert));
+		wpabuf_put_buf(attr, wpa_s->conf->wfa_gen_capa_cert);
+	}
+
+	return attr;
+}
+
+
+
+static void wpas_wfa_capab_tx(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpabuf *attr, *buf;
+	size_t buf_len;
+
+	if (wpa_s->conf->wfa_gen_capa != WFA_GEN_CAPA_PROTECTED ||
+	    wpa_s->wpa_state != WPA_COMPLETED ||
+	    !pmf_in_use(wpa_s, wpa_s->bssid))
+		return;
+
+	attr = wpas_wfa_gen_capab_attr(wpa_s);
+	if (!attr)
+		return;
+
+	buf_len = 1 + 3 + 1 + 1 + wpabuf_len(attr);
+	buf = wpabuf_alloc(buf_len);
+	if (!buf) {
+		wpabuf_free(attr);
+		return;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
+	wpabuf_put_be32(buf, WFA_CAPAB_VENDOR_TYPE);
+	wpabuf_put_u8(buf, 0); /* Capabilities Length */
+	wpabuf_put_buf(buf, attr);
+	wpabuf_free(attr);
+
+	wpa_printf(MSG_DEBUG, "WFA: Send WFA Capabilities frame");
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "WFA: Failed to send WFA Capabilities frame");
+
+	wpabuf_free(buf);
+}
+
+
 void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -620,6 +716,7 @@
 	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_verify_ssid_beacon, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_wfa_capab_tx, wpa_s, NULL);
 
 	wpas_wps_deinit(wpa_s);
 
@@ -702,7 +799,6 @@
 #ifdef CONFIG_HS20
 	if (wpa_s->drv_priv)
 		wpa_drv_configure_frame_filters(wpa_s, 0);
-	hs20_deinit(wpa_s);
 #endif /* CONFIG_HS20 */
 
 	for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
@@ -963,6 +1059,13 @@
 	if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0)
 		return;
 
+	/* wpa->current_bss might have changed due to memory reallocation, so
+	 * need to update ssid/ssid_len */
+	if (!wpa_s->current_bss)
+		return;
+	ssid = wpa_s->current_bss->ssid;
+	ssid_len = wpa_s->current_bss->ssid_len;
+
 	bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->bssid);
 	if (!bss)
 		return;
@@ -1148,6 +1251,12 @@
 #ifdef CONFIG_HS20
 		hs20_configure_frame_filters(wpa_s);
 #endif
+		if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_PROTECTED &&
+		    pmf_in_use(wpa_s, wpa_s->bssid)) {
+			eloop_cancel_timeout(wpas_wfa_capab_tx, wpa_s, NULL);
+			eloop_register_timeout(0, 100000, wpas_wfa_capab_tx,
+					       wpa_s, NULL);
+		}
 	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
 		   state == WPA_ASSOCIATED) {
 		wpa_s->new_connection = 1;
@@ -1581,6 +1690,9 @@
 {
 	int akm_count = wpa_s->max_num_akms;
 	u8 capab = 0;
+#ifdef CONFIG_SAE
+	enum sae_pwe sae_pwe;
+#endif /* CONFIG_SAE */
 
 	if (akm_count < 2)
 		return;
@@ -1659,13 +1771,16 @@
 		return;
 	}
 
-	if (wpa_s->conf->sae_pwe != SAE_PWE_HUNT_AND_PECK &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+#ifdef CONFIG_SAE
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+	if (sae_pwe != SAE_PWE_HUNT_AND_PECK &&
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
 	if (ssid->sae_pk)
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
 #endif /* CONFIG_SAE_PK */
+#endif /* CONFIG_SAE */
 
 	if (!((wpa_s->allowed_key_mgmts &
 	       (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY)) && capab))
@@ -1704,17 +1819,18 @@
 {
 	struct wpa_ie_data ie;
 	int sel, proto;
+#ifdef CONFIG_SAE
 	enum sae_pwe sae_pwe;
-	const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
+#endif /* CONFIG_SAE */
+	const u8 *bss_wpa, *bss_rsn, *bss_rsnx;
 	bool wmm;
 
 	if (bss) {
 		bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 		bss_rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false);
 		bss_rsnx = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false);
-		bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 	} else {
-		bss_wpa = bss_rsn = bss_rsnx = bss_osen = NULL;
+		bss_wpa = bss_rsn = bss_rsnx = NULL;
 	}
 
 	if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
@@ -1730,34 +1846,17 @@
 		   (ie.key_mgmt & ssid->key_mgmt)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
 		proto = WPA_PROTO_WPA;
-#ifdef CONFIG_HS20
-	} else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN) &&
-		   wpa_parse_wpa_ie(bss_osen, 2 + bss_osen[1], &ie) == 0 &&
-		   (ie.group_cipher & ssid->group_cipher) &&
-		   (ie.pairwise_cipher & ssid->pairwise_cipher) &&
-		   (ie.key_mgmt & ssid->key_mgmt)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
-		proto = WPA_PROTO_OSEN;
-	} else if (bss_rsn && (ssid->proto & WPA_PROTO_OSEN) &&
-	    wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
-	    (ie.group_cipher & ssid->group_cipher) &&
-	    (ie.pairwise_cipher & ssid->pairwise_cipher) &&
-	    (ie.key_mgmt & ssid->key_mgmt)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using OSEN (within RSN)");
-		proto = WPA_PROTO_RSN;
-#endif /* CONFIG_HS20 */
 	} else if (bss) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
 			ssid->proto, ssid->pairwise_cipher, ssid->group_cipher,
 			ssid->key_mgmt);
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s",
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s",
 			MAC2STR(bss->bssid),
 			wpa_ssid_txt(bss->ssid, bss->ssid_len),
 			bss_wpa ? " WPA" : "",
-			bss_rsn ? " RSN" : "",
-			bss_osen ? " OSEN" : "");
+			bss_rsn ? " RSN" : "");
 		if (bss_rsn) {
 			wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]);
 			if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) {
@@ -1784,9 +1883,7 @@
 		}
 		return -1;
 	} else {
-		if (ssid->proto & WPA_PROTO_OSEN)
-			proto = WPA_PROTO_OSEN;
-		else if (ssid->proto & WPA_PROTO_RSN)
+		if (ssid->proto & WPA_PROTO_RSN)
 			proto = WPA_PROTO_RSN;
 		else
 			proto = WPA_PROTO_WPA;
@@ -1816,7 +1913,7 @@
 #ifdef CONFIG_OWE
 			if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
 			    !ssid->owe_only &&
-			    !bss_wpa && !bss_rsn && !bss_osen) {
+			    !bss_wpa && !bss_rsn) {
 				wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 				wpa_s->wpa_proto = 0;
 				*wpa_ie_len = 0;
@@ -1840,7 +1937,7 @@
 	wpa_s->wpa_proto = proto;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
-			 !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
+			 !!(ssid->proto & WPA_PROTO_RSN));
 
 	if (bss || !wpa_s->ap_ies_from_associnfo) {
 		const u8 *rsnoe = NULL, *rsno2e = NULL, *rsnxoe = NULL;
@@ -2028,11 +2125,6 @@
 	} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
-#ifdef CONFIG_HS20
-	} else if (sel & WPA_KEY_MGMT_OSEN) {
-		wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
-		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_OWE
 	} else if (sel & WPA_KEY_MGMT_OWE) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_OWE;
@@ -2063,7 +2155,8 @@
 	    (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV))
 		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
 #endif /* CONFIG_OCV */
-	sae_pwe = wpa_s->conf->sae_pwe;
+#ifdef CONFIG_SAE
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 	if ((ssid->sae_password_id ||
 	     wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
 	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
@@ -2084,6 +2177,7 @@
 			  (!ssid->sae_password && ssid->passphrase &&
 			   sae_pk_valid_password(ssid->passphrase))));
 #endif /* CONFIG_SAE_PK */
+#endif /* CONFIG_SAE */
 	if (bss && is_6ghz_freq(bss->freq) &&
 	    wpas_get_ssid_pmf(wpa_s, ssid) != MGMT_FRAME_PROTECTION_REQUIRED) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Force MFPR=1 on 6 GHz");
@@ -2103,6 +2197,9 @@
 			 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);
+	wpa_sm_set_param(wpa_s->wpa,
+			 WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK,
+			 wpa_s->eapol_2_key_info_set_mask);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	/* Extended Key ID is only supported in infrastructure BSS so far */
@@ -2161,6 +2258,16 @@
 		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION, false);
 	}
 
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SPP_AMSDU,
+			 (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) &&
+			 ieee802_11_rsnx_capab(bss_rsnx,
+					       WLAN_RSNX_CAPAB_SPP_A_MSDU) &&
+			 wpa_s->pairwise_cipher & (WPA_CIPHER_CCMP_256 |
+						   WPA_CIPHER_GCMP_256 |
+						   WPA_CIPHER_CCMP |
+						   WPA_CIPHER_GCMP) &&
+			 (wpa_s->wpa_proto & WPA_PROTO_RSN));
+
 	if (!skip_default_rsne) {
 		if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie,
 						    wpa_ie_len)) {
@@ -2208,9 +2315,9 @@
 		    (ssid->sae_password || ssid->passphrase || ssid->ext_psk))
 			psk_set = 1;
 
-		if (!psk_set) {
+		if (!psk_set && !ssid->pmk_valid) {
 			wpa_msg(wpa_s, MSG_INFO,
-				"No PSK available for association");
+				"No PSK/PMK available for association");
 			wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
 			return -1;
 		}
@@ -2340,6 +2447,8 @@
 		if (!wpa_s->disable_fils)
 			*pos |= 0x01;
 #endif /* CONFIG_FILS */
+		if (wpa_s->conf->twt_requester)
+			*pos |= 0x20; /* Bit 77 - TWT Requester Support */
 		break;
 	case 10: /* Bits 80-87 */
 #ifndef CONFIG_NO_ROBUST_AV
@@ -2474,7 +2583,8 @@
 		if (style == WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS) {
 			/* Pregenerated addresses do not expire but their value
 			 * might have changed, so let's check that. */
-			if (ether_addr_equal(wpa_s->own_addr, ssid->mac_value))
+			if (ssid &&
+			    ether_addr_equal(wpa_s->own_addr, ssid->mac_value))
 				return 0;
 		} else if ((wpa_s->last_mac_addr_change.sec != 0 ||
 			    wpa_s->last_mac_addr_change.usec != 0) &&
@@ -2526,6 +2636,8 @@
 		return -1;
 	}
 
+	wpas_p2p_update_dev_addr(wpa_s);
+
 	wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
 		MAC2STR(addr));
 
@@ -2544,13 +2656,15 @@
 }
 
 
-void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid,
+void wpa_s_setup_sae_pt(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			bool force)
 {
 #ifdef CONFIG_SAE
+	struct wpa_config *conf = wpa_s->conf;
 	int *groups = conf->sae_groups;
 	int default_groups[] = { 19, 20, 21, 0 };
 	const char *password;
+	enum sae_pwe sae_pwe;
 
 	if (!groups || groups[0] <= 0)
 		groups = default_groups;
@@ -2559,13 +2673,15 @@
 	if (!password)
 		password = ssid->passphrase;
 
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+
 	if (!password ||
 	    !wpa_key_mgmt_sae(ssid->key_mgmt) ||
-	    (conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
+	    (sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
 	     !wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) &&
 	     !force &&
 	     !sae_pk_valid_password(password)) ||
-	    conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) {
+	    sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) {
 		/* PT derivation not needed */
 		sae_deinit_pt(ssid->pt);
 		ssid->pt = NULL;
@@ -2616,6 +2732,9 @@
 			"Could not update MAC address information");
 		return -1;
 	}
+
+	wpas_p2p_update_dev_addr(wpa_s);
+
 	wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
 	return 0;
 }
@@ -2686,7 +2805,7 @@
 		wpa_s_clear_sae_rejected(wpa_s);
 
 #ifdef CONFIG_SAE
-	wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
+	wpa_s_setup_sae_pt(wpa_s, ssid, false);
 #endif /* CONFIG_SAE */
 
 	if (rand_style > WPAS_MAC_ADDR_STYLE_PERMANENT) {
@@ -3096,6 +3215,27 @@
 }
 
 
+static int ibss_get_center_320mhz(int channel)
+{
+	int seg0;
+
+	if (channel >= 1 && channel <= 45)
+		seg0 = 31;
+	else if (channel >= 49 && channel <= 77)
+		seg0 = 63;
+	else if (channel >= 81 && channel <= 109)
+		seg0 = 95;
+	else if (channel >= 113 && channel <= 141)
+		seg0 = 127;
+	else if (channel >= 145 && channel <= 173)
+		seg0 = 159;
+	else
+		seg0 = 191;
+
+	return seg0;
+}
+
+
 static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
 				       const struct wpa_ssid *ssid,
 				       struct hostapd_hw_modes *mode,
@@ -3109,6 +3249,11 @@
 	static const int bw160[] = {
 		5955, 6115, 6275, 6435, 6595, 6755, 6915
 	};
+	static const int bw320[]= {
+		5955, 6255, 6115, 6415, 6275, 6575, 6435,
+		6735, 6595, 6895, 6755, 7055
+	};
+
 	struct hostapd_freq_params vht_freq;
 	int i;
 	unsigned int j, k;
@@ -3169,6 +3314,26 @@
 		}
 	}
 
+	/* In 320 MHz, the initial four 20 MHz channels were validated
+	 * above. If 320 MHz is supported, check the remaining 12 20 MHz
+	 * channels for the total of 320 MHz bandwidth for 6 GHz.
+	 */
+	if ((mode->eht_capab[ieee80211_mode].phy_cap[
+		     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+	     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK) && is_6ghz &&
+	    ibss_mesh_is_80mhz_avail(channel + 16, mode) &&
+	    ibss_mesh_is_80mhz_avail(channel + 32, mode) &&
+	    ibss_mesh_is_80mhz_avail(channel + 48, mode)) {
+		for (j = 0; j < ARRAY_SIZE(bw320); j += 2) {
+			if (freq->freq >= bw320[j] &&
+			    freq->freq <= bw320[j + 1]) {
+				chwidth = CONF_OPER_CHWIDTH_320MHZ;
+				seg0 = ibss_get_center_320mhz(freq->channel);
+				break;
+			}
+		}
+	}
+
 	if (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
 		/* setup center_freq2, bandwidth */
 		for (k = 0; k < ARRAY_SIZE(bw80); k++) {
@@ -3413,13 +3578,12 @@
 }
 
 
-static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s,
-				  struct wpa_bss *bss,
-				  u8 *wpa_ie, size_t wpa_ie_len,
-				  size_t max_wpa_ie_len)
+int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   u8 *wpa_ie, size_t wpa_ie_len, size_t max_wpa_ie_len)
 {
-	struct wpabuf *wfa_ie = NULL;
+	struct wpabuf *wfa_ie = NULL, *attr = NULL;
 	u8 wfa_capa[1];
+	u8 capab_len = 0;
 	size_t wfa_ie_len, buf_len;
 
 	os_memset(wfa_capa, 0, sizeof(wfa_capa));
@@ -3431,7 +3595,13 @@
 	if (wpa_is_non_eht_scs_traffic_desc_supported(bss))
 		wfa_capa[0] |= WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC;
 
-	if (!wfa_capa[0])
+	if (wfa_capa[0])
+		capab_len = 1;
+
+	if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_UNPROTECTED)
+		attr = wpas_wfa_gen_capab_attr(wpa_s);
+
+	if (capab_len == 0 && !attr)
 		return wpa_ie_len;
 
 	/* Wi-Fi Alliance element */
@@ -3440,17 +3610,23 @@
 		  3 +	/* OUI */
 		  1 +	/* OUI Type */
 		  1 +	/* Capabilities Length */
-		  sizeof(wfa_capa);	/* Capabilities */
+		  capab_len +	/* Capabilities */
+		  (attr ? wpabuf_len(attr) : 0) /* Attributes */;
 	wfa_ie = wpabuf_alloc(buf_len);
-	if (!wfa_ie)
+	if (!wfa_ie) {
+		wpabuf_free(attr);
 		return wpa_ie_len;
+	}
 
 	wpabuf_put_u8(wfa_ie, WLAN_EID_VENDOR_SPECIFIC);
 	wpabuf_put_u8(wfa_ie, buf_len - 2);
 	wpabuf_put_be24(wfa_ie, OUI_WFA);
 	wpabuf_put_u8(wfa_ie, WFA_CAPA_OUI_TYPE);
-	wpabuf_put_u8(wfa_ie, sizeof(wfa_capa));
-	wpabuf_put_data(wfa_ie, wfa_capa, sizeof(wfa_capa));
+	wpabuf_put_u8(wfa_ie, capab_len);
+	wpabuf_put_data(wfa_ie, wfa_capa, capab_len);
+	if (attr)
+		wpabuf_put_buf(wfa_ie, attr);
+	wpabuf_free(attr);
 
 	wfa_ie_len = wpabuf_len(wfa_ie);
 	if (wpa_ie_len + wfa_ie_len <= max_wpa_ie_len) {
@@ -3543,19 +3719,6 @@
 			os_free(wpa_ie);
 			return NULL;
 		}
-#ifdef CONFIG_HS20
-	} else if (bss && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) &&
-		   (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) {
-		/* No PMKSA caching, but otherwise similar to RSN/WPA */
-		wpa_ie_len = max_wpa_ie_len;
-		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
-					      wpa_ie, &wpa_ie_len, false)) {
-			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
-				"key management and encryption suites");
-			os_free(wpa_ie);
-			return NULL;
-		}
-#endif /* CONFIG_HS20 */
 	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
 		   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
 		/*
@@ -3995,10 +4158,10 @@
 	}
 
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE_SUPPORT,
-			 wpas_rsn_overriding(wpa_s));
+			 wpas_rsn_overriding(wpa_s, ssid));
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
 			 RSN_OVERRIDE_NOT_USED);
-	if (wpas_rsn_overriding(wpa_s) &&
+	if (wpas_rsn_overriding(wpa_s, ssid) &&
 	    wpas_ap_supports_rsn_overriding(wpa_s, bss) &&
 	    wpa_ie_len + 2 + 4 + 1 <= max_wpa_ie_len) {
 		u8 *pos = wpa_ie + wpa_ie_len, *start = pos;
@@ -4035,7 +4198,7 @@
 		wpa_ie_len += pos - start;
 	}
 
-	params->rsn_overriding = wpas_rsn_overriding(wpa_s);
+	params->rsn_overriding = wpas_rsn_overriding(wpa_s, ssid);
 	params->wpa_ie = wpa_ie;
 	params->wpa_ie_len = wpa_ie_len;
 	params->auth_alg = algs;
@@ -4686,7 +4849,7 @@
 		params.prev_bssid = prev_bssid;
 
 #ifdef CONFIG_SAE
-	params.sae_pwe = wpa_s->conf->sae_pwe;
+	params.sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 #endif /* CONFIG_SAE */
 
 	ret = wpa_drv_associate(wpa_s, &params);
@@ -5144,6 +5307,51 @@
 }
 
 
+static bool ssid_in_last_scan(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid)
+{
+	size_t i;
+
+	/* Check if the previous scan included the selected network */
+	if (wpa_s->last_scan_num_ssids <= 1 ||
+	    !ssid->ssid || ssid->ssid_len == 0)
+		return false;
+
+	/* Iterate through the previous scan SSIDs */
+	for (i = 0; i < wpa_s->last_scan_num_ssids;  i++) {
+		if (os_memcmp(wpa_s->last_scan_ssids[i].ssid, ssid->ssid,
+			      ssid->ssid_len) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+
+/**
+ * Checks whether an SSID was discovered in the last scan.
+ * @wpa_s: wpa_supplicant structure for a network interface.
+ * @ssid: wpa_ssid structure for a configured network.
+ * Returns: true if ssid found, false otherwise.
+ */
+static bool ssid_in_last_scan_res(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid)
+{
+	size_t i;
+
+	if (!wpa_s->last_scan_res || !ssid->ssid || ssid->ssid_len == 0)
+		return false;
+
+	for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+		if (os_memcmp(wpa_s->last_scan_res[i]->ssid,
+			      ssid->ssid, ssid->ssid_len) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+
 /**
  * wpa_supplicant_select_network - Attempt association with a network
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -5201,13 +5409,22 @@
 			(ssid->mode == WPAS_MODE_MESH ||
 			 ssid->mode == WPAS_MODE_AP) ? ssid : NULL;
 
-		if (ssid->scan_ssid &&
-		    (wpa_s->no_suitable_network || wpa_s->last_scan_external)) {
-			wpa_printf(MSG_DEBUG,
-				   "Request a new scan for hidden network");
-			request_new_scan = true;
-		} else if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
-			   !ssid->owe_only) {
+		if (ssid->scan_ssid) {
+			if (ssid_in_last_scan(wpa_s, ssid)) {
+				wpa_printf(MSG_DEBUG,
+					   "Hidden network was scanned for in last scan");
+			} else if (ssid_in_last_scan_res(wpa_s, ssid)) {
+				wpa_printf(MSG_DEBUG,
+					   "Hidden network was found in last scan results");
+			} else {
+				request_new_scan = true;
+				wpa_printf(MSG_DEBUG,
+					   "Request a new scan for hidden network");
+			}
+		}
+
+		if (!request_new_scan && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
+		    !ssid->owe_only) {
 			wpa_printf(MSG_DEBUG,
 				   "Request a new scan for OWE transition SSID");
 			request_new_scan = true;
@@ -5229,7 +5446,7 @@
 	wpa_s->last_owe_group = 0;
 	if (ssid) {
 		ssid->owe_transition_bss_select_count = 0;
-		wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
+		wpa_s_setup_sae_pt(wpa_s, ssid, false);
 	}
 
 	if (wpa_s->connect_without_scan || request_new_scan ||
@@ -5765,6 +5982,7 @@
 			MACSTR ")",
 			wpa_supplicant_state_txt(wpa_s->wpa_state),
 			MAC2STR(connected_addr));
+	delay_processing:
 		wpabuf_free(wpa_s->pending_eapol_rx);
 		wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
 		if (wpa_s->pending_eapol_rx) {
@@ -5862,9 +6080,23 @@
 			      encrypted) > 0)
 		return;
 	wpa_drv_poll(wpa_s);
-	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK))
-		wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len, encrypted);
-	else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) {
+		if (wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len,
+				    encrypted) == -2 &&
+#ifdef CONFIG_AP
+		    !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+		    wpa_s->last_eapol_matches_bssid) {
+			/* Handle the case where reassociation occurs to the
+			 * current connected AP */
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Delay processing of received EAPOL frame for reassociation to the current connected AP (state=%s connected_addr="
+				MACSTR ")",
+				wpa_supplicant_state_txt(wpa_s->wpa_state),
+				MAC2STR(connected_addr));
+			goto delay_processing;
+		}
+	} else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
 		/*
 		 * Set portValid = true here since we are going to skip 4-way
 		 * handshake processing which would normally set portValid. We
@@ -6119,6 +6351,10 @@
 	wpa_s->new_connection = 1;
 	wpa_s->parent = parent ? parent : wpa_s;
 	wpa_s->p2pdev = wpa_s->parent;
+#ifdef CONFIG_P2P
+	if (parent)
+		wpa_s->p2p_mode = parent->p2p_mode;
+#endif /* CONFIG_P2P */
 	wpa_s->sched_scanning = 0;
 	wpa_s->setband_mask = WPA_SETBAND_AUTO;
 
@@ -6133,6 +6369,12 @@
 #endif /* CONFIG_NO_ROBUST_AV */
 	wpa_s->ml_probe_mld_id = -1;
 
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+	dl_list_init(&wpa_s->mesh_external_pmksa_cache);
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
 	return wpa_s;
 }
 
@@ -6348,7 +6590,7 @@
 
 	htcaps_mask->ht_capabilities_info |= msk;
 	htcaps->ht_capabilities_info &= ~msk;
-	htcaps->ht_capabilities_info |= (tx_stbc << 7) & msk;
+	htcaps->ht_capabilities_info |= host_to_le16(tx_stbc << 7) & msk;
 
 	return 0;
 }
@@ -6374,7 +6616,7 @@
 
 	htcaps_mask->ht_capabilities_info |= msk;
 	htcaps->ht_capabilities_info &= ~msk;
-	htcaps->ht_capabilities_info |= (rx_stbc << 8) & msk;
+	htcaps->ht_capabilities_info |= host_to_le16(rx_stbc << 8) & msk;
 
 	return 0;
 }
@@ -6441,13 +6683,15 @@
 
 #ifdef CONFIG_HT_OVERRIDES
 	if (ssid->disable_sgi) {
-		vhtcaps_mask->vht_capabilities_info |= (VHT_CAP_SHORT_GI_80 |
-							VHT_CAP_SHORT_GI_160);
-		vhtcaps->vht_capabilities_info &= ~(VHT_CAP_SHORT_GI_80 |
-						    VHT_CAP_SHORT_GI_160);
+		vhtcaps_mask->vht_capabilities_info |=
+			host_to_le32(VHT_CAP_SHORT_GI_80 |
+				     VHT_CAP_SHORT_GI_160);
+		vhtcaps->vht_capabilities_info &=
+			host_to_le32(~(VHT_CAP_SHORT_GI_80 |
+				       VHT_CAP_SHORT_GI_160));
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"disable-sgi override specified, vht-caps: 0x%x",
-			vhtcaps->vht_capabilities_info);
+			le_to_host32(vhtcaps->vht_capabilities_info));
 	}
 
 	/* if max ampdu is <= 3, we have to make the HT cap the same */
@@ -7470,17 +7714,16 @@
 		u16 i;
 
 		for (i = 0; i < wpa_s->hw.num_modes; i++) {
-			if (wpa_s->hw.modes[i].vht_capab) {
-				wpa_s->hw_capab = CAPAB_VHT;
-				break;
-			}
-
-			if (wpa_s->hw.modes[i].ht_capab &
-			    HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
-				wpa_s->hw_capab = CAPAB_HT40;
-			else if (wpa_s->hw.modes[i].ht_capab &&
-				 wpa_s->hw_capab == CAPAB_NO_HT_VHT)
-				wpa_s->hw_capab = CAPAB_HT;
+			if (wpa_s->hw.modes[i].eht_capab[IEEE80211_MODE_INFRA].
+			    eht_supported)
+				wpa_s->hw_capab |= BIT(CAPAB_EHT);
+			if (wpa_s->hw.modes[i].he_capab[IEEE80211_MODE_INFRA].
+			    he_supported)
+				wpa_s->hw_capab |= BIT(CAPAB_HE);
+			if (wpa_s->hw.modes[i].vht_capab)
+				wpa_s->hw_capab |= BIT(CAPAB_VHT);
+			if (wpa_s->hw.modes[i].ht_capab)
+				wpa_s->hw_capab |= BIT(CAPAB_HT);
 		}
 		wpa_s->support_6ghz = wpas_is_6ghz_supported(wpa_s, false);
 	}
@@ -7522,12 +7765,15 @@
 		    capa.mac_addr_rand_sched_scan_supported)
 			wpa_s->mac_addr_rand_supported |=
 				(MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
+		wpa_s->drv_max_probe_req_ie_len = capa.max_probe_req_ie_len;
 
 		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
 		if (wpa_s->extended_capa &&
 		    wpa_s->extended_capa_len >= 3 &&
 		    wpa_s->extended_capa[2] & 0x40)
 			wpa_s->multi_bss_support = 1;
+	} else {
+		wpa_s->drv_max_probe_req_ie_len = 1500;
 	}
 #ifdef CONFIG_PASN
 	wpa_pasn_sm_set_caps(wpa_s->wpa, wpa_s->drv_flags2);
@@ -7642,12 +7888,6 @@
 	if (wpa_bss_init(wpa_s) < 0)
 		return -1;
 
-#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
-#ifdef CONFIG_MESH
-	dl_list_init(&wpa_s->mesh_external_pmksa_cache);
-#endif /* CONFIG_MESH */
-#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
-
 	/*
 	 * Set Wake-on-WLAN triggers, if configured.
 	 * Note: We don't restore/remove the triggers on shutdown (it doesn't
@@ -7683,9 +7923,6 @@
 
 	wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
 
-#ifdef CONFIG_HS20
-	hs20_init(wpa_s);
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_MBO
 	if (!wpa_s->disable_mbo_oce && wpa_s->conf->oce) {
 		if ((wpa_s->conf->oce & OCE_STA) &&
@@ -8713,12 +8950,19 @@
 }
 
 
-bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s)
+bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
-	if (wpa_s->conf->rsn_overriding == RSN_OVERRIDING_DISABLED)
+	enum wpas_rsn_overriding rsno;
+
+	if (ssid && ssid->rsn_overriding != RSN_OVERRIDING_NOT_SET)
+		rsno = ssid->rsn_overriding;
+	else
+		rsno = wpa_s->conf->rsn_overriding;
+
+	if (rsno == RSN_OVERRIDING_DISABLED)
 		return false;
 
-	if (wpa_s->conf->rsn_overriding == RSN_OVERRIDING_ENABLED)
+	if (rsno == RSN_OVERRIDING_ENABLED)
 		return true;
 
 	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
@@ -8912,7 +9156,8 @@
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
 	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
-	    !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) &&
+	    !(wpa_key_mgmt_sae(ssid->key_mgmt) &&
+	      (ssid->passphrase || ssid->sae_password || ssid->pmk_valid)) &&
 	    !ssid->mem_only_psk)
 		return 1;
 
@@ -8981,6 +9226,16 @@
 
 
 #ifdef CONFIG_SAE
+
+enum sae_pwe wpas_get_ssid_sae_pwe(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid)
+{
+	if (!ssid || ssid->sae_pwe == DEFAULT_SAE_PWE)
+		return wpa_s->conf->sae_pwe;
+	return ssid->sae_pwe;
+}
+
+
 bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
 			 struct wpa_ssid *ssid,
 			 const struct wpa_ie_data *ie)
@@ -8990,6 +9245,7 @@
 		   (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) ||
 		 wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION);
 }
+
 #endif /* CONFIG_SAE */
 
 
@@ -9725,7 +9981,7 @@
 	}
 
 	return wpa_s->driver->send_action(wpa_s->drv_priv, freq, wait, dst, src,
-					  bssid, data, data_len, no_cck);
+					  bssid, data, data_len, no_cck, -1);
 }
 
 
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index abbe7d7..cf10118 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -401,6 +401,50 @@
 # it from taking 100% of radio resources. The default value is 500 ms.
 #p2p_search_delay=500
 
+# Enable/disable P2P pairing setup
+#p2p_pairing_setup=0
+
+# Enable/disable P2P pairing cache for verification
+#p2p_pairing_cache=0
+
+# Enable/disable P2P pairing verification with cached NIK/NPK
+#p2p_pairing_verification=0
+
+# Supported P2P bootstrapping method bitmap
+# b0: whether opportunistic bootstrapping is supported
+# b1: whether PIN display is supported
+# b2: whether passphrase display is supported
+# b3: whether QR Code display is supported
+# b4: whether NFC tag is supported
+# b5: whether keypad (PIN only) is supported
+# b6: whether keypad (passphrase) is supported
+# b7: whether QR Code scan is supported
+# b8: whether NFC reader is supported
+# b14: whether service managed bootstrapping is supported
+# b15: whether bootstrapping handshakes skipped is supported
+#p2p_bootstrap_methods=0
+
+# Bitmap of supported PASN types
+# B0: whether DH Group 19 with unauthenticated PASN is supported
+# B1: whether DH Group 19 with authenticated PASN is supported
+# B2: whether DH Group 20 with unauthenticated PASN is supported
+# B3: whether DH Group 20 authenticated PASN is supported
+#p2p_pasn_type=0
+
+# Bootstrap request for unauthorized peer is asked to come back after
+# this many TUs.
+#p2p_comeback_after=977
+
+# Enable/disable TWT based power management for P2P
+#p2p_twt_power_mgmt=0
+
+# Enable/disable P2P client channel switch request
+#p2p_chan_switch_req_enable=0
+
+# Regulatory info encoding for operation in 6 GHz band
+# As defined in Table E-12 and E-13 of IEEE P802.11-REVme/D7.0.
+#p2p_reg_info=0
+
 # Opportunistic Key Caching (also known as Proactive Key Caching) default
 # This parameter can be used to set the default behavior for the
 # proactive_key_caching parameter. By default, OKC is disabled unless enabled
@@ -608,6 +652,34 @@
 # 1 = Publish
 #ftm_initiator=0
 
+#twt_requester: Whether TWT requester is enabled
+# 0 = disabled (default)
+# 1 = enabled if supported by the driver
+#twt_requester=0
+
+# Wi-Fi Alliance generational capabilities indication
+#
+# wfa_gen_capa: Whether to indicate Wi-Fi generational capability to the AP
+# 0 = do not indicate (default)
+# 1 = indicate in protected Action frame
+# 2 = indicate in unprotected (Re)Association Request frame
+#wfa_gen_capa=0
+#
+# wfa_gen_capa_supp: Supported Generations (hexdump of a bit field)
+# A bit field of supported Wi-Fi generations. This is encoded as an little
+# endian octet string. If this is not set, the driver capabilities are
+# determined automatically.
+# bit 0: Wi-Fi 4
+# bit 1: Wi-Fi 5
+# bit 2: Wi-Fi 6
+# bit 3: Wi-Fi 7
+#wfa_gen_capa_supp=07
+#
+# wfa_gen_capa_cert: Certified Generations (hexdump of a bit field)
+# This has the same format as wfa_gen_capa_supp. This is an optional field, but
+# if included, shall have the same length as wfa_gen_capa_supp.
+#wfa_gen_capa_cert=07
+
 # credential block
 #
 # Each credential used for automatic network selection is configured as a set
@@ -882,6 +954,8 @@
 # NOTE: The protocol used for this mechanism is still subject to change and as
 # such, this should not yet be enabled for production uses to avoid issues if
 # something were to change.
+# A per-network block parameter with the same name can be used to override this
+# global parameter.
 # 0 = Disabled (default)
 # 1 = Enabled automatically if the driver indicates support
 # 2 = Forced to be enabled even without driver capability indication
@@ -1182,6 +1256,10 @@
 # mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being
 # default priority
 #
+# macsec_icv_indicator: Always include ICV indicator
+# 0 = ICV Indicator is not included when ICV has default length (default)
+# 1 = ICV Indicator is always included (compatibility mode)
+#
 # mixed_cell: This option can be used to configure whether so called mixed
 # cells, i.e., networks that use both plaintext and encryption in the same
 # SSID, are allowed when selecting a BSS from scan results.
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 84b7bd5..8965bfa 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -470,15 +470,6 @@
 	WPAS_TEST_FAILURE_SCAN_TRIGGER,
 };
 
-struct icon_entry {
-	struct dl_list list;
-	u8 bssid[ETH_ALEN];
-	u8 dialog_token;
-	char *file_name;
-	u8 *image;
-	size_t image_len;
-};
-
 struct wpa_bss_tmp_disallowed {
 	struct dl_list list;
 	u8 bssid[ETH_ALEN];
@@ -686,6 +677,18 @@
 };
 
 
+enum local_hw_capab {
+	CAPAB_HT,
+	CAPAB_VHT,
+	CAPAB_HE,
+	CAPAB_EHT,
+};
+
+struct last_scan_ssid {
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -823,6 +826,8 @@
 	size_t last_scan_res_size;
 	struct os_reltime last_scan;
 	bool last_scan_external;
+	struct last_scan_ssid last_scan_ssids[WPAS_MAX_SCAN_SSIDS];
+	size_t last_scan_num_ssids;
 
 	const struct wpa_driver_ops *driver;
 	int interface_removed; /* whether the network interface has been
@@ -844,7 +849,7 @@
 	int eapol_received; /* number of EAPOL packets received after the
 			     * previous association event */
 
-	u8 rsnxe[20];
+	u8 rsnxe[257];
 	size_t rsnxe_len;
 
 	struct scard_data *scard;
@@ -941,6 +946,7 @@
 	unsigned int drv_key_mgmt;
 	unsigned int drv_rrm_flags;
 	unsigned int drv_max_acl_mac_addrs;
+	size_t drv_max_probe_req_ie_len;
 
 	/*
 	 * A bitmap of supported protocols for probe response offload. See
@@ -1042,6 +1048,7 @@
 		u8 sched_obss_scan;
 		u16 obss_scan_int;
 		u16 bss_max_idle_period;
+		bool spp_amsdu;
 #ifdef CONFIG_SAE
 		struct sae_data sae;
 		struct wpabuf *sae_token;
@@ -1137,6 +1144,7 @@
 	u8 pending_join_dev_addr[ETH_ALEN];
 	u8 p2p_bootstrap_dev_addr[ETH_ALEN];
 	int pending_join_wps_method;
+	char pending_join_password[100];
 	u8 p2p_join_ssid[SSID_MAX_LEN];
 	size_t p2p_join_ssid_len;
 	int p2p_join_scan_count;
@@ -1193,6 +1201,7 @@
 	unsigned int p2p2:1;
 	u16 p2p_bootstrap;
 	enum hostapd_hw_mode p2p_go_acs_band;
+	enum wpa_p2p_mode p2p_mode;
 	int p2p_persistent_go_freq;
 	int p2p_persistent_id;
 	int p2p_go_intent;
@@ -1202,6 +1211,7 @@
 	struct wpa_radio_work *p2p_scan_work;
 	struct wpa_radio_work *p2p_listen_work;
 	struct wpa_radio_work *p2p_send_action_work;
+	bool p2p_removing_listen_work;
 
 	u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
 	struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
@@ -1255,17 +1265,7 @@
 	unsigned int auto_network_select:1;
 	unsigned int interworking_fast_assoc_tried:1;
 	unsigned int fetch_all_anqp:1;
-	unsigned int fetch_osu_info:1;
-	unsigned int fetch_osu_waiting_scan:1;
-	unsigned int fetch_osu_icon_in_progress:1;
 	struct wpa_bss *interworking_gas_bss;
-	unsigned int osu_icon_id;
-	struct dl_list icon_head; /* struct icon_entry */
-	struct osu_provider *osu_prov;
-	size_t osu_prov_count;
-	struct os_reltime osu_icon_fetch_start;
-	unsigned int num_osu_scans;
-	unsigned int num_prov_found;
 #endif /* CONFIG_INTERWORKING */
 	unsigned int drv_capa_known;
 
@@ -1274,12 +1274,7 @@
 		u16 num_modes;
 		u16 flags;
 	} hw;
-	enum local_hw_capab {
-		CAPAB_NO_HT_VHT,
-		CAPAB_HT,
-		CAPAB_HT40,
-		CAPAB_VHT,
-	} hw_capab;
+	unsigned int hw_capab; /* bitmap of enum local_hw_capab bits */
 #ifdef CONFIG_MACSEC
 	struct ieee802_1x_kay *kay;
 #endif /* CONFIG_MACSEC */
@@ -1400,6 +1395,7 @@
 	unsigned int oci_freq_override_fils_assoc;
 	unsigned int oci_freq_override_wnm_sleep;
 	unsigned int disable_eapol_g2_tx;
+	unsigned int eapol_2_key_info_set_mask;
 	int test_assoc_comeback_type;
 #endif /* CONFIG_TESTING_OPTIONS */
 
@@ -1619,6 +1615,9 @@
 	struct wpa_radio_work *pasn_auth_work;
 	unsigned int pasn_count;
 	struct pasn_auth *pasn_params;
+#ifdef CONFIG_P2P
+	struct wpa_radio_work *p2p_pasn_auth_work;
+#endif /* CONFIG_P2P */
 #endif /* CONFIG_PASN */
 
 	bool is_6ghz_enabled;
@@ -1760,7 +1759,7 @@
 void fils_connection_failure(struct wpa_supplicant *wpa_s);
 void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
 int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
-bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s);
+bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
 void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
 		      const u8 *bssid);
@@ -1773,6 +1772,9 @@
 void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen,
 			 struct wpa_bss *bss);
+int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   u8 *wpa_ie, size_t wpa_ie_len,
+			   size_t max_wpa_ie_len);
 int wpas_update_random_addr(struct wpa_supplicant *wpa_s,
 			    enum wpas_mac_addr_style style,
 			    struct wpa_ssid *ssid);
@@ -1908,7 +1910,8 @@
 					struct channel_list_changed *info);
 int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
 					   struct wpa_bss *current_bss,
-					   struct wpa_bss *seleceted);
+					   struct wpa_bss *selected,
+					   bool poll_current);
 void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s);
 
 /* eap_register.c */
@@ -1944,8 +1947,10 @@
 
 int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+enum sae_pwe wpas_get_ssid_sae_pwe(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid);
 int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
-void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid,
+void wpa_s_setup_sae_pt(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			bool force);
 void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s);
 
@@ -2001,7 +2006,13 @@
 struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 				     int i, struct wpa_bss *bss,
 				     struct wpa_ssid *group,
-				     int only_first_ssid, int debug_print);
+				     int only_first_ssid, int debug_print,
+				     bool link);
+
+struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
+					   struct wpa_ssid *group,
+					   struct wpa_ssid **selected_ssid,
+					   int only_first_ssid);
 
 int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
 						enum wpa_driver_if_type if_type,
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index cec26c4..6a45d4a 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -9,3 +9,4 @@
 sae_pwe=2
 p2p_optimize_listen_chan=1
 wowlan_disconnect_on_deinit=1
+rsn_overriding=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 741ac6c..0011274 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -355,7 +355,10 @@
 			   "driver-based 4-way hs and FT");
 		res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
 		if (res == 0) {
-			os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
+			if (wpa_key_mgmt_sha384(wpa_s->key_mgmt))
+				os_memcpy(pmk, buf, pmk_len);
+			else
+				os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
 			os_memset(buf, 0, sizeof(buf));
 		}
 #else /* CONFIG_IEEE80211R */
@@ -1473,8 +1476,6 @@
 			ptk, NULL, NULL, 0);
 }
 
-#endif /* CONFIG_NO_WPA */
-
 
 #ifdef CONFIG_PASN
 static int wpa_supplicant_set_ltf_keyseed(void *_wpa_s, const u8 *own_addr,
@@ -1509,6 +1510,8 @@
 	wpa_msg(wpa_s, MSG_INFO, "RSN: SSID matched expected value");
 }
 
+#endif /* CONFIG_NO_WPA */
+
 
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index 600b3bc..7893a39 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -249,6 +249,7 @@
 				  ssid->macsec_replay_window,
 				  ssid->macsec_offload, ssid->macsec_port,
 				  ssid->mka_priority, ssid->macsec_csindex,
+				  ssid->macsec_icv_indicator,
 				  wpa_s->ifname, wpa_s->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
 	if (res == NULL)
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index f3a8c9c..d8f6198 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1375,7 +1375,7 @@
 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
 		wpa_supplicant_cancel_scan(wpa_s);
 		wpas_clear_wps(wpa_s);
-	} else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+	} else if (wpa_s->wpa_state >= WPA_ASSOCIATING) {
 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
 			   "deauthenticate");
 		wpa_s->own_disconnect_req = 1;