Address wpa_supplicant vulnerabilities am: 7899587b4b

Change-Id: I91c0f47d498fc1d95dd63cd1201ae3cbc86bd003
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index df3542a..eb01da5 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -216,18 +216,12 @@
 
 L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
 
-ifdef CONFIG_IAPP
-L_CFLAGS += -DCONFIG_IAPP
-OBJS += src/ap/iapp.c
-endif
-
 ifdef CONFIG_RSN_PREAUTH
 L_CFLAGS += -DCONFIG_RSN_PREAUTH
 CONFIG_L2_PACKET=y
 endif
 
 ifdef CONFIG_HS20
-NEED_AES_OMAC1=y
 CONFIG_PROXYARP=y
 endif
 
@@ -237,8 +231,6 @@
 
 ifdef CONFIG_SUITEB
 L_CFLAGS += -DCONFIG_SUITEB
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_SUITEB192
@@ -249,24 +241,14 @@
 ifdef CONFIG_OCV
 L_CFLAGS += -DCONFIG_OCV
 OBJS += src/common/ocv.c
-CONFIG_IEEE80211W=y
-endif
-
-ifdef CONFIG_IEEE80211W
-L_CFLAGS += -DCONFIG_IEEE80211W
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_IEEE80211R
 L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=y
 NEED_AES_SIV=y
 NEED_ETH_P_OUI=y
-NEED_SHA256=y
 NEED_HMAC_SHA256_KDF=y
 endif
 
@@ -280,6 +262,7 @@
 OBJS += src/common/sae.c
 NEED_ECC=y
 NEED_DH_GROUPS=y
+NEED_DRAGONFLY=y
 endif
 
 ifdef CONFIG_OWE
@@ -288,11 +271,14 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
 endif
 
+ifdef CONFIG_WAPI_INTERFACE
+L_CFLAGS += -DCONFIG_WAPI_INTERFACE
+endif
+
 ifdef CONFIG_FILS
 L_CFLAGS += -DCONFIG_FILS
 OBJS += src/ap/fils_hlp.c
@@ -423,7 +409,6 @@
 L_CFLAGS += -DEAP_SERVER_AKA
 OBJS += src/eap_server/eap_server_aka.c
 CONFIG_EAP_SIM_COMMON=y
-NEED_SHA256=y
 NEED_AES_CBC=y
 endif
 
@@ -448,7 +433,6 @@
 ifdef CONFIG_EAP_PSK
 L_CFLAGS += -DEAP_SERVER_PSK
 OBJS += src/eap_server/eap_server_psk.c src/eap_common/eap_psk_common.c
-NEED_AES_OMAC1=y
 NEED_AES_ENCBLOCK=y
 NEED_AES_EAX=y
 endif
@@ -464,15 +448,13 @@
 ifdef CONFIG_EAP_GPSK_SHA256
 L_CFLAGS += -DEAP_GPSK_SHA256
 endif
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_EAP_PWD
 L_CFLAGS += -DEAP_SERVER_PWD
 OBJS += src/eap_server/eap_server_pwd.c src/eap_common/eap_pwd_common.c
-NEED_SHA256=y
 NEED_ECC=y
+NEED_DRAGONFLY=y
 endif
 
 ifdef CONFIG_EAP_EKE
@@ -496,6 +478,18 @@
 NEED_AES_UNWRAP=y
 endif
 
+ifdef CONFIG_EAP_TEAP
+L_CFLAGS += -DEAP_SERVER_TEAP
+OBJS += src/eap_server/eap_server_teap.c
+OBJS += src/eap_common/eap_teap_common.c
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_TLS_PRF_SHA256=y
+NEED_TLS_PRF_SHA384=y
+NEED_AES_UNWRAP=y
+endif
+
 ifdef CONFIG_WPS
 L_CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
 OBJS += src/utils/uuid.c
@@ -510,7 +504,6 @@
 OBJS += src/wps/wps_enrollee.c
 OBJS += src/wps/wps_registrar.c
 NEED_DH_GROUPS=y
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
@@ -559,9 +552,9 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
+NEED_ECC=y
 NEED_JSON=y
 NEED_GAS=y
 NEED_BASE64=y
@@ -606,6 +599,10 @@
 L_CFLAGS += -DPKCS12_FUNCS
 endif
 
+ifdef NEED_DRAGONFLY
+OBJS += src/common/dragonfly.c
+endif
+
 ifdef MS_FUNCS
 OBJS += src/crypto/ms_funcs.c
 NEED_DES=y
@@ -634,7 +631,6 @@
 
 ifdef CONFIG_TLSV12
 L_CFLAGS += -DCONFIG_TLSV12
-NEED_SHA256=y
 endif
 
 ifeq ($(CONFIG_TLS), openssl)
@@ -648,7 +644,6 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += src/crypto/fips_prf_openssl.c
 endif
-NEED_SHA256=y
 NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_h += -lcrypto
@@ -706,7 +701,6 @@
 OBJS += src/tls/pkcs1.c
 OBJS += src/tls/pkcs5.c
 OBJS += src/tls/pkcs8.c
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
 ifdef CONFIG_TLSV12
@@ -801,12 +795,10 @@
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_SIV
 AESOBJS += src/crypto/aes-siv.c
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += src/crypto/aes-ctr.c
@@ -814,9 +806,7 @@
 ifdef NEED_AES_ENCBLOCK
 AESOBJS += src/crypto/aes-encblock.c
 endif
-ifdef NEED_AES_OMAC1
 AESOBJS += src/crypto/aes-omac1.c
-endif
 ifdef NEED_AES_UNWRAP
 ifneq ($(CONFIG_TLS), openssl)
 NEED_AES_DEC=y
@@ -904,7 +894,6 @@
 endif
 endif
 
-ifdef NEED_SHA256
 L_CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), gnutls)
@@ -918,6 +907,9 @@
 ifdef NEED_TLS_PRF_SHA256
 OBJS += src/crypto/sha256-tlsprf.c
 endif
+ifdef NEED_TLS_PRF_SHA384
+OBJS += src/crypto/sha384-tlsprf.c
+endif
 ifdef NEED_HMAC_SHA256_KDF
 OBJS += src/crypto/sha256-kdf.c
 endif
@@ -927,7 +919,6 @@
 ifdef NEED_HMAC_SHA512_KDF
 OBJS += src/crypto/sha512-kdf.c
 endif
-endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
 ifneq ($(CONFIG_TLS), openssl)
@@ -1128,6 +1119,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_PROPRIETARY_MODULE := true
 LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_VINTF_FRAGMENTS := android.hardware.wifi.hostapd.xml
 ifdef CONFIG_DRIVER_CUSTOM
 LOCAL_STATIC_LIBRARIES := libCustomWifi
 endif
@@ -1145,7 +1137,8 @@
 ifeq ($(HOSTAPD_USE_HIDL), y)
 LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd@1.0
 LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd@1.1
-LOCAL_SHARED_LIBRARIES += libbase libhidlbase libhidltransport libhwbinder libutils
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd@1.2
+LOCAL_SHARED_LIBRARIES += libbase libhidlbase libutils
 LOCAL_STATIC_LIBRARIES += libhostapd_hidl
 endif
 LOCAL_CFLAGS := $(L_CFLAGS)
@@ -1187,17 +1180,16 @@
 LOCAL_CPPFLAGS := $(L_CPPFLAGS)
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_C_INCLUDES := $(INCLUDES)
-HIDL_INTERFACE_VERSION = 1.1
+HIDL_INTERFACE_VERSION = 1.2
 LOCAL_SRC_FILES := \
     hidl/$(HIDL_INTERFACE_VERSION)/hidl.cpp \
     hidl/$(HIDL_INTERFACE_VERSION)/hostapd.cpp
 LOCAL_SHARED_LIBRARIES := \
     android.hardware.wifi.hostapd@1.0 \
     android.hardware.wifi.hostapd@1.1 \
+    android.hardware.wifi.hostapd@1.2 \
     libbase \
     libhidlbase \
-    libhidltransport \
-    libhwbinder \
     libutils \
     liblog
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index f1366b4..6c4410e 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,84 @@
 ChangeLog for hostapd
 
+2019-08-07 - v2.9
+	* SAE changes
+	  - disable use of groups using Brainpool curves
+	  - improved protection against side channel attacks
+	  [https://w1.fi/security/2019-6/]
+	* EAP-pwd changes
+	  - disable use of groups using Brainpool curves
+	  - improved protection against side channel attacks
+	  [https://w1.fi/security/2019-6/]
+	* fixed FT-EAP initial mobility domain association using PMKSA caching
+	* added configuration of airtime policy
+	* fixed FILS to and RSNE into (Re)Association Response frames
+	* fixed DPP bootstrapping URI parser of channel list
+	* added support for regulatory WMM limitation (for ETSI)
+	* added support for MACsec Key Agreement using IEEE 802.1X/PSK
+	* added experimental support for EAP-TEAP server (RFC 7170)
+	* added experimental support for EAP-TLS server with TLS v1.3
+	* added support for two server certificates/keys (RSA/ECC)
+	* added AKMSuiteSelector into "STA <addr>" control interface data to
+	  determine with AKM was used for an association
+	* added eap_sim_id parameter to allow EAP-SIM/AKA server pseudonym and
+	  fast reauthentication use to be disabled
+	* fixed an ECDH operation corner case with OpenSSL
+
+2019-04-21 - v2.8
+	* SAE changes
+	  - added support for SAE Password Identifier
+	  - changed default configuration to enable only group 19
+	    (i.e., disable groups 20, 21, 25, 26 from default configuration) and
+	    disable all unsuitable groups completely based on REVmd changes
+	  - improved anti-clogging token mechanism and SAE authentication
+	    frame processing during heavy CPU load; this mitigates some issues
+	    with potential DoS attacks trying to flood an AP with large number
+	    of SAE messages
+	  - added Finite Cyclic Group field in status code 77 responses
+	  - reject use of unsuitable groups based on new implementation guidance
+	    in REVmd (allow only FFC groups with prime >= 3072 bits and ECC
+	    groups with prime >= 256)
+	  - minimize timing and memory use differences in PWE derivation
+	    [https://w1.fi/security/2019-1/] (CVE-2019-9494)
+	  - fixed confirm message validation in error cases
+	    [https://w1.fi/security/2019-3/] (CVE-2019-9496)
+	* EAP-pwd changes
+	  - minimize timing and memory use differences in PWE derivation
+	    [https://w1.fi/security/2019-2/] (CVE-2019-9495)
+	  - verify peer scalar/element
+	    [https://w1.fi/security/2019-4/] (CVE-2019-9497 and CVE-2019-9498)
+	  - fix message reassembly issue with unexpected fragment
+	    [https://w1.fi/security/2019-5/]
+	  - enforce rand,mask generation rules more strictly
+	  - fix a memory leak in PWE derivation
+	  - disallow ECC groups with a prime under 256 bits (groups 25, 26, and
+	    27)
+	* Hotspot 2.0 changes
+	  - added support for release number 3
+	  - reject release 2 or newer association without PMF
+	* added support for RSN operating channel validation
+	  (CONFIG_OCV=y and configuration parameter ocv=1)
+	* added Multi-AP protocol support
+	* added FTM responder configuration
+	* fixed build with LibreSSL
+	* added FT/RRB workaround for short Ethernet frame padding
+	* fixed KEK2 derivation for FILS+FT
+	* added RSSI-based association rejection from OCE
+	* extended beacon reporting functionality
+	* VLAN changes
+	  - allow local VLAN management with remote RADIUS authentication
+	  - add WPA/WPA2 passphrase/PSK -based VLAN assignment
+	* OpenSSL: allow systemwide policies to be overridden
+	* extended PEAP to derive EMSK to enable use with ERP/FILS
+	* extended WPS to allow SAE configuration to be added automatically
+	  for PSK (wps_cred_add_sae=1)
+	* fixed FT and SA Query Action frame with AP-MLME-in-driver cases
+	* OWE: allow Diffie-Hellman Parameter element to be included with DPP
+	  in preparation for DPP protocol extension
+	* RADIUS server: started to accept ERP keyName-NAI as user identity
+	  automatically without matching EAP database entry
+	* fixed PTK rekeying with FILS and FT
+
 2018-12-02 - v2.7
 	* fixed WPA packet number reuse with replayed messages and key
 	  reinstallation
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 6e263c5..f01f581 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -121,6 +121,7 @@
 LIBS += -lbfd -ldl -liberty -lz
 LIBS_c += -lbfd -ldl -liberty -lz
 LIBS_h += -lbfd -ldl -liberty -lz
+LIBS_n += -lbfd -ldl -liberty -lz
 endif
 endif
 
@@ -248,18 +249,12 @@
 CFLAGS += -DCONFIG_CTRL_IFACE
 endif
 
-ifdef CONFIG_IAPP
-CFLAGS += -DCONFIG_IAPP
-OBJS += ../src/ap/iapp.o
-endif
-
 ifdef CONFIG_RSN_PREAUTH
 CFLAGS += -DCONFIG_RSN_PREAUTH
 CONFIG_L2_PACKET=y
 endif
 
 ifdef CONFIG_HS20
-NEED_AES_OMAC1=y
 CONFIG_PROXYARP=y
 endif
 
@@ -269,8 +264,6 @@
 
 ifdef CONFIG_SUITEB
 CFLAGS += -DCONFIG_SUITEB
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_SUITEB192
@@ -281,24 +274,14 @@
 ifdef CONFIG_OCV
 CFLAGS += -DCONFIG_OCV
 OBJS += ../src/common/ocv.o
-CONFIG_IEEE80211W=y
-endif
-
-ifdef CONFIG_IEEE80211W
-CFLAGS += -DCONFIG_IEEE80211W
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=y
 NEED_AES_SIV=y
 NEED_ETH_P_OUI=y
-NEED_SHA256=y
 NEED_HMAC_SHA256_KDF=y
 endif
 
@@ -313,6 +296,7 @@
 NEED_ECC=y
 NEED_DH_GROUPS=y
 NEED_AP_MLME=y
+NEED_DRAGONFLY=y
 endif
 
 ifdef CONFIG_OWE
@@ -321,11 +305,19 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
 endif
 
+ifdef CONFIG_WAPI_INTERFACE
+L_CFLAGS += -DCONFIG_WAPI_INTERFACE
+endif
+
+ifdef CONFIG_AIRTIME_POLICY
+CFLAGS += -DCONFIG_AIRTIME_POLICY
+OBJS += ../src/ap/airtime_policy.o
+endif
+
 ifdef CONFIG_FILS
 CFLAGS += -DCONFIG_FILS
 OBJS += ../src/ap/fils_hlp.o
@@ -385,7 +377,6 @@
 
 ifdef CONFIG_ERP
 CFLAGS += -DCONFIG_ERP
-NEED_SHA256=y
 NEED_HMAC_SHA256_KDF=y
 endif
 
@@ -446,7 +437,6 @@
 CFLAGS += -DEAP_SERVER_AKA
 OBJS += ../src/eap_server/eap_server_aka.o
 CONFIG_EAP_SIM_COMMON=y
-NEED_SHA256=y
 NEED_AES_CBC=y
 endif
 
@@ -471,7 +461,6 @@
 ifdef CONFIG_EAP_PSK
 CFLAGS += -DEAP_SERVER_PSK
 OBJS += ../src/eap_server/eap_server_psk.o ../src/eap_common/eap_psk_common.o
-NEED_AES_OMAC1=y
 NEED_AES_ENCBLOCK=y
 NEED_AES_EAX=y
 endif
@@ -487,15 +476,13 @@
 ifdef CONFIG_EAP_GPSK_SHA256
 CFLAGS += -DEAP_GPSK_SHA256
 endif
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_EAP_PWD
 CFLAGS += -DEAP_SERVER_PWD
 OBJS += ../src/eap_server/eap_server_pwd.o ../src/eap_common/eap_pwd_common.o
-NEED_SHA256=y
 NEED_ECC=y
+NEED_DRAGONFLY=y
 endif
 
 ifdef CONFIG_EAP_EKE
@@ -519,6 +506,18 @@
 NEED_AES_UNWRAP=y
 endif
 
+ifdef CONFIG_EAP_TEAP
+CFLAGS += -DEAP_SERVER_TEAP
+OBJS += ../src/eap_server/eap_server_teap.o
+OBJS += ../src/eap_common/eap_teap_common.o
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_TLS_PRF_SHA256=y
+NEED_TLS_PRF_SHA384=y
+NEED_AES_UNWRAP=y
+endif
+
 ifdef CONFIG_WPS
 CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
 OBJS += ../src/utils/uuid.o
@@ -533,7 +532,6 @@
 OBJS += ../src/wps/wps_enrollee.o
 OBJS += ../src/wps/wps_registrar.o
 NEED_DH_GROUPS=y
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
@@ -582,9 +580,9 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
+NEED_ECC=y
 NEED_JSON=y
 NEED_GAS=y
 NEED_BASE64=y
@@ -613,6 +611,15 @@
 endif
 endif
 
+ifdef CONFIG_MACSEC
+CFLAGS += -DCONFIG_MACSEC
+OBJS += ../src/ap/wpa_auth_kay.o
+OBJS += ../src/pae/ieee802_1x_cp.o
+OBJS += ../src/pae/ieee802_1x_kay.o
+OBJS += ../src/pae/ieee802_1x_key.o
+OBJS += ../src/pae/ieee802_1x_secy_ops.o
+endif
+
 # Basic EAP functionality is needed for EAPOL
 OBJS += eap_register.o
 OBJS += ../src/eap_server/eap_server.o
@@ -629,6 +636,10 @@
 CFLAGS += -DPKCS12_FUNCS
 endif
 
+ifdef NEED_DRAGONFLY
+OBJS += ../src/common/dragonfly.o
+endif
+
 ifdef MS_FUNCS
 OBJS += ../src/crypto/ms_funcs.o
 NEED_DES=y
@@ -657,7 +668,6 @@
 
 ifdef CONFIG_TLSV12
 CFLAGS += -DCONFIG_TLSV12
-NEED_SHA256=y
 endif
 
 ifeq ($(CONFIG_TLS), wolfssl)
@@ -671,7 +681,6 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_wolfssl.o
 endif
-NEED_SHA256=y
 NEED_TLS_PRF_SHA256=y
 LIBS += -lwolfssl -lm
 LIBS_h += -lwolfssl -lm
@@ -693,7 +702,6 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_openssl.o
 endif
-NEED_SHA256=y
 NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_h += -lcrypto
@@ -757,7 +765,6 @@
 OBJS += ../src/tls/pkcs1.o
 OBJS += ../src/tls/pkcs5.o
 OBJS += ../src/tls/pkcs8.o
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
 ifdef CONFIG_TLSV12
@@ -834,7 +841,6 @@
 OBJS += ../src/tls/pkcs1.o
 OBJS += ../src/tls/pkcs5.o
 OBJS += ../src/tls/pkcs8.o
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
 ifdef CONFIG_TLSV12
@@ -895,12 +901,10 @@
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_SIV
 AESOBJS += ../src/crypto/aes-siv.o
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += ../src/crypto/aes-ctr.o
@@ -908,13 +912,11 @@
 ifdef NEED_AES_ENCBLOCK
 AESOBJS += ../src/crypto/aes-encblock.o
 endif
-ifdef NEED_AES_OMAC1
 ifneq ($(CONFIG_TLS), linux)
 ifneq ($(CONFIG_TLS), wolfssl)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 endif
-endif
 ifdef NEED_AES_UNWRAP
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
@@ -1020,7 +1022,6 @@
 endif
 endif
 
-ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
@@ -1038,6 +1039,9 @@
 ifdef NEED_TLS_PRF_SHA256
 OBJS += ../src/crypto/sha256-tlsprf.o
 endif
+ifdef NEED_TLS_PRF_SHA384
+OBJS += ../src/crypto/sha384-tlsprf.o
+endif
 ifdef NEED_HMAC_SHA256_KDF
 OBJS += ../src/crypto/sha256-kdf.o
 endif
@@ -1047,7 +1051,6 @@
 ifdef NEED_HMAC_SHA512_KDF
 OBJS += ../src/crypto/sha512-kdf.o
 endif
-endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
 ifneq ($(CONFIG_TLS), openssl)
@@ -1315,7 +1318,6 @@
 NOBJS += ../src/utils/wpabuf.o
 ifdef CONFIG_WPA_TRACE
 NOBJS += ../src/utils/trace.o
-LIBS_n += -lbfd
 endif
 
 HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
diff --git a/hostapd/android.config b/hostapd/android.config
index 4502a60..68fec32 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -38,18 +38,9 @@
 # Driver interface for no driver (e.g., RADIUS server only)
 #CONFIG_DRIVER_NONE=y
 
-# IEEE 802.11F/IAPP
-#CONFIG_IAPP=y
-
 # WPA2/IEEE 802.11i RSN pre-authentication
 #CONFIG_RSN_PREAUTH=y
 
-# IEEE 802.11w (management frame protection)
-# This version is an experimental implementation based on IEEE 802.11w/D1.0
-# draft and is subject to change since the standard has not yet been finalized.
-# Driver support is also needed for IEEE 802.11w.
-CONFIG_IEEE80211W=y
-
 # Support Operating Channel Validation
 #CONFIG_OCV=y
 
@@ -210,6 +201,11 @@
 # Experimental implementation of draft-harkins-owe-07.txt
 #CONFIG_OWE=y
 
+# WLAN Authentication and Privacy Infrastructure (WAPI): interface only.
+# Configure the building of the interface which allows WAPI configuration.
+# Note: does not configure WAPI implementation itself.
+#CONFIG_WAPI_INTERFACE=y
+
 # Wpa_supplicant's random pool is not necessary on Android. Randomness is
 # already provided by the entropymixer service which ensures sufficient
 # entropy is maintained across reboots. Commit b410eb1913 'Initialize
diff --git a/hostapd/android.hardware.wifi.hostapd.xml b/hostapd/android.hardware.wifi.hostapd.xml
new file mode 100644
index 0000000..4dc1701
--- /dev/null
+++ b/hostapd/android.hardware.wifi.hostapd.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.wifi.hostapd</name>
+        <transport>hwbinder</transport>
+        <version>1.2</version>
+        <interface>
+            <name>IHostapd</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 42f3b40..21c9ab2 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -24,14 +24,6 @@
 #include "config_file.h"
 
 
-#ifndef CONFIG_NO_RADIUS
-#ifdef EAP_SERVER
-static struct hostapd_radius_attr *
-hostapd_parse_radius_attr(const char *value);
-#endif /* EAP_SERVER */
-#endif /* CONFIG_NO_RADIUS */
-
-
 #ifndef CONFIG_NO_VLAN
 static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
 					 const char *fname)
@@ -660,75 +652,6 @@
 }
 
 
-static struct hostapd_radius_attr *
-hostapd_parse_radius_attr(const char *value)
-{
-	const char *pos;
-	char syntax;
-	struct hostapd_radius_attr *attr;
-	size_t len;
-
-	attr = os_zalloc(sizeof(*attr));
-	if (attr == NULL)
-		return NULL;
-
-	attr->type = atoi(value);
-
-	pos = os_strchr(value, ':');
-	if (pos == NULL) {
-		attr->val = wpabuf_alloc(1);
-		if (attr->val == NULL) {
-			os_free(attr);
-			return NULL;
-		}
-		wpabuf_put_u8(attr->val, 0);
-		return attr;
-	}
-
-	pos++;
-	if (pos[0] == '\0' || pos[1] != ':') {
-		os_free(attr);
-		return NULL;
-	}
-	syntax = *pos++;
-	pos++;
-
-	switch (syntax) {
-	case 's':
-		attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
-		break;
-	case 'x':
-		len = os_strlen(pos);
-		if (len & 1)
-			break;
-		len /= 2;
-		attr->val = wpabuf_alloc(len);
-		if (attr->val == NULL)
-			break;
-		if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
-			wpabuf_free(attr->val);
-			os_free(attr);
-			return NULL;
-		}
-		break;
-	case 'd':
-		attr->val = wpabuf_alloc(4);
-		if (attr->val)
-			wpabuf_put_be32(attr->val, atoi(pos));
-		break;
-	default:
-		os_free(attr);
-		return NULL;
-	}
-
-	if (attr->val == NULL) {
-		os_free(attr);
-		return NULL;
-	}
-
-	return attr;
-}
-
 
 static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val)
 {
@@ -788,12 +711,10 @@
 			val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
 #endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
 		else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
 			val |= WPA_KEY_MGMT_PSK_SHA256;
 		else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_SAE
 		else if (os_strcmp(start, "SAE") == 0)
 			val |= WPA_KEY_MGMT_SAE;
@@ -2313,6 +2234,42 @@
 #endif /* EAP_SERVER */
 
 
+#ifdef CONFIG_AIRTIME_POLICY
+static int add_airtime_weight(struct hostapd_bss_config *bss, char *value)
+{
+	struct airtime_sta_weight *wt;
+	char *pos, *next;
+
+	wt = os_zalloc(sizeof(*wt));
+	if (!wt)
+		return -1;
+
+	/* 02:01:02:03:04:05 10 */
+	pos = value;
+	next = os_strchr(pos, ' ');
+	if (next)
+		*next++ = '\0';
+	if (!next || hwaddr_aton(pos, wt->addr)) {
+		wpa_printf(MSG_ERROR, "Invalid station address: '%s'", pos);
+		os_free(wt);
+		return -1;
+	}
+
+	pos = next;
+	wt->weight = atoi(pos);
+	if (!wt->weight) {
+		wpa_printf(MSG_ERROR, "Invalid weight: '%s'", pos);
+		os_free(wt);
+		return -1;
+	}
+
+	wt->next = bss->airtime_weight_list;
+	bss->airtime_weight_list = wt;
+	return 0;
+}
+#endif /* CONFIG_AIRTIME_POLICY */
+
+
 #ifdef CONFIG_SAE
 static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
 {
@@ -2376,6 +2333,36 @@
 #endif /* CONFIG_SAE */
 
 
+#ifdef CONFIG_DPP2
+static int hostapd_dpp_controller_parse(struct hostapd_bss_config *bss,
+					const char *pos)
+{
+	struct dpp_controller_conf *conf;
+	char *val;
+
+	conf = os_zalloc(sizeof(*conf));
+	if (!conf)
+		return -1;
+	val = get_param(pos, "ipaddr=");
+	if (!val || hostapd_parse_ip_addr(val, &conf->ipaddr))
+		goto fail;
+	os_free(val);
+	val = get_param(pos, "pkhash=");
+	if (!val || os_strlen(val) != 2 * SHA256_MAC_LEN ||
+	    hexstr2bin(val, conf->pkhash, SHA256_MAC_LEN) < 0)
+		goto fail;
+	os_free(val);
+	conf->next = bss->dpp_controller;
+	bss->dpp_controller = conf;
+	return 0;
+fail:
+	os_free(val);
+	os_free(conf);
+	return -1;
+}
+#endif /* CONFIG_DPP2 */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
 			       const char *buf, char *pos, int line)
@@ -2496,7 +2483,11 @@
 	} else if (os_strcmp(buf, "eapol_version") == 0) {
 		int eapol_version = atoi(pos);
 
+#ifdef CONFIG_MACSEC
+		if (eapol_version < 1 || eapol_version > 3) {
+#else /* CONFIG_MACSEC */
 		if (eapol_version < 1 || eapol_version > 2) {
+#endif /* CONFIG_MACSEC */
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid EAPOL version (%d): '%s'.",
 				   line, eapol_version, pos);
@@ -2519,12 +2510,21 @@
 	} else if (os_strcmp(buf, "server_cert") == 0) {
 		os_free(bss->server_cert);
 		bss->server_cert = os_strdup(pos);
+	} else if (os_strcmp(buf, "server_cert2") == 0) {
+		os_free(bss->server_cert2);
+		bss->server_cert2 = os_strdup(pos);
 	} else if (os_strcmp(buf, "private_key") == 0) {
 		os_free(bss->private_key);
 		bss->private_key = os_strdup(pos);
+	} else if (os_strcmp(buf, "private_key2") == 0) {
+		os_free(bss->private_key2);
+		bss->private_key2 = os_strdup(pos);
 	} else if (os_strcmp(buf, "private_key_passwd") == 0) {
 		os_free(bss->private_key_passwd);
 		bss->private_key_passwd = os_strdup(pos);
+	} else if (os_strcmp(buf, "private_key_passwd2") == 0) {
+		os_free(bss->private_key_passwd2);
+		bss->private_key_passwd2 = os_strdup(pos);
 	} else if (os_strcmp(buf, "check_cert_subject") == 0) {
 		if (!pos[0]) {
 			wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'",
@@ -2545,6 +2545,10 @@
 		bss->tls_session_lifetime = atoi(pos);
 	} else if (os_strcmp(buf, "tls_flags") == 0) {
 		bss->tls_flags = parse_tls_flags(pos);
+	} else if (os_strcmp(buf, "max_auth_rounds") == 0) {
+		bss->max_auth_rounds = atoi(pos);
+	} else if (os_strcmp(buf, "max_auth_rounds_short") == 0) {
+		bss->max_auth_rounds_short = atoi(pos);
 	} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
 		os_free(bss->ocsp_stapling_response);
 		bss->ocsp_stapling_response = os_strdup(pos);
@@ -2605,6 +2609,24 @@
 	} else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
 		bss->pac_key_refresh_time = atoi(pos);
 #endif /* EAP_SERVER_FAST */
+#ifdef EAP_SERVER_TEAP
+	} else if (os_strcmp(buf, "eap_teap_auth") == 0) {
+		int val = atoi(pos);
+
+		if (val < 0 || val > 1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid eap_teap_auth value",
+				   line);
+			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) {
+		bss->eap_teap_id = atoi(pos);
+#endif /* EAP_SERVER_TEAP */
 #ifdef EAP_SERVER_SIM
 	} else if (os_strcmp(buf, "eap_sim_db") == 0) {
 		os_free(bss->eap_sim_db);
@@ -2613,6 +2635,8 @@
 		bss->eap_sim_db_timeout = atoi(pos);
 	} else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
 		bss->eap_sim_aka_result_ind = atoi(pos);
+	} else if (os_strcmp(buf, "eap_sim_id") == 0) {
+		bss->eap_sim_id = atoi(pos);
 #endif /* EAP_SERVER_SIM */
 #ifdef EAP_SERVER_TNC
 	} else if (os_strcmp(buf, "tnc") == 0) {
@@ -2688,8 +2712,7 @@
 		bss->eapol_key_index_workaround = atoi(pos);
 #ifdef CONFIG_IAPP
 	} else if (os_strcmp(buf, "iapp_interface") == 0) {
-		bss->ieee802_11f = 1;
-		os_strlcpy(bss->iapp_iface, pos, sizeof(bss->iapp_iface));
+		wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
 #endif /* CONFIG_IAPP */
 	} else if (os_strcmp(buf, "own_ip_addr") == 0) {
 		if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
@@ -2816,6 +2839,9 @@
 				a = a->next;
 			a->next = attr;
 		}
+	} else if (os_strcmp(buf, "radius_req_attr_sqlite") == 0) {
+		os_free(bss->radius_req_attr_sqlite);
+		bss->radius_req_attr_sqlite = os_strdup(pos);
 	} else if (os_strcmp(buf, "radius_das_port") == 0) {
 		bss->radius_das_port = atoi(pos);
 	} else if (os_strcmp(buf, "radius_das_client") == 0) {
@@ -3110,6 +3136,8 @@
 		}
 	} else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
 		conf->acs_exclude_dfs = atoi(pos);
+	} else if (os_strcmp(buf, "op_class") == 0) {
+		conf->op_class = atoi(pos);
 	} else if (os_strcmp(buf, "channel") == 0) {
 		if (os_strcmp(pos, "acs_survey") == 0) {
 #ifndef CONFIG_ACS
@@ -3124,12 +3152,25 @@
 			conf->channel = atoi(pos);
 			conf->acs = conf->channel == 0;
 		}
+	} else if (os_strcmp(buf, "edmg_channel") == 0) {
+		conf->edmg_channel = atoi(pos);
+	} else if (os_strcmp(buf, "enable_edmg") == 0) {
+		conf->enable_edmg = atoi(pos);
 	} else if (os_strcmp(buf, "chanlist") == 0) {
 		if (hostapd_parse_chanlist(conf, pos)) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
 				   line);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "freqlist") == 0) {
+		if (freq_range_list_parse(&conf->acs_freq_list, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid frequency list",
+				   line);
+			return 1;
+		}
+		conf->acs_freq_list_present = 1;
+	} else if (os_strcmp(buf, "acs_exclude_6ghz_non_psc") == 0) {
+		conf->acs_exclude_6ghz_non_psc = atoi(pos);
 	} else if (os_strcmp(buf, "beacon_int") == 0) {
 		int val = atoi(pos);
 		/* MIB defines range as 1..65535, but very small values
@@ -3351,7 +3392,6 @@
 		}
 	} else if (os_strcmp(buf, "use_driver_iface_addr") == 0) {
 		conf->use_driver_iface_addr = atoi(pos);
-#ifdef CONFIG_IEEE80211W
 	} else if (os_strcmp(buf, "ieee80211w") == 0) {
 		bss->ieee80211w = atoi(pos);
 	} else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
@@ -3382,7 +3422,6 @@
 				   line);
 			return 1;
 		}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	} else if (os_strcmp(buf, "ocv") == 0) {
 		bss->ocv = atoi(pos);
@@ -3442,6 +3481,8 @@
 		conf->he_op.he_twt_required = atoi(pos);
 	} else if (os_strcmp(buf, "he_rts_threshold") == 0) {
 		conf->he_op.he_rts_threshold = atoi(pos);
+	} else if (os_strcmp(buf, "he_basic_mcs_nss_set") == 0) {
+		conf->he_op.he_basic_mcs_nss_set = atoi(pos);
 	} else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) {
 		conf->he_mu_edca.he_qos_info |=
 			set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT);
@@ -3526,6 +3567,20 @@
 	} else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) {
 		conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] =
 			atoi(pos) & 0xff;
+	} else if (os_strcmp(buf, "he_spr_sr_control") == 0) {
+		conf->spr.sr_control = atoi(pos) & 0xff;
+	} else if (os_strcmp(buf, "he_spr_non_srg_obss_pd_max_offset") == 0) {
+		conf->spr.non_srg_obss_pd_max_offset = atoi(pos);
+	} else if (os_strcmp(buf, "he_spr_srg_obss_pd_min_offset") == 0) {
+		conf->spr.srg_obss_pd_min_offset = atoi(pos);
+	} else if (os_strcmp(buf, "he_spr_srg_obss_pd_max_offset") == 0) {
+		conf->spr.srg_obss_pd_max_offset = atoi(pos);
+	} else if (os_strcmp(buf, "he_oper_chwidth") == 0) {
+		conf->he_oper_chwidth = atoi(pos);
+	} else if (os_strcmp(buf, "he_oper_centr_freq_seg0_idx") == 0) {
+		conf->he_oper_centr_freq_seg0_idx = atoi(pos);
+	} else if (os_strcmp(buf, "he_oper_centr_freq_seg1_idx") == 0) {
+		conf->he_oper_centr_freq_seg1_idx = atoi(pos);
 #endif /* CONFIG_IEEE80211AX */
 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 		bss->max_listen_interval = atoi(pos);
@@ -4110,6 +4165,9 @@
 	} else if (os_strcmp(buf, "sae_commit_override") == 0) {
 		wpabuf_free(bss->sae_commit_override);
 		bss->sae_commit_override = wpabuf_parse_bin(pos);
+	} else if (os_strcmp(buf, "rsnxe_override_eapol") == 0) {
+		wpabuf_free(bss->rsnxe_override_eapol);
+		bss->rsnxe_override_eapol = wpabuf_parse_bin(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_SAE
 	} else if (os_strcmp(buf, "sae_password") == 0) {
@@ -4138,6 +4196,10 @@
 		}
 	} else if (os_strcmp(buf, "sae_require_mfp") == 0) {
 		bss->sae_require_mfp = atoi(pos);
+	} else if (os_strcmp(buf, "sae_confirm_immediate") == 0) {
+		bss->sae_confirm_immediate = atoi(pos);
+	} else if (os_strcmp(buf, "sae_pwe") == 0) {
+		bss->sae_pwe = atoi(pos);
 	} else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
 		int val = atoi(pos);
 		if (val < 0 || val > 255) {
@@ -4287,6 +4349,12 @@
 	} else if (os_strcmp(buf, "broadcast_deauth") == 0) {
 		bss->broadcast_deauth = atoi(pos);
 #ifdef CONFIG_DPP
+	} else if (os_strcmp(buf, "dpp_name") == 0) {
+		os_free(bss->dpp_name);
+		bss->dpp_name = os_strdup(pos);
+	} else if (os_strcmp(buf, "dpp_mud_url") == 0) {
+		os_free(bss->dpp_mud_url);
+		bss->dpp_mud_url = os_strdup(pos);
 	} else if (os_strcmp(buf, "dpp_connector") == 0) {
 		os_free(bss->dpp_connector);
 		bss->dpp_connector = os_strdup(pos);
@@ -4298,6 +4366,11 @@
 	} else if (os_strcmp(buf, "dpp_csign") == 0) {
 		if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos))
 			return 1;
+#ifdef CONFIG_DPP2
+	} else if (os_strcmp(buf, "dpp_controller") == 0) {
+		if (hostapd_dpp_controller_parse(bss, pos))
+			return 1;
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 #ifdef CONFIG_OWE
 	} else if (os_strcmp(buf, "owe_transition_bssid") == 0) {
@@ -4349,6 +4422,121 @@
 		conf->rssi_reject_assoc_timeout = atoi(pos);
 	} else if (os_strcmp(buf, "pbss") == 0) {
 		bss->pbss = atoi(pos);
+#ifdef CONFIG_AIRTIME_POLICY
+	} else if (os_strcmp(buf, "airtime_mode") == 0) {
+		int val = atoi(pos);
+
+		if (val < 0 || val > AIRTIME_MODE_MAX) {
+			wpa_printf(MSG_ERROR, "Line %d: Unknown airtime_mode",
+				   line);
+			return 1;
+		}
+		conf->airtime_mode = val;
+	} else if (os_strcmp(buf, "airtime_update_interval") == 0) {
+		conf->airtime_update_interval = atoi(pos);
+	} else if (os_strcmp(buf, "airtime_bss_weight") == 0) {
+		bss->airtime_weight = atoi(pos);
+	} else if (os_strcmp(buf, "airtime_bss_limit") == 0) {
+		int val = atoi(pos);
+
+		if (val < 0 || val > 1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid airtime_bss_limit (must be 0 or 1)",
+				   line);
+			return 1;
+		}
+		bss->airtime_limit = val;
+	} else if (os_strcmp(buf, "airtime_sta_weight") == 0) {
+		if (add_airtime_weight(bss, pos) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid airtime weight '%s'",
+				   line, pos);
+			return 1;
+		}
+#endif /* CONFIG_AIRTIME_POLICY */
+#ifdef CONFIG_MACSEC
+	} else if (os_strcmp(buf, "macsec_policy") == 0) {
+		int macsec_policy = atoi(pos);
+
+		if (macsec_policy < 0 || macsec_policy > 1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid macsec_policy (%d): '%s'.",
+				   line, macsec_policy, pos);
+			return 1;
+		}
+		bss->macsec_policy = macsec_policy;
+	} else if (os_strcmp(buf, "macsec_integ_only") == 0) {
+		int macsec_integ_only = atoi(pos);
+
+		if (macsec_integ_only < 0 || macsec_integ_only > 1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid macsec_integ_only (%d): '%s'.",
+				   line, macsec_integ_only, pos);
+			return 1;
+		}
+		bss->macsec_integ_only = macsec_integ_only;
+	} else if (os_strcmp(buf, "macsec_replay_protect") == 0) {
+		int macsec_replay_protect = atoi(pos);
+
+		if (macsec_replay_protect < 0 || macsec_replay_protect > 1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid macsec_replay_protect (%d): '%s'.",
+				   line, macsec_replay_protect, pos);
+			return 1;
+		}
+		bss->macsec_replay_protect = macsec_replay_protect;
+	} else if (os_strcmp(buf, "macsec_replay_window") == 0) {
+		bss->macsec_replay_window = atoi(pos);
+	} else if (os_strcmp(buf, "macsec_port") == 0) {
+		int macsec_port = atoi(pos);
+
+		if (macsec_port < 1 || macsec_port > 65534) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid macsec_port (%d): '%s'.",
+				   line, macsec_port, pos);
+			return 1;
+		}
+		bss->macsec_port = macsec_port;
+	} else if (os_strcmp(buf, "mka_priority") == 0) {
+		int mka_priority = atoi(pos);
+
+		if (mka_priority < 0 || mka_priority > 255) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid mka_priority (%d): '%s'.",
+				   line, mka_priority, pos);
+			return 1;
+		}
+		bss->mka_priority = mka_priority;
+	} else if (os_strcmp(buf, "mka_cak") == 0) {
+		size_t len = os_strlen(pos);
+
+		if (len > 2 * MACSEC_CAK_MAX_LEN ||
+		    (len != 2 * 16 && len != 2 * 32) ||
+		    hexstr2bin(pos, bss->mka_cak, len / 2)) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
+				   line, pos);
+			return 1;
+		}
+		bss->mka_cak_len = len / 2;
+		bss->mka_psk_set |= MKA_PSK_SET_CAK;
+	} else if (os_strcmp(buf, "mka_ckn") == 0) {
+		size_t len = os_strlen(pos);
+
+		if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */
+		    len < 2 || /* too short */
+		    len % 2 != 0 /* not an integral number of bytes */) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
+				   line, pos);
+			return 1;
+		}
+		bss->mka_ckn_len = len / 2;
+		if (hexstr2bin(pos, bss->mka_ckn, bss->mka_ckn_len)) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
+				   line, pos);
+			return -1;
+		}
+		bss->mka_psk_set |= MKA_PSK_SET_CKN;
+#endif /* CONFIG_MACSEC */
 	} else {
 		wpa_printf(MSG_ERROR,
 			   "Line %d: unknown configuration item '%s'",
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index e4b16e6..9758881 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -130,7 +130,6 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
 #ifdef NEED_AP_MLME
 static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
 				       const char *txtaddr)
@@ -149,7 +148,6 @@
 	return 0;
 }
 #endif /* NEED_AP_MLME */
-#endif /* CONFIG_IEEE80211W */
 
 
 #ifdef CONFIG_WPS
@@ -1098,7 +1096,6 @@
 	}
 #endif /* CONFIG_FILS */
 #endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 		ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
 		if (os_snprintf_error(end - pos, ret))
@@ -1111,7 +1108,6 @@
 			return pos - buf;
 		pos += ret;
 	}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_SAE
 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
 		ret = os_snprintf(pos, end - pos, "SAE ");
@@ -1330,6 +1326,33 @@
 	}
 }
 
+
+static int hostapd_ctrl_iface_set_band(struct hostapd_data *hapd,
+				       const char *band)
+{
+	union wpa_event_data event;
+	enum set_band setband;
+
+	if (os_strcmp(band, "AUTO") == 0)
+		setband = WPA_SETBAND_AUTO;
+	else if (os_strcmp(band, "5G") == 0)
+		setband = WPA_SETBAND_5G;
+	else if (os_strcmp(band, "2G") == 0)
+		setband = WPA_SETBAND_2G;
+	else
+		return -1;
+
+	if (hostapd_drv_set_band(hapd, setband) == 0) {
+		os_memset(&event, 0, sizeof(event));
+		event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+		event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
+		wpa_supplicant_event(hapd, EVENT_CHANNEL_LIST_CHANGED, &event);
+	}
+
+	return 0;
+}
+
+
 static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
 {
 	char *value;
@@ -1413,6 +1436,8 @@
 		os_free(hapd->dpp_configurator_params);
 		hapd->dpp_configurator_params = os_strdup(value);
 #endif /* CONFIG_DPP */
+	} else if (os_strcasecmp(cmd, "setband") == 0) {
+		ret = hostapd_ctrl_iface_set_band(hapd, value);
 	} else {
 		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
 		if (ret)
@@ -1428,6 +1453,11 @@
 			if (ieee802_11_update_beacons(hapd->iface))
 				wpa_printf(MSG_DEBUG,
 					   "Failed to update beacons with WMM parameters");
+		} else if (os_strcmp(cmd, "wpa_passphrase") == 0 ||
+			   os_strcmp(cmd, "sae_password") == 0 ||
+			   os_strcmp(cmd, "sae_pwe") == 0) {
+			if (hapd->started)
+				hostapd_setup_sae_pt(hapd->conf);
 		}
 	}
 
@@ -1830,26 +1860,40 @@
 	struct iphdr ip;
 	const u8 *pos;
 	unsigned int i;
+	char extra[30];
 
-	if (len != HWSIM_PACKETLEN)
+	if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) {
+		wpa_printf(MSG_DEBUG,
+			   "test data: RX - ignore unexpected length %d",
+			   (int) len);
 		return;
+	}
 
 	eth = (const struct ether_header *) buf;
 	os_memcpy(&ip, eth + 1, sizeof(ip));
 	pos = &buf[sizeof(*eth) + sizeof(ip)];
 
 	if (ip.ihl != 5 || ip.version != 4 ||
-	    ntohs(ip.tot_len) != HWSIM_IP_LEN)
+	    ntohs(ip.tot_len) > HWSIM_IP_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "test data: RX - ignore unexpect IP header");
 		return;
+	}
 
-	for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
-		if (*pos != (u8) i)
+	for (i = 0; i < ntohs(ip.tot_len) - sizeof(ip); i++) {
+		if (*pos != (u8) i) {
+			wpa_printf(MSG_DEBUG,
+				   "test data: RX - ignore mismatching payload");
 			return;
+		}
 		pos++;
 	}
 
-	wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
-		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+	extra[0] = '\0';
+	if (ntohs(ip.tot_len) != HWSIM_IP_LEN)
+		os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.tot_len));
+	wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s",
+		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra);
 }
 
 
@@ -1894,7 +1938,7 @@
 static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
 {
 	u8 dst[ETH_ALEN], src[ETH_ALEN];
-	char *pos;
+	char *pos, *pos2;
 	int used;
 	long int val;
 	u8 tos;
@@ -1903,11 +1947,12 @@
 	struct iphdr *ip;
 	u8 *dpos;
 	unsigned int i;
+	size_t send_len = HWSIM_IP_LEN;
 
 	if (hapd->l2_test == NULL)
 		return -1;
 
-	/* format: <dst> <src> <tos> */
+	/* format: <dst> <src> <tos> [len=<length>] */
 
 	pos = cmd;
 	used = hwaddr_aton2(pos, dst);
@@ -1921,11 +1966,19 @@
 		return -1;
 	pos += used;
 
-	val = strtol(pos, NULL, 0);
+	val = strtol(pos, &pos2, 0);
 	if (val < 0 || val > 0xff)
 		return -1;
 	tos = val;
 
+	pos = os_strstr(pos2, " len=");
+	if (pos) {
+		i = atoi(pos + 5);
+		if (i < sizeof(*ip) || i > HWSIM_IP_LEN)
+			return -1;
+		send_len = i;
+	}
+
 	eth = (struct ether_header *) &buf[2];
 	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
 	os_memcpy(eth->ether_shost, src, ETH_ALEN);
@@ -1936,17 +1989,17 @@
 	ip->version = 4;
 	ip->ttl = 64;
 	ip->tos = tos;
-	ip->tot_len = htons(HWSIM_IP_LEN);
+	ip->tot_len = htons(send_len);
 	ip->protocol = 1;
 	ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
 	ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
 	ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
 	dpos = (u8 *) (ip + 1);
-	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
+	for (i = 0; i < send_len - sizeof(*ip); i++)
 		*dpos++ = i;
 
 	if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
-			   HWSIM_PACKETLEN) < 0)
+			   sizeof(struct ether_header) + send_len) < 0)
 		return -1;
 
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
@@ -2086,7 +2139,6 @@
 	if (hwaddr_aton(cmd, addr))
 		return -1;
 
-#ifdef CONFIG_IEEE80211W
 	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
 		if (hapd->last_igtk_alg == WPA_ALG_NONE)
 			return -1;
@@ -2110,7 +2162,6 @@
 					   hapd->last_igtk,
 					   hapd->last_igtk_len);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	if (is_broadcast_ether_addr(addr)) {
 		if (hapd->last_gtk_alg == WPA_ALG_NONE)
@@ -2656,6 +2707,20 @@
 }
 
 
+static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
+					    char *buf, size_t buflen)
+{
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: SHOW_NEIGHBOR: Neighbor report is not enabled");
+		return -1;
+	}
+
+	return hostapd_neighbor_show(hapd, buf, buflen);
+}
+
+
 static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
 {
 	struct wpa_ssid_value ssid;
@@ -2762,6 +2827,7 @@
 					      char *buf)
 {
 	struct wpa_ssid_value ssid;
+	struct wpa_ssid_value *ssidp = NULL;
 	u8 bssid[ETH_ALEN];
 	char *tmp;
 
@@ -2771,13 +2837,16 @@
 	}
 
 	tmp = os_strstr(buf, "ssid=");
-	if (!tmp || ssid_parse(tmp + 5, &ssid)) {
-		wpa_printf(MSG_ERROR,
-			   "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID");
-		return -1;
+	if (tmp) {
+		ssidp = &ssid;
+		if (ssid_parse(tmp + 5, &ssid)) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL: REMOVE_NEIGHBOR: Bad SSID");
+			return -1;
+		}
 	}
 
-	return hostapd_neighbor_remove(hapd, bssid, &ssid);
+	return hostapd_neighbor_remove(hapd, bssid, ssidp);
 }
 
 
@@ -3009,13 +3078,11 @@
 	} else if (os_strcmp(buf, "STOP_AP") == 0) {
 		if (hostapd_ctrl_iface_stop_ap(hapd))
 			reply_len = -1;
-#ifdef CONFIG_IEEE80211W
 #ifdef NEED_AP_MLME
 	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
 		if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
 			reply_len = -1;
 #endif /* NEED_AP_MLME */
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WPS
 	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
 		if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
@@ -3202,6 +3269,9 @@
 	} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
 		if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "SHOW_NEIGHBOR") == 0) {
+		reply_len = hostapd_ctrl_iface_show_neighbor(hapd, reply,
+							     reply_size);
 	} else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
 		if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
 			reply_len = -1;
@@ -3269,6 +3339,15 @@
 			if (os_snprintf_error(reply_size, reply_len))
 				reply_len = -1;
 		}
+	} else if (os_strncmp(buf, "DPP_NFC_URI ", 12) == 0) {
+		res = hostapd_dpp_nfc_uri(hapd, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
 		res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18);
 		if (res < 0) {
@@ -4344,6 +4423,8 @@
 		return -1;
 	}
 
+	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
 	return 0;
 
 fail:
@@ -4446,6 +4527,8 @@
 	eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
 				 interface, NULL);
 
+	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
 	return 0;
 
 fail:
@@ -4515,37 +4598,48 @@
 }
 
 
-static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
-				    enum wpa_msg_type type,
-				    const char *buf, size_t len)
+static void hostapd_ctrl_iface_send_internal(int sock, struct dl_list *ctrl_dst,
+					     const char *ifname, int level,
+					     const char *buf, size_t len)
 {
 	struct wpa_ctrl_dst *dst, *next;
-	struct dl_list *ctrl_dst;
 	struct msghdr msg;
-	int idx;
-	struct iovec io[2];
+	int idx, res;
+	struct iovec io[5];
 	char levelstr[10];
-	int s;
 
-	if (type != WPA_MSG_ONLY_GLOBAL) {
-		s = hapd->ctrl_sock;
-		ctrl_dst = &hapd->ctrl_dst;
-	} else {
-		s = hapd->iface->interfaces->global_ctrl_sock;
-		ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst;
-	}
-
-	if (s < 0 || dl_list_empty(ctrl_dst))
+	if (sock < 0 || dl_list_empty(ctrl_dst))
 		return;
 
-	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
-	io[0].iov_base = levelstr;
-	io[0].iov_len = os_strlen(levelstr);
-	io[1].iov_base = (char *) buf;
-	io[1].iov_len = len;
+	res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+	if (os_snprintf_error(sizeof(levelstr), res))
+		return;
+	idx = 0;
+	if (ifname) {
+#ifdef CONFIG_CTRL_IFACE_UDP
+		io[idx].iov_base = "IFACE=";
+		io[idx].iov_len = 6;
+#else /* CONFIG_CTRL_IFACE_UDP */
+		io[idx].iov_base = "IFNAME=";
+		io[idx].iov_len = 7;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+		idx++;
+		io[idx].iov_base = (char *) ifname;
+		io[idx].iov_len = os_strlen(ifname);
+		idx++;
+		io[idx].iov_base = " ";
+		io[idx].iov_len = 1;
+		idx++;
+	}
+	io[idx].iov_base = levelstr;
+	io[idx].iov_len = os_strlen(levelstr);
+	idx++;
+	io[idx].iov_base = (char *) buf;
+	io[idx].iov_len = len;
+	idx++;
 	os_memset(&msg, 0, sizeof(msg));
 	msg.msg_iov = io;
-	msg.msg_iovlen = 2;
+	msg.msg_iovlen = idx;
 
 	idx = 0;
 	dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
@@ -4555,22 +4649,16 @@
 				       &dst->addr, dst->addrlen);
 			msg.msg_name = &dst->addr;
 			msg.msg_namelen = dst->addrlen;
-			if (sendmsg(s, &msg, 0) < 0) {
+			if (sendmsg(sock, &msg, 0) < 0) {
 				int _errno = errno;
 				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
 					   "%d - %s",
 					   idx, errno, strerror(errno));
 				dst->errors++;
 				if (dst->errors > 10 || _errno == ENOENT) {
-					if (type != WPA_MSG_ONLY_GLOBAL)
-						hostapd_ctrl_iface_detach(
-							hapd, &dst->addr,
-							dst->addrlen);
-					else
-						hostapd_global_ctrl_iface_detach(
-							hapd->iface->interfaces,
-							&dst->addr,
-							dst->addrlen);
+					ctrl_iface_detach(ctrl_dst,
+							  &dst->addr,
+							  dst->addrlen);
 				}
 			} else
 				dst->errors = 0;
@@ -4579,4 +4667,25 @@
 	}
 }
 
+
+static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+				    enum wpa_msg_type type,
+				    const char *buf, size_t len)
+{
+	if (type != WPA_MSG_NO_GLOBAL) {
+		hostapd_ctrl_iface_send_internal(
+			hapd->iface->interfaces->global_ctrl_sock,
+			&hapd->iface->interfaces->global_ctrl_dst,
+			type != WPA_MSG_PER_INTERFACE ?
+			NULL : hapd->conf->iface,
+			level, buf, len);
+	}
+
+	if (type != WPA_MSG_ONLY_GLOBAL) {
+		hostapd_ctrl_iface_send_internal(
+			hapd->ctrl_sock, &hapd->ctrl_dst,
+			NULL, level, buf, len);
+	}
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/hostapd/defconfig b/hostapd/defconfig
index ea5e2c9..6e2eaa3 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -44,15 +44,9 @@
 # Driver interface for no driver (e.g., RADIUS server only)
 #CONFIG_DRIVER_NONE=y
 
-# IEEE 802.11F/IAPP
-CONFIG_IAPP=y
-
 # WPA2/IEEE 802.11i RSN pre-authentication
 CONFIG_RSN_PREAUTH=y
 
-# IEEE 802.11w (management frame protection)
-CONFIG_IEEE80211W=y
-
 # Support Operating Channel Validation
 #CONFIG_OCV=y
 
@@ -108,11 +102,18 @@
 #CONFIG_EAP_GPSK_SHA256=y
 
 # EAP-FAST for the integrated EAP server
-# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
-# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
-# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
 #CONFIG_EAP_FAST=y
 
+# EAP-TEAP for the integrated EAP server
+# Note: The current EAP-TEAP implementation is experimental and should not be
+# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number
+# of conflicting statements and missing details and the implementation has
+# vendor specific workarounds for those and as such, may not interoperate with
+# any other implementation. This should not be used for anything else than
+# experimentation and interoperability testing until those issues has been
+# resolved.
+#CONFIG_EAP_TEAP=y
+
 # Wi-Fi Protected Setup (WPS)
 #CONFIG_WPS=y
 # Enable UPnP support for external WPS Registrars
@@ -376,6 +377,14 @@
 # Experimental implementation of draft-harkins-owe-07.txt
 #CONFIG_OWE=y
 
+# WLAN Authentication and Privacy Infrastructure (WAPI): interface only.
+# Configure the building of the interface which allows WAPI configuration.
+# Note: does not configure WAPI implementation itself.
+#CONFIG_WAPI_INTERFACE=y
+
+# Airtime policy support
+#CONFIG_AIRTIME_POLICY=y
+
 # Override default value for the wpa_disable_eapol_key_retries configuration
 # parameter. See that parameter in hostapd.conf for more details.
 #CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c
index 8477c21..3e870c7 100644
--- a/hostapd/eap_register.c
+++ b/hostapd/eap_register.c
@@ -121,6 +121,11 @@
 		ret = eap_server_fast_register();
 #endif /* EAP_SERVER_FAST */
 
+#ifdef EAP_SERVER_TEAP
+	if (ret == 0)
+		ret = eap_server_teap_register();
+#endif /* EAP_SERVER_TEAP */
+
 #ifdef EAP_SERVER_WSC
 	if (ret == 0)
 		ret = eap_server_wsc_register();
diff --git a/hostapd/hidl/1.1/hostapd.cpp b/hostapd/hidl/1.1/hostapd.cpp
deleted file mode 100644
index 0298537..0000000
--- a/hostapd/hidl/1.1/hostapd.cpp
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * hidl interface for wpa_hostapd daemon
- * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-#include <iomanip>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-
-#include "hostapd.h"
-#include "hidl_return_util.h"
-
-extern "C"
-{
-#include "utils/eloop.h"
-}
-
-// The HIDL implementation for hostapd creates a hostapd.conf dynamically for
-// each interface. This file can then be used to hook onto the normal config
-// file parsing logic in hostapd code.  Helps us to avoid duplication of code
-// in the HIDL interface.
-// TOOD(b/71872409): Add unit tests for this.
-namespace {
-constexpr char kConfFileNameFmt[] = "/data/vendor/wifi/hostapd/hostapd_%s.conf";
-
-using android::base::RemoveFileIfExists;
-using android::base::StringPrintf;
-using android::base::WriteStringToFile;
-using android::hardware::wifi::hostapd::V1_1::IHostapd;
-
-std::string WriteHostapdConfig(
-    const std::string& interface_name, const std::string& config)
-{
-	const std::string file_path =
-	    StringPrintf(kConfFileNameFmt, interface_name.c_str());
-	if (WriteStringToFile(
-		config, file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
-		getuid(), getgid())) {
-		return file_path;
-	}
-	// Diagnose failure
-	int error = errno;
-	wpa_printf(
-	    MSG_ERROR, "Cannot write hostapd config to %s, error: %s",
-	    file_path.c_str(), strerror(error));
-	struct stat st;
-	int result = stat(file_path.c_str(), &st);
-	if (result == 0) {
-		wpa_printf(
-		    MSG_ERROR, "hostapd config file uid: %d, gid: %d, mode: %d",
-		    st.st_uid, st.st_gid, st.st_mode);
-	} else {
-		wpa_printf(
-		    MSG_ERROR,
-		    "Error calling stat() on hostapd config file: %s",
-		    strerror(errno));
-	}
-	return "";
-}
-
-std::string CreateHostapdConfig(
-    const IHostapd::IfaceParams& iface_params,
-    const IHostapd::NetworkParams& nw_params)
-{
-	if (nw_params.ssid.size() >
-	    static_cast<uint32_t>(
-		IHostapd::ParamSizeLimits::SSID_MAX_LEN_IN_BYTES)) {
-		wpa_printf(
-		    MSG_ERROR, "Invalid SSID size: %zu", nw_params.ssid.size());
-		return "";
-	}
-	if ((nw_params.encryptionType != IHostapd::EncryptionType::NONE) &&
-	    (nw_params.pskPassphrase.size() <
-		 static_cast<uint32_t>(
-		     IHostapd::ParamSizeLimits::
-			 WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES) ||
-	     nw_params.pskPassphrase.size() >
-		 static_cast<uint32_t>(
-		     IHostapd::ParamSizeLimits::
-			 WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
-		wpa_printf(
-		    MSG_ERROR, "Invalid psk passphrase size: %zu",
-		    nw_params.pskPassphrase.size());
-		return "";
-	}
-
-	// SSID string
-	std::stringstream ss;
-	ss << std::hex;
-	ss << std::setfill('0');
-	for (uint8_t b : nw_params.ssid) {
-		ss << std::setw(2) << static_cast<unsigned int>(b);
-	}
-	const std::string ssid_as_string = ss.str();
-
-	// Encryption config string
-	std::string encryption_config_as_string;
-	switch (nw_params.encryptionType) {
-	case IHostapd::EncryptionType::NONE:
-		// no security params
-		break;
-	case IHostapd::EncryptionType::WPA:
-		encryption_config_as_string = StringPrintf(
-		    "wpa=3\n"
-		    "wpa_pairwise=TKIP CCMP\n"
-		    "wpa_passphrase=%s",
-		    nw_params.pskPassphrase.c_str());
-		break;
-	case IHostapd::EncryptionType::WPA2:
-		encryption_config_as_string = StringPrintf(
-		    "wpa=2\n"
-		    "rsn_pairwise=CCMP\n"
-		    "wpa_passphrase=%s",
-		    nw_params.pskPassphrase.c_str());
-		break;
-	default:
-		wpa_printf(MSG_ERROR, "Unknown encryption type");
-		return "";
-	}
-
-	std::string channel_config_as_string;
-	if (iface_params.V1_0.channelParams.enableAcs) {
-		std::string chanlist_as_string;
-		for (const auto &range :
-		     iface_params.channelParams.acsChannelRanges) {
-			if (range.start != range.end) {
-				chanlist_as_string +=
-					StringPrintf("%d-%d ", range.start, range.end);
-			} else {
-				chanlist_as_string += StringPrintf("%d ", range.start);
-			}
-		}
-		channel_config_as_string = StringPrintf(
-		    "channel=0\n"
-		    "acs_exclude_dfs=%d\n"
-		    "chanlist=%s",
-		    iface_params.V1_0.channelParams.acsShouldExcludeDfs,
-		    chanlist_as_string.c_str());
-	} else {
-		channel_config_as_string = StringPrintf(
-		    "channel=%d", iface_params.V1_0.channelParams.channel);
-	}
-
-	// Hw Mode String
-	std::string hw_mode_as_string;
-	std::string ht_cap_vht_oper_chwidth_as_string;
-	switch (iface_params.V1_0.channelParams.band) {
-	case IHostapd::Band::BAND_2_4_GHZ:
-		hw_mode_as_string = "hw_mode=g";
-		break;
-	case IHostapd::Band::BAND_5_GHZ:
-		hw_mode_as_string = "hw_mode=a";
-		if (iface_params.V1_0.channelParams.enableAcs) {
-			ht_cap_vht_oper_chwidth_as_string =
-			    "ht_capab=[HT40+]\n"
-			    "vht_oper_chwidth=1";
-		}
-		break;
-	case IHostapd::Band::BAND_ANY:
-		hw_mode_as_string = "hw_mode=any";
-		if (iface_params.V1_0.channelParams.enableAcs) {
-			ht_cap_vht_oper_chwidth_as_string =
-			    "ht_capab=[HT40+]\n"
-			    "vht_oper_chwidth=1";
-		}
-		break;
-	default:
-		wpa_printf(MSG_ERROR, "Invalid band");
-		return "";
-	}
-
-	return StringPrintf(
-	    "interface=%s\n"
-	    "driver=nl80211\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.
-	    "ssid2=%s\n"
-	    "%s\n"
-	    "ieee80211n=%d\n"
-	    "ieee80211ac=%d\n"
-	    "%s\n"
-	    "%s\n"
-	    "ignore_broadcast_ssid=%d\n"
-	    "wowlan_triggers=any\n"
-	    "%s\n",
-	    iface_params.V1_0.ifaceName.c_str(), ssid_as_string.c_str(),
-	    channel_config_as_string.c_str(),
-	    iface_params.V1_0.hwModeParams.enable80211N ? 1 : 0,
-	    iface_params.V1_0.hwModeParams.enable80211AC ? 1 : 0,
-	    hw_mode_as_string.c_str(), ht_cap_vht_oper_chwidth_as_string.c_str(),
-	    nw_params.isHidden ? 1 : 0, encryption_config_as_string.c_str());
-}
-
-// hostapd core functions accept "C" style function pointers, so use global
-// functions to pass to the hostapd core function and store the corresponding
-// std::function methods to be invoked.
-//
-// NOTE: Using the pattern from the vendor HAL (wifi_legacy_hal.cpp).
-//
-// Callback to be invoked once setup is complete
-std::function<void(struct hostapd_data*)> on_setup_complete_internal_callback;
-void onAsyncSetupCompleteCb(void* ctx)
-{
-	struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
-	if (on_setup_complete_internal_callback) {
-		on_setup_complete_internal_callback(iface_hapd);
-		// Invalidate this callback since we don't want this firing
-		// again.
-		on_setup_complete_internal_callback = nullptr;
-	}
-}
-}  // namespace
-
-namespace android {
-namespace hardware {
-namespace wifi {
-namespace hostapd {
-namespace V1_1 {
-namespace implementation {
-using hidl_return_util::call;
-using namespace android::hardware::wifi::hostapd::V1_0;
-
-Hostapd::Hostapd(struct hapd_interfaces* interfaces) : interfaces_(interfaces)
-{}
-
-Return<void> Hostapd::addAccessPoint(
-    const V1_0::IHostapd::IfaceParams& iface_params,
-    const NetworkParams& nw_params, addAccessPoint_cb _hidl_cb)
-{
-	return call(
-	    this, &Hostapd::addAccessPointInternal, _hidl_cb, iface_params,
-	    nw_params);
-}
-
-Return<void> Hostapd::addAccessPoint_1_1(
-    const IfaceParams& iface_params, const NetworkParams& nw_params,
-    addAccessPoint_cb _hidl_cb)
-{
-	return call(
-	    this, &Hostapd::addAccessPointInternal_1_1, _hidl_cb, iface_params,
-	    nw_params);
-}
-
-Return<void> Hostapd::removeAccessPoint(
-    const hidl_string& iface_name, removeAccessPoint_cb _hidl_cb)
-{
-	return call(
-	    this, &Hostapd::removeAccessPointInternal, _hidl_cb, iface_name);
-}
-
-Return<void> Hostapd::terminate()
-{
-	wpa_printf(MSG_INFO, "Terminating...");
-	eloop_terminate();
-	return Void();
-}
-
-Return<void> Hostapd::registerCallback(
-    const sp<IHostapdCallback>& callback, registerCallback_cb _hidl_cb)
-{
-	return call(
-	    this, &Hostapd::registerCallbackInternal, _hidl_cb, callback);
-}
-
-HostapdStatus Hostapd::addAccessPointInternal(
-    const V1_0::IHostapd::IfaceParams& iface_params,
-    const NetworkParams& nw_params)
-{
-	return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
-}
-
-HostapdStatus Hostapd::addAccessPointInternal_1_1(
-    const IfaceParams& iface_params, const NetworkParams& nw_params)
-{
-	if (hostapd_get_iface(interfaces_, iface_params.V1_0.ifaceName.c_str())) {
-		wpa_printf(
-		    MSG_ERROR, "Interface %s already present",
-		    iface_params.V1_0.ifaceName.c_str());
-		return {HostapdStatusCode::FAILURE_IFACE_EXISTS, ""};
-	}
-	const auto conf_params = CreateHostapdConfig(iface_params, nw_params);
-	if (conf_params.empty()) {
-		wpa_printf(MSG_ERROR, "Failed to create config params");
-		return {HostapdStatusCode::FAILURE_ARGS_INVALID, ""};
-	}
-	const auto conf_file_path =
-	    WriteHostapdConfig(iface_params.V1_0.ifaceName, conf_params);
-	if (conf_file_path.empty()) {
-		wpa_printf(MSG_ERROR, "Failed to write config file");
-		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
-	}
-	std::string add_iface_param_str = StringPrintf(
-	    "%s config=%s", iface_params.V1_0.ifaceName.c_str(),
-	    conf_file_path.c_str());
-	std::vector<char> add_iface_param_vec(
-	    add_iface_param_str.begin(), add_iface_param_str.end() + 1);
-	if (hostapd_add_iface(interfaces_, add_iface_param_vec.data()) < 0) {
-		wpa_printf(
-		    MSG_ERROR, "Adding interface %s failed",
-		    add_iface_param_str.c_str());
-		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
-	}
-	struct hostapd_data* iface_hapd =
-	    hostapd_get_iface(interfaces_, iface_params.V1_0.ifaceName.c_str());
-	WPA_ASSERT(iface_hapd != nullptr && iface_hapd->iface != nullptr);
-	// Register the setup complete callbacks
-	on_setup_complete_internal_callback =
-	    [this](struct hostapd_data* iface_hapd) {
-		    wpa_printf(
-			MSG_DEBUG, "AP interface setup completed - state %s",
-			hostapd_state_text(iface_hapd->iface->state));
-		    if (iface_hapd->iface->state == HAPD_IFACE_DISABLED) {
-			    // Invoke the failure callback on all registered
-			    // clients.
-			    for (const auto& callback : callbacks_) {
-				    callback->onFailure(
-					iface_hapd->conf->iface);
-			    }
-		    }
-	    };
-	iface_hapd->setup_complete_cb = onAsyncSetupCompleteCb;
-	iface_hapd->setup_complete_cb_ctx = iface_hapd;
-	if (hostapd_enable_iface(iface_hapd->iface) < 0) {
-		wpa_printf(
-		    MSG_ERROR, "Enabling interface %s failed",
-		    iface_params.V1_0.ifaceName.c_str());
-		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
-	}
-	return {HostapdStatusCode::SUCCESS, ""};
-}
-
-HostapdStatus Hostapd::removeAccessPointInternal(const std::string& iface_name)
-{
-	std::vector<char> remove_iface_param_vec(
-	    iface_name.begin(), iface_name.end() + 1);
-	if (hostapd_remove_iface(interfaces_, remove_iface_param_vec.data()) <
-	    0) {
-		wpa_printf(
-		    MSG_ERROR, "Removing interface %s failed",
-		    iface_name.c_str());
-		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
-	}
-	return {HostapdStatusCode::SUCCESS, ""};
-}
-
-HostapdStatus Hostapd::registerCallbackInternal(
-    const sp<IHostapdCallback>& callback)
-{
-	callbacks_.push_back(callback);
-	return {HostapdStatusCode::SUCCESS, ""};
-}
-
-}  // namespace implementation
-}  // namespace V1_1
-}  // namespace hostapd
-}  // namespace wifi
-}  // namespace hardware
-}  // namespace android
diff --git a/hostapd/hidl/1.1/hostapd.h b/hostapd/hidl/1.1/hostapd.h
deleted file mode 100644
index e2b4ba8..0000000
--- a/hostapd/hidl/1.1/hostapd.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * hidl interface for wpa_hostapd daemon
- * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef HOSTAPD_HIDL_SUPPLICANT_H
-#define HOSTAPD_HIDL_SUPPLICANT_H
-
-#include <string>
-
-#include <android-base/macros.h>
-
-#include <android/hardware/wifi/hostapd/1.1/IHostapd.h>
-#include <android/hardware/wifi/hostapd/1.1/IHostapdCallback.h>
-
-extern "C"
-{
-#include "utils/common.h"
-#include "utils/includes.h"
-#include "utils/wpa_debug.h"
-#include "ap/hostapd.h"
-}
-
-namespace android {
-namespace hardware {
-namespace wifi {
-namespace hostapd {
-namespace V1_1 {
-namespace implementation {
-using namespace android::hardware::wifi::hostapd::V1_0;
-
-/**
- * Implementation of the hostapd hidl object. This hidl
- * object is used core for global control operations on
- * hostapd.
- */
-class Hostapd : public V1_1::IHostapd
-{
-public:
-	Hostapd(hapd_interfaces* interfaces);
-	~Hostapd() override = default;
-
-	// Hidl methods exposed.
-	Return<void> addAccessPoint(
-	    const V1_0::IHostapd::IfaceParams& iface_params,
-	    const NetworkParams& nw_params, addAccessPoint_cb _hidl_cb) override;
-	Return<void> addAccessPoint_1_1(
-	    const IfaceParams& iface_params, const NetworkParams& nw_params,
-	    addAccessPoint_cb _hidl_cb) override;
-	Return<void> removeAccessPoint(
-	    const hidl_string& iface_name,
-	    removeAccessPoint_cb _hidl_cb) override;
-	Return<void> terminate() override;
-	Return<void> registerCallback(
-	    const sp<IHostapdCallback>& callback,
-	    registerCallback_cb _hidl_cb) override;
-
-private:
-	// Corresponding worker functions for the HIDL methods.
-	HostapdStatus addAccessPointInternal(
-	    const V1_0::IHostapd::IfaceParams& iface_params,
-	    const NetworkParams& nw_params);
-	HostapdStatus addAccessPointInternal_1_1(
-	    const IfaceParams& IfaceParams, const NetworkParams& nw_params);
-	HostapdStatus removeAccessPointInternal(const std::string& iface_name);
-	HostapdStatus registerCallbackInternal(
-	    const sp<IHostapdCallback>& callback);
-
-	// Raw pointer to the global structure maintained by the core.
-	struct hapd_interfaces* interfaces_;
-	// Callbacks registered.
-	std::vector<sp<IHostapdCallback>> callbacks_;
-
-	DISALLOW_COPY_AND_ASSIGN(Hostapd);
-};
-}  // namespace implementation
-}  // namespace V1_1
-}  // namespace hostapd
-}  // namespace wifi
-}  // namespace hardware
-}  // namespace android
-
-#endif  // HOSTAPD_HIDL_SUPPLICANT_H
diff --git a/hostapd/hidl/1.1/hidl.cpp b/hostapd/hidl/1.2/hidl.cpp
similarity index 93%
rename from hostapd/hidl/1.1/hidl.cpp
rename to hostapd/hidl/1.2/hidl.cpp
index 2051e7b..4bde312 100644
--- a/hostapd/hidl/1.1/hidl.cpp
+++ b/hostapd/hidl/1.2/hidl.cpp
@@ -22,8 +22,8 @@
 
 using android::hardware::configureRpcThreadpool;
 using android::hardware::IPCThreadState;
-using android::hardware::wifi::hostapd::V1_1::IHostapd;
-using android::hardware::wifi::hostapd::V1_1::implementation::Hostapd;
+using android::hardware::wifi::hostapd::V1_2::IHostapd;
+using android::hardware::wifi::hostapd::V1_2::implementation::Hostapd;
 
 // This file is a bridge between the hostapd code written in 'C' and the HIDL
 // interface in C++. So, using "C" style static globals here!
diff --git a/hostapd/hidl/1.1/hidl.h b/hostapd/hidl/1.2/hidl.h
similarity index 100%
rename from hostapd/hidl/1.1/hidl.h
rename to hostapd/hidl/1.2/hidl.h
diff --git a/hostapd/hidl/1.1/hidl_return_util.h b/hostapd/hidl/1.2/hidl_return_util.h
similarity index 83%
rename from hostapd/hidl/1.1/hidl_return_util.h
rename to hostapd/hidl/1.2/hidl_return_util.h
index d914ee2..81742f8 100644
--- a/hostapd/hidl/1.1/hidl_return_util.h
+++ b/hostapd/hidl/1.2/hidl_return_util.h
@@ -16,7 +16,7 @@
 namespace hardware {
 namespace wifi {
 namespace hostapd {
-namespace V1_1 {
+namespace V1_2 {
 namespace implementation {
 namespace hidl_return_util {
 
@@ -25,18 +25,17 @@
  * HIDL interface object.
  */
 // Use for HIDL methods which return only an instance of HostapdStatus.
-template <typename ObjT, typename WorkFuncT, typename... Args>
+template <typename ObjT, typename WorkFuncT, typename StatusT, typename... Args>
 Return<void> call(
     ObjT* obj, WorkFuncT&& work,
-    const std::function<void(const HostapdStatus&)>& hidl_cb, Args&&... args)
+    const std::function<void(const StatusT&)>& hidl_cb, Args&&... args)
 {
 	hidl_cb((obj->*work)(std::forward<Args>(args)...));
 	return Void();
 }
-
 }  // namespace hidl_return_util
 }  // namespace implementation
-}  // namespace V1_1
+}  // namespace V1_2
 }  // namespace hostapd
 }  // namespace wifi
 }  // namespace hardware
diff --git a/hostapd/hidl/1.2/hostapd.cpp b/hostapd/hidl/1.2/hostapd.cpp
new file mode 100644
index 0000000..7aa057b
--- /dev/null
+++ b/hostapd/hidl/1.2/hostapd.cpp
@@ -0,0 +1,643 @@
+/*
+ * hidl interface for wpa_hostapd daemon
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
+#include "hostapd.h"
+#include "hidl_return_util.h"
+
+extern "C"
+{
+#include "utils/eloop.h"
+}
+
+// The HIDL implementation for hostapd creates a hostapd.conf dynamically for
+// each interface. This file can then be used to hook onto the normal config
+// file parsing logic in hostapd code.  Helps us to avoid duplication of code
+// in the HIDL interface.
+// TOOD(b/71872409): Add unit tests for this.
+namespace {
+constexpr char kConfFileNameFmt[] = "/data/vendor/wifi/hostapd/hostapd_%s.conf";
+
+using android::base::RemoveFileIfExists;
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+using android::hardware::wifi::hostapd::V1_2::IHostapd;
+
+std::string WriteHostapdConfig(
+    const std::string& interface_name, const std::string& config)
+{
+	const std::string file_path =
+	    StringPrintf(kConfFileNameFmt, interface_name.c_str());
+	if (WriteStringToFile(
+		config, file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+		getuid(), getgid())) {
+		return file_path;
+	}
+	// Diagnose failure
+	int error = errno;
+	wpa_printf(
+	    MSG_ERROR, "Cannot write hostapd config to %s, error: %s",
+	    file_path.c_str(), strerror(error));
+	struct stat st;
+	int result = stat(file_path.c_str(), &st);
+	if (result == 0) {
+		wpa_printf(
+		    MSG_ERROR, "hostapd config file uid: %d, gid: %d, mode: %d",
+		    st.st_uid, st.st_gid, st.st_mode);
+	} else {
+		wpa_printf(
+		    MSG_ERROR,
+		    "Error calling stat() on hostapd config file: %s",
+		    strerror(errno));
+	}
+	return "";
+}
+
+/*
+ * Get the op_class for a channel/band
+ * The logic here is based on Table E-4 in the 802.11 Specification
+ */
+int getOpClassForChannel(int channel, int band, bool support11n, bool support11ac) {
+	// 2GHz Band
+	if ((band & IHostapd::BandMask::BAND_2_GHZ) != 0) {
+		if (channel == 14) {
+			return 82;
+		}
+		if (channel >= 1 && channel <= 13) {
+			if (!support11n) {
+				//20MHz channel
+				return 81;
+			}
+			if (channel <= 9) {
+				// HT40 with secondary channel above primary
+				return 83;
+			}
+			// HT40 with secondary channel below primary
+			return 84;
+		}
+		// Error
+		return 0;
+	}
+
+	// 5GHz Band
+	if ((band & IHostapd::BandMask::BAND_5_GHZ) != 0) {
+		if (support11ac) {
+			switch (channel) {
+				case 42:
+				case 58:
+				case 106:
+				case 122:
+				case 138:
+				case 155:
+					// 80MHz channel
+					return 128;
+				case 50:
+				case 114:
+					// 160MHz channel
+					return 129;
+			}
+		}
+
+		if (!support11n) {
+			if (channel >= 36 && channel <= 48) {
+				return 115;
+			}
+			if (channel >= 52 && channel <= 64) {
+				return 118;
+			}
+			if (channel >= 100 && channel <= 144) {
+				return 121;
+			}
+			if (channel >= 149 && channel <= 161) {
+				return 124;
+			}
+			if (channel >= 165 && channel <= 169) {
+				return 125;
+			}
+		} else {
+			switch (channel) {
+				case 36:
+				case 44:
+					// HT40 with secondary channel above primary
+					return 116;
+				case 40:
+				case 48:
+					// HT40 with secondary channel below primary
+					return 117;
+				case 52:
+				case 60:
+					// HT40 with secondary channel above primary
+					return  119;
+				case 56:
+				case 64:
+					// HT40 with secondary channel below primary
+					return 120;
+				case 100:
+				case 108:
+				case 116:
+				case 124:
+				case 132:
+				case 140:
+					// HT40 with secondary channel above primary
+					return 122;
+				case 104:
+				case 112:
+				case 120:
+				case 128:
+				case 136:
+				case 144:
+					// HT40 with secondary channel below primary
+					return 123;
+				case 149:
+				case 157:
+					// HT40 with secondary channel above primary
+					return 126;
+				case 153:
+				case 161:
+					// HT40 with secondary channel below primary
+					return 127;
+			}
+		}
+		// Error
+		return 0;
+	}
+
+	// 6GHz Band
+	if ((band & IHostapd::BandMask::BAND_6_GHZ) != 0) {
+		// Channels 1, 5. 9, 13, ...
+		if ((channel & 0x03) == 0x01) {
+			// 20MHz channel
+			return 131;
+		}
+		// Channels 3, 11, 19, 27, ...
+		if ((channel & 0x07) == 0x03) {
+			// 40MHz channel
+			return 132;
+		}
+		// Channels 7, 23, 39, 55, ...
+		if ((channel & 0x0F) == 0x07) {
+			// 80MHz channel
+			return 133;
+		}
+		// Channels 15, 47, 69, ...
+		if ((channel & 0x1F) == 0x0F) {
+			// 160MHz channel
+			return 134;
+		}
+		// Error
+		return 0;
+	}
+
+	return 0;
+}
+
+bool validatePassphrase(int passphrase_len, int min_len, int max_len)
+{
+	if (min_len != -1 && passphrase_len < min_len) return false;
+	if (max_len != -1 && passphrase_len > max_len) return false;
+	return true;
+}
+
+std::string CreateHostapdConfig(
+    const IHostapd::IfaceParams& iface_params,
+    const IHostapd::NetworkParams& nw_params)
+{
+	if (nw_params.V1_0.ssid.size() >
+	    static_cast<uint32_t>(
+		IHostapd::ParamSizeLimits::SSID_MAX_LEN_IN_BYTES)) {
+		wpa_printf(
+		    MSG_ERROR, "Invalid SSID size: %zu", nw_params.V1_0.ssid.size());
+		return "";
+	}
+
+	// SSID string
+	std::stringstream ss;
+	ss << std::hex;
+	ss << std::setfill('0');
+	for (uint8_t b : nw_params.V1_0.ssid) {
+		ss << std::setw(2) << static_cast<unsigned int>(b);
+	}
+	const std::string ssid_as_string = ss.str();
+
+	// Encryption config string
+	std::string encryption_config_as_string;
+	switch (nw_params.encryptionType) {
+	case IHostapd::EncryptionType::NONE:
+		// no security params
+		break;
+	case IHostapd::EncryptionType::WPA:
+		if (!validatePassphrase(
+		    nw_params.passphrase.size(),
+		    static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+				WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
+		    static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+				WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
+			return "";
+		}
+		encryption_config_as_string = StringPrintf(
+		    "wpa=3\n"
+		    "wpa_pairwise=TKIP CCMP\n"
+		    "wpa_passphrase=%s",
+		    nw_params.passphrase.c_str());
+		break;
+	case IHostapd::EncryptionType::WPA2:
+		if (!validatePassphrase(
+		    nw_params.passphrase.size(),
+		    static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+				WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
+		    static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+				WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
+			return "";
+		}
+		encryption_config_as_string = StringPrintf(
+		    "wpa=2\n"
+		    "rsn_pairwise=CCMP\n"
+		    "wpa_passphrase=%s",
+		    nw_params.passphrase.c_str());
+		break;
+	case IHostapd::EncryptionType::WPA3_SAE_TRANSITION:
+		if (!validatePassphrase(
+		    nw_params.passphrase.size(),
+		    static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+				WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
+		    static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+				WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
+			return "";
+		}
+		encryption_config_as_string = StringPrintf(
+		    "wpa=2\n"
+		    "rsn_pairwise=CCMP\n"
+		    "wpa_key_mgmt=WPA-PSK SAE\n"
+		    "ieee80211w=1\n"
+		    "sae_require_mfp=1\n"
+		    "wpa_passphrase=%s\n"
+		    "sae_password=%s",
+		    nw_params.passphrase.c_str(),
+		    nw_params.passphrase.c_str());
+		break;
+	case IHostapd::EncryptionType::WPA3_SAE:
+		if (!validatePassphrase(nw_params.passphrase.size(), 1, -1)) {
+			return "";
+		}
+		encryption_config_as_string = StringPrintf(
+		    "wpa=2\n"
+		    "rsn_pairwise=CCMP\n"
+		    "wpa_key_mgmt=SAE\n"
+		    "ieee80211w=2\n"
+		    "sae_require_mfp=2\n"
+		    "sae_password=%s",
+		    nw_params.passphrase.c_str());
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "Unknown encryption type");
+		return "";
+	}
+
+	unsigned int band = 0;
+	band |= iface_params.channelParams.bandMask;
+
+	std::string channel_config_as_string;
+	bool isFirst = true;
+	if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
+		std::string freqList_as_string;
+		for (const auto &range :
+		    iface_params.channelParams.acsChannelFreqRangesMhz) {
+			if (!isFirst) {
+				freqList_as_string += ",";
+			}
+			isFirst = false;
+
+			if (range.start != range.end) {
+				freqList_as_string +=
+				    StringPrintf("%d-%d", range.start, range.end);
+			} else {
+				freqList_as_string += StringPrintf("%d", range.start);
+			}
+		}
+		channel_config_as_string = StringPrintf(
+		    "channel=0\n"
+		    "acs_exclude_dfs=%d\n"
+		    "freqlist=%s",
+		    iface_params.V1_1.V1_0.channelParams.acsShouldExcludeDfs,
+		    freqList_as_string.c_str());
+	} else {
+		int op_class = getOpClassForChannel(
+		    iface_params.V1_1.V1_0.channelParams.channel,
+		    band,
+		    iface_params.V1_1.V1_0.hwModeParams.enable80211N,
+		    iface_params.V1_1.V1_0.hwModeParams.enable80211AC);
+		channel_config_as_string = StringPrintf(
+		    "channel=%d\n"
+		    "op_class=%d",
+		    iface_params.V1_1.V1_0.channelParams.channel, op_class);
+	}
+
+	std::string hw_mode_as_string;
+	std::string ht_cap_vht_oper_chwidth_as_string;
+
+	if ((band & IHostapd::BandMask::BAND_2_GHZ) != 0) {
+		if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
+		    || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
+			hw_mode_as_string = "hw_mode=any";
+			if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
+				ht_cap_vht_oper_chwidth_as_string =
+				    "ht_capab=[HT40+]\n"
+				    "vht_oper_chwidth=1";
+			}
+		} else {
+			hw_mode_as_string = "hw_mode=g";
+		}
+	} else {
+		if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
+		    || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
+			hw_mode_as_string = "hw_mode=a";
+			if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
+				ht_cap_vht_oper_chwidth_as_string =
+				    "ht_capab=[HT40+]\n"
+				    "vht_oper_chwidth=1";
+			}
+		} else {
+			wpa_printf(MSG_ERROR, "Invalid band");
+			return "";
+		}
+	}
+
+	std::string he_params_as_string;
+	if (iface_params.hwModeParams.enable80211AX) {
+		he_params_as_string = StringPrintf(
+		    "ieee80211ax=1\n"
+		    "he_su_beamformer=%d\n"
+		    "he_su_beamformee=%d\n"
+		    "he_mu_beamformer=%d\n"
+		    "he_twt_required=%d\n",
+		    iface_params.hwModeParams.enableHeSingleUserBeamformer ? 1 : 0,
+		    iface_params.hwModeParams.enableHeSingleUserBeamformee ? 1 : 0,
+		    iface_params.hwModeParams.enableHeMultiUserBeamformer ? 1 : 0,
+		    iface_params.hwModeParams.enableHeTargetWakeTime ? 1 : 0);
+	} else {
+		he_params_as_string = "ieee80211ax=0";
+	}
+
+	return StringPrintf(
+	    "interface=%s\n"
+	    "driver=nl80211\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.
+	    "ssid2=%s\n"
+	    "%s\n"
+	    "ieee80211n=%d\n"
+	    "ieee80211ac=%d\n"
+	    "%s\n"
+	    "%s\n"
+	    "%s\n"
+	    "ignore_broadcast_ssid=%d\n"
+	    "wowlan_triggers=any\n"
+	    "%s\n",
+	    iface_params.V1_1.V1_0.ifaceName.c_str(), ssid_as_string.c_str(),
+	    channel_config_as_string.c_str(),
+	    iface_params.V1_1.V1_0.hwModeParams.enable80211N ? 1 : 0,
+	    iface_params.V1_1.V1_0.hwModeParams.enable80211AC ? 1 : 0,
+	    he_params_as_string.c_str(),
+	    hw_mode_as_string.c_str(), ht_cap_vht_oper_chwidth_as_string.c_str(),
+	    nw_params.V1_0.isHidden ? 1 : 0, encryption_config_as_string.c_str());
+}
+
+// hostapd core functions accept "C" style function pointers, so use global
+// functions to pass to the hostapd core function and store the corresponding
+// std::function methods to be invoked.
+//
+// NOTE: Using the pattern from the vendor HAL (wifi_legacy_hal.cpp).
+//
+// Callback to be invoked once setup is complete
+std::function<void(struct hostapd_data*)> on_setup_complete_internal_callback;
+void onAsyncSetupCompleteCb(void* ctx)
+{
+	struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
+	if (on_setup_complete_internal_callback) {
+		on_setup_complete_internal_callback(iface_hapd);
+		// Invalidate this callback since we don't want this firing
+		// again.
+		on_setup_complete_internal_callback = nullptr;
+	}
+}
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace hostapd {
+namespace V1_2 {
+namespace implementation {
+using hidl_return_util::call;
+using namespace android::hardware::wifi::hostapd::V1_0;
+
+Hostapd::Hostapd(struct hapd_interfaces* interfaces) : interfaces_(interfaces)
+{}
+
+Return<void> Hostapd::addAccessPoint(
+    const V1_0::IHostapd::IfaceParams& iface_params,
+    const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb)
+{
+	return call(
+	    this, &Hostapd::addAccessPointInternal, _hidl_cb, iface_params,
+	    nw_params);
+}
+
+Return<void> Hostapd::addAccessPoint_1_1(
+    const V1_1::IHostapd::IfaceParams& iface_params,
+    const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb)
+{
+	return call(
+	    this, &Hostapd::addAccessPointInternal_1_1, _hidl_cb, iface_params,
+	    nw_params);
+}
+
+Return<void> Hostapd::addAccessPoint_1_2(
+    const IfaceParams& iface_params, const NetworkParams& nw_params,
+    addAccessPoint_1_2_cb _hidl_cb)
+{
+	return call(
+	    this, &Hostapd::addAccessPointInternal_1_2, _hidl_cb, iface_params,
+	    nw_params);
+}
+
+Return<void> Hostapd::removeAccessPoint(
+    const hidl_string& iface_name, removeAccessPoint_cb _hidl_cb)
+{
+	return call(
+	    this, &Hostapd::removeAccessPointInternal, _hidl_cb, iface_name);
+}
+
+Return<void> Hostapd::terminate()
+{
+	wpa_printf(MSG_INFO, "Terminating...");
+	eloop_terminate();
+	return Void();
+}
+
+Return<void> Hostapd::registerCallback(
+    const sp<V1_1::IHostapdCallback>& callback, registerCallback_cb _hidl_cb)
+{
+	return call(
+	    this, &Hostapd::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> Hostapd::forceClientDisconnect(
+    const hidl_string& iface_name, const hidl_array<uint8_t, 6>& client_address,
+    V1_2::Ieee80211ReasonCode reason_code, forceClientDisconnect_cb _hidl_cb)
+{
+	return call(
+	    this, &Hostapd::forceClientDisconnectInternal, _hidl_cb, iface_name,
+	    client_address, reason_code);
+}
+
+Return<void> Hostapd::setDebugParams(
+    DebugLevel level, setDebugParams_cb _hidl_cb)
+{
+	return call(
+	    this, &Hostapd::setDebugParamsInternal, _hidl_cb, level);
+}
+
+V1_0::HostapdStatus Hostapd::addAccessPointInternal(
+    const V1_0::IHostapd::IfaceParams& iface_params,
+    const V1_0::IHostapd::NetworkParams& nw_params)
+{
+	return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+V1_0::HostapdStatus Hostapd::addAccessPointInternal_1_1(
+    const V1_1::IHostapd::IfaceParams& iface_params,
+    const V1_1::IHostapd::NetworkParams& nw_params)
+{
+	return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+HostapdStatus Hostapd::addAccessPointInternal_1_2(
+    const IfaceParams& iface_params, const NetworkParams& nw_params)
+{
+	if (hostapd_get_iface(interfaces_, iface_params.V1_1.V1_0.ifaceName.c_str())) {
+		wpa_printf(
+		    MSG_ERROR, "Interface %s already present",
+		    iface_params.V1_1.V1_0.ifaceName.c_str());
+		return {HostapdStatusCode::FAILURE_IFACE_EXISTS, ""};
+	}
+	const auto conf_params = CreateHostapdConfig(iface_params, nw_params);
+	if (conf_params.empty()) {
+		wpa_printf(MSG_ERROR, "Failed to create config params");
+		return {HostapdStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	const auto conf_file_path =
+	    WriteHostapdConfig(iface_params.V1_1.V1_0.ifaceName, conf_params);
+	if (conf_file_path.empty()) {
+		wpa_printf(MSG_ERROR, "Failed to write config file");
+		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	std::string add_iface_param_str = StringPrintf(
+	    "%s config=%s", iface_params.V1_1.V1_0.ifaceName.c_str(),
+	    conf_file_path.c_str());
+	std::vector<char> add_iface_param_vec(
+	    add_iface_param_str.begin(), add_iface_param_str.end() + 1);
+	if (hostapd_add_iface(interfaces_, add_iface_param_vec.data()) < 0) {
+		wpa_printf(
+		    MSG_ERROR, "Adding interface %s failed",
+		    add_iface_param_str.c_str());
+		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	struct hostapd_data* iface_hapd =
+	    hostapd_get_iface(interfaces_, iface_params.V1_1.V1_0.ifaceName.c_str());
+	WPA_ASSERT(iface_hapd != nullptr && iface_hapd->iface != nullptr);
+	// Register the setup complete callbacks
+	on_setup_complete_internal_callback =
+	    [this](struct hostapd_data* iface_hapd) {
+		    wpa_printf(
+			MSG_DEBUG, "AP interface setup completed - state %s",
+			hostapd_state_text(iface_hapd->iface->state));
+		    if (iface_hapd->iface->state == HAPD_IFACE_DISABLED) {
+			    // Invoke the failure callback on all registered
+			    // clients.
+			    for (const auto& callback : callbacks_) {
+				    callback->onFailure(
+					iface_hapd->conf->iface);
+			    }
+		    }
+	    };
+	iface_hapd->setup_complete_cb = onAsyncSetupCompleteCb;
+	iface_hapd->setup_complete_cb_ctx = iface_hapd;
+	if (hostapd_enable_iface(iface_hapd->iface) < 0) {
+		wpa_printf(
+		    MSG_ERROR, "Enabling interface %s failed",
+		    iface_params.V1_1.V1_0.ifaceName.c_str());
+		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {HostapdStatusCode::SUCCESS, ""};
+}
+
+V1_0::HostapdStatus Hostapd::removeAccessPointInternal(const std::string& iface_name)
+{
+	std::vector<char> remove_iface_param_vec(
+	    iface_name.begin(), iface_name.end() + 1);
+	if (hostapd_remove_iface(interfaces_, remove_iface_param_vec.data()) <
+	    0) {
+		wpa_printf(
+		    MSG_ERROR, "Removing interface %s failed",
+		    iface_name.c_str());
+		return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {V1_0::HostapdStatusCode::SUCCESS, ""};
+}
+
+V1_0::HostapdStatus Hostapd::registerCallbackInternal(
+    const sp<V1_1::IHostapdCallback>& callback)
+{
+	callbacks_.push_back(callback);
+	return {V1_0::HostapdStatusCode::SUCCESS, ""};
+}
+
+V1_2::HostapdStatus Hostapd::forceClientDisconnectInternal(const std::string& iface_name,
+    const std::array<uint8_t, 6>& client_address, V1_2::Ieee80211ReasonCode reason_code)
+{
+	struct hostapd_data *hapd = hostapd_get_iface(interfaces_, iface_name.c_str());
+	struct sta_info *sta;
+	if (!hapd) {
+		wpa_printf(MSG_ERROR, "Interface %s doesn't exist", iface_name.c_str());
+		return {V1_2::HostapdStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+	}
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		int res;
+		res = memcmp(sta->addr, client_address.data(), ETH_ALEN);
+		if (res == 0) {
+			wpa_printf(MSG_INFO, "Force client:" MACSTR " disconnect with reason: %d",
+			    MAC2STR(client_address.data()), (uint16_t) reason_code);
+			ap_sta_disconnect(hapd, sta, sta->addr, (uint16_t) reason_code);
+			return {V1_2::HostapdStatusCode::SUCCESS, ""};
+		}
+	}
+	return {V1_2::HostapdStatusCode::FAILURE_CLIENT_UNKNOWN, ""};
+}
+
+V1_2::HostapdStatus Hostapd::setDebugParamsInternal(DebugLevel level)
+{
+	wpa_debug_level = static_cast<uint32_t>(level);
+	return {V1_2::HostapdStatusCode::SUCCESS, ""};
+}
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace hostapd
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/hostapd/hidl/1.2/hostapd.h b/hostapd/hidl/1.2/hostapd.h
new file mode 100644
index 0000000..ca6c32e
--- /dev/null
+++ b/hostapd/hidl/1.2/hostapd.h
@@ -0,0 +1,103 @@
+/*
+ * hidl interface for wpa_hostapd daemon
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_HIDL_SUPPLICANT_H
+#define HOSTAPD_HIDL_SUPPLICANT_H
+
+#include <string>
+
+#include <android-base/macros.h>
+
+#include <android/hardware/wifi/hostapd/1.2/IHostapd.h>
+#include <android/hardware/wifi/hostapd/1.1/IHostapdCallback.h>
+
+extern "C"
+{
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "utils/wpa_debug.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace hostapd {
+namespace V1_2 {
+namespace implementation {
+using namespace android::hardware::wifi::hostapd::V1_0;
+
+/**
+ * Implementation of the hostapd hidl object. This hidl
+ * object is used core for global control operations on
+ * hostapd.
+ */
+class Hostapd : public V1_2::IHostapd
+{
+public:
+	Hostapd(hapd_interfaces* interfaces);
+	~Hostapd() override = default;
+
+	// Hidl methods exposed.
+	Return<void> addAccessPoint(
+	    const V1_0::IHostapd::IfaceParams& iface_params,
+	    const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb) override;
+	Return<void> addAccessPoint_1_1(
+	    const V1_1::IHostapd::IfaceParams& iface_params,
+	    const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb) override;
+	Return<void> addAccessPoint_1_2(
+	    const V1_2::IHostapd::IfaceParams& iface_params, const NetworkParams& nw_params,
+	    addAccessPoint_1_2_cb _hidl_cb) override;
+	Return<void> removeAccessPoint(
+	    const hidl_string& iface_name,
+	    removeAccessPoint_cb _hidl_cb) override;
+	Return<void> terminate() override;
+	Return<void> registerCallback(
+	    const sp<V1_1::IHostapdCallback>& callback,
+	    registerCallback_cb _hidl_cb) override;
+	Return<void>forceClientDisconnect(
+	    const hidl_string& iface_name,
+	    const hidl_array<uint8_t, 6>& client_address,
+	    V1_2::Ieee80211ReasonCode reason_code, forceClientDisconnect_cb _hidl_cb) override;
+	Return<void> setDebugParams(
+	    DebugLevel level, setDebugParams_cb _hidl_cb) override;
+private:
+	// Corresponding worker functions for the HIDL methods.
+	V1_0::HostapdStatus addAccessPointInternal(
+	    const V1_0::IHostapd::IfaceParams& iface_params,
+	    const V1_0::IHostapd::NetworkParams& nw_params);
+	V1_0::HostapdStatus addAccessPointInternal_1_1(
+	    const V1_1::IHostapd::IfaceParams& IfaceParams,
+	    const V1_0::IHostapd::NetworkParams& nw_params);
+	V1_2::HostapdStatus addAccessPointInternal_1_2(
+	    const V1_2::IHostapd::IfaceParams& IfaceParams,
+	    const V1_2::IHostapd::NetworkParams& nw_params);
+	V1_0::HostapdStatus removeAccessPointInternal(const std::string& iface_name);
+	V1_0::HostapdStatus registerCallbackInternal(
+	    const sp<V1_1::IHostapdCallback>& callback);
+	V1_2::HostapdStatus forceClientDisconnectInternal(
+	    const std::string& iface_name,
+	    const std::array<uint8_t, 6>& client_address,
+	    V1_2::Ieee80211ReasonCode reason_code);
+	V1_2::HostapdStatus setDebugParamsInternal(DebugLevel level);
+	// Raw pointer to the global structure maintained by the core.
+	struct hapd_interfaces* interfaces_;
+	// Callbacks registered.
+	std::vector<sp<V1_1::IHostapdCallback>> callbacks_;
+	DISALLOW_COPY_AND_ASSIGN(Hostapd);
+};
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace hostapd
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+
+#endif  // HOSTAPD_HIDL_SUPPLICANT_H
diff --git a/hostapd/hostapd.android.rc b/hostapd/hostapd.android.rc
index c8792d6..512ca0d 100644
--- a/hostapd/hostapd.android.rc
+++ b/hostapd/hostapd.android.rc
@@ -14,6 +14,7 @@
 service hostapd /vendor/bin/hw/hostapd
     interface android.hardware.wifi.hostapd@1.0::IHostapd default
     interface android.hardware.wifi.hostapd@1.1::IHostapd default
+    interface android.hardware.wifi.hostapd@1.2::IHostapd default
     class main
     capabilities NET_ADMIN NET_RAW
     user wifi
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index f8caa56..263a04e 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -41,7 +41,6 @@
 # bit 2 (4) = RADIUS
 # bit 3 (8) = WPA
 # bit 4 (16) = driver interface
-# bit 5 (32) = IAPP
 # bit 6 (64) = MLME
 #
 # Levels (minimum value for logged events):
@@ -147,7 +146,8 @@
 # Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
 # g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
 # with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
-# needs to be set to hw_mode=a. When using ACS (see channel parameter), a
+# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
+# to be set to hw_mode=a. When using ACS (see channel parameter), a
 # special value "any" can be used to indicate that any support band can be used.
 # This special case is currently supported only with drivers with which
 # offloaded ACS is used.
@@ -164,6 +164,12 @@
 # which will enable the ACS survey based algorithm.
 channel=1
 
+# Global operating class (IEEE 802.11, Annex E, Table E-4)
+# This option allows hostapd to specify the operating class of the channel
+# configured with the channel parameter. channel and op_class together can
+# uniquely identify channels across different bands, including the 6 GHz band.
+#op_class=131
+
 # ACS tuning - Automatic Channel Selection
 # See: http://wireless.kernel.org/en/users/Documentation/acs
 #
@@ -199,11 +205,26 @@
 #chanlist=100 104 108 112 116
 #chanlist=1 6 11-13
 
+# Frequency list restriction. This option allows hostapd to select one of the
+# provided frequencies when a frequency should be automatically selected.
+# Frequency list can be provided as range using hyphen ('-') or individual
+# frequencies can be specified by comma (',') separated values
+# Default: all frequencies allowed in selected hw_mode
+#freqlist=2437,5945,5965
+#freqlist=2437,5985-6105
+
 # Exclude DFS channels from ACS
 # This option can be used to exclude all DFS channels from the ACS channel list
 # in cases where the driver supports DFS channels.
 #acs_exclude_dfs=1
 
+# Include only preferred scan channels from 6 GHz band for ACS
+# This option can be used to include only preferred scan channels in the 6 GHz
+# band. This can be useful in particular for devices that operate only a 6 GHz
+# BSS without a collocated 2.4/5 GHz BSS.
+# Default behavior is to include all PSC and non-PSC channels.
+#acs_exclude_6ghz_non_psc=1
+
 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
 beacon_int=100
 
@@ -782,10 +803,8 @@
 # 1 = supported
 #he_mu_beamformer=1
 
-# he_bss_color: BSS color
-# 0 = no BSS color (default)
-# unsigned integer = BSS color
-#he_bss_color=0
+# he_bss_color: BSS color (1-63)
+#he_bss_color=1
 
 #he_default_pe_duration: The duration of PE field in an HE PPDU in us
 # Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us
@@ -801,6 +820,22 @@
 # unsigned integer = duration in units of 16 us
 #he_rts_threshold=0
 
+# HE operating channel information; see matching vht_* parameters for details.
+# On the 6 GHz band the center freq calculation starts from 5.940 GHz offset.
+# For example idx=3 would result in 5955 MHz center frequency. In addition,
+# he_oper_chwidth is ignored, and the channel width is derived from the
+# configured operating class or center frequency indexes (see
+# IEEE P802.11ax/D4.3 Annex E, Table E-4).
+#he_oper_chwidth
+#he_oper_centr_freq_seg0_idx
+#he_oper_centr_freq_seg1_idx
+
+#he_basic_mcs_nss_set: Basic NSS/MCS set
+# 16-bit combination of 2-bit values of Max HE-MCS For 1..8 SS; each 2-bit
+# value having following meaning:
+# 0 = HE-MCS 0-7, 1 = HE-MCS 0-9, 2 = HE-MCS 0-11, 3 = not supported
+#he_basic_mcs_nss_set
+
 #he_mu_edca_qos_info_param_count
 #he_mu_edca_qos_info_q_ack
 #he_mu_edca_qos_info_queue_request=1
@@ -825,6 +860,12 @@
 #he_mu_edca_ac_vo_ecwmax=15
 #he_mu_edca_ac_vo_timer=255
 
+# Spatial Reuse Parameter Set
+#he_spr_sr_control
+#he_spr_non_srg_obss_pd_max_offset
+#he_spr_srg_obss_pd_min_offset
+#he_spr_srg_obss_pd_max_offset
+
 ##### IEEE 802.1X-2004 related configuration ##################################
 
 # Require IEEE 802.1X authorization
@@ -836,6 +877,8 @@
 # the new version number correctly (they seem to drop the frames completely).
 # In order to make hostapd interoperate with these clients, the version number
 # can be set to the older version (1) with this configuration value.
+# Note: When using MACsec, eapol_version shall be set to 3, which is
+# defined in IEEE Std 802.1X-2010.
 #eapol_version=2
 
 # Optional displayable message sent with EAP Request-Identity. The first \0
@@ -879,6 +922,54 @@
 # ERP is enabled (eap_server_erp=1).
 #erp_domain=example.com
 
+##### MACsec ##################################################################
+
+# macsec_policy: IEEE 802.1X/MACsec options
+# This determines how sessions are secured with MACsec (only for MACsec
+# drivers).
+# 0: MACsec not in use (default)
+# 1: MACsec enabled - Should secure, accept key server's advice to
+#    determine whether to use a secure session or not.
+#
+# macsec_integ_only: IEEE 802.1X/MACsec transmit mode
+# This setting applies only when MACsec is in use, i.e.,
+#  - macsec_policy is enabled
+#  - the key server has decided to enable MACsec
+# 0: Encrypt traffic (default)
+# 1: Integrity only
+#
+# macsec_replay_protect: IEEE 802.1X/MACsec replay protection
+# This setting applies only when MACsec is in use, i.e.,
+#  - macsec_policy is enabled
+#  - the key server has decided to enable MACsec
+# 0: Replay protection disabled (default)
+# 1: Replay protection enabled
+#
+# macsec_replay_window: IEEE 802.1X/MACsec replay protection window
+# This determines a window in which replay is tolerated, to allow receipt
+# of frames that have been misordered by the network.
+# This setting applies only when MACsec replay protection active, i.e.,
+#  - macsec_replay_protect is enabled
+#  - the key server has decided to enable MACsec
+# 0: No replay window, strict check (default)
+# 1..2^32-1: number of packets that could be misordered
+#
+# macsec_port: IEEE 802.1X/MACsec port
+# Port component of the SCI
+# Range: 1-65534 (default: 1)
+#
+# mka_priority (Priority of MKA Actor)
+# Range: 0..255 (default: 255)
+#
+# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
+# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
+# In this mode, instances of hostapd can act as MACsec peers. The peer
+# with lower priority will become the key server and start distributing SAKs.
+# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-byte (128-bit)
+# hex-string (32 hex-digits) or a 32-byte (256-bit) hex-string (64 hex-digits)
+# mka_ckn (CKN = CAK Name) takes a 1..32-bytes (8..256 bit) hex-string
+# (2..64 hex-digits)
+
 ##### Integrated EAP server ###################################################
 
 # Optionally, hostapd can be configured to use an integrated EAP server
@@ -912,6 +1003,23 @@
 # Passphrase for private key
 #private_key_passwd=secret passphrase
 
+# An alternative server certificate and private key can be configured with the
+# following parameters (with values just like the parameters above without the
+# '2' suffix). The ca_cert file (in PEM encoding) is used to add the trust roots
+# for both server certificates and/or client certificates).
+#
+# The main use case for this alternative server certificate configuration is to
+# enable both RSA and ECC public keys. The server will pick which one to use
+# based on the client preferences for the cipher suite (in the TLS ClientHello
+# message). It should be noted that number of deployed EAP peer implementations
+# do not filter out the cipher suite list based on their local configuration and
+# as such, configuration of alternative types of certificates on the server may
+# result in interoperability issues.
+#server_cert2=/etc/hostapd.server-ecc.pem
+#private_key2=/etc/hostapd.server-ecc.prv
+#private_key_passwd2=secret passphrase
+
+
 # Server identity
 # EAP methods that provide mechanism for authenticated server identity delivery
 # use this value. If not set, "hostapd" is used as a default.
@@ -930,7 +1038,7 @@
 #check_crl=1
 
 # Specify whether to ignore certificate CRL validity time mismatches with
-# errors X509_V_ERR_CERT_HAS_EXPIRED and X509_V_ERR_CERT_NOT_YET_VALID.
+# errors X509_V_ERR_CRL_HAS_EXPIRED and X509_V_ERR_CRL_NOT_YET_VALID.
 #
 # 0 = ignore errors
 # 1 = do not ignore errors (default)
@@ -999,6 +1107,12 @@
 # [ENABLE-TLSv1.3] = enable TLSv1.3 (experimental - disabled by default)
 #tls_flags=[flag1][flag2]...
 
+# Maximum number of EAP message rounds with data (default: 100)
+#max_auth_rounds=100
+
+# Maximum number of short EAP message rounds (default: 50)
+#max_auth_rounds_short=50
+
 # Cached OCSP stapling response (DER encoded)
 # If set, this file is sent as a certificate status response by the EAP server
 # if the EAP peer requests certificate status in the ClientHello message.
@@ -1109,10 +1223,41 @@
 # (or fewer) of the lifetime remains.
 #pac_key_refresh_time=86400
 
+# EAP-TEAP authentication type
+# 0 = inner EAP (default)
+# 1 = Basic-Password-Auth
+#eap_teap_auth=0
+
+# EAP-TEAP authentication behavior when using PAC
+# 0 = perform inner authentication (default)
+# 1 = skip inner authentication (inner EAP/Basic-Password-Auth)
+#eap_teap_pac_no_inner=0
+
+# EAP-TEAP behavior with Result TLV
+# 0 = include with Intermediate-Result TLV (default)
+# 1 = send in a separate message (for testing purposes)
+#eap_teap_separate_result=0
+
+# EAP-TEAP identities
+# 0 = allow any identity type (default)
+# 1 = require user identity
+# 2 = require machine identity
+# 3 = request user identity; accept either user or machine identity
+# 4 = request machine identity; accept either user or machine identity
+# 5 = require both user and machine identity
+#eap_teap_id=0
+
 # EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
 # (default: 0 = disabled).
 #eap_sim_aka_result_ind=1
 
+# EAP-SIM and EAP-AKA identity options
+# 0 = do not use pseudonyms or fast reauthentication
+# 1 = use pseudonyms, but not fast reauthentication
+# 2 = do not use pseudonyms, but use fast reauthentication
+# 3 = use pseudonyms and use fast reauthentication (default)
+#eap_sim_id=3
+
 # Trusted Network Connect (TNC)
 # If enabled, TNC validation will be required before the peer is allowed to
 # connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other
@@ -1124,11 +1269,6 @@
 # Whether to enable ERP on the EAP server.
 #eap_server_erp=1
 
-##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################
-
-# Interface to be used for IAPP broadcast packets
-#iapp_interface=eth0
-
 
 ##### RADIUS client configuration #############################################
 # for IEEE 802.1X with external Authentication Server, IEEE 802.11
@@ -1292,6 +1432,17 @@
 # Operator-Name = "Operator"
 #radius_acct_req_attr=126:s:Operator
 
+# If SQLite support is included, path to a database from which additional
+# RADIUS request attributes are extracted based on the station MAC address.
+#
+# The schema for the radius_attributes table is:
+# id | sta | reqtype | attr   :   multi-key (sta, reqtype)
+# id   = autonumber
+# sta  = station MAC address in `11:22:33:44:55:66` format.
+# type = `auth` | `acct` | NULL (match any)
+# attr = existing config file format, e.g. `126:s:Test Operator`
+#radius_req_attr_sqlite=radius_attr.sqlite
+
 # Dynamic Authorization Extensions (RFC 5176)
 # This mechanism can be used to allow dynamic changes to user session based on
 # commands from a RADIUS server (or some other disconnect client that has the
@@ -1508,6 +1659,12 @@
 # 1 = optional
 # 2 = required
 #ieee80211w=0
+# The most common configuration options for this based on the PMF (protected
+# management frames) certification program are:
+# PMF enabled: ieee80211w=1 and wpa_key_mgmt=WPA-EAP WPA-EAP-SHA256
+# PMF required: ieee80211w=2 and wpa_key_mgmt=WPA-EAP-SHA256
+# (and similarly for WPA-PSK and WPA-PSK-SHA256 if WPA2-Personal is used)
+# WPA3-Personal-only mode: ieee80211w=2 and wpa_key_mgmt=SAE
 
 # Group management cipher suite
 # Default: AES-128-CMAC (BIP)
@@ -1594,7 +1751,7 @@
 #sae_anti_clogging_threshold=5
 
 # Maximum number of SAE synchronization errors (dot11RSNASAESync)
-# The offending SAe peer will be disconnected if more than this many
+# The offending SAE peer will be disconnected if more than this many
 # synchronization errors happen.
 #sae_sync=5
 
@@ -1619,6 +1776,21 @@
 # MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1.
 #sae_require_mfp=0
 
+# SAE Confirm behavior
+# By default, AP will send out only SAE Commit message in response to a received
+# SAE Commit message. This parameter can be set to 1 to override that behavior
+# to send both SAE Commit and SAE Confirm messages without waiting for the STA
+# to send its SAE Confirm message first.
+#sae_confirm_immediate=0
+
+# SAE mechanism for PWE derivation
+# 0 = hunting-and-pecking loop only (default)
+# 1 = hash-to-element only
+# 2 = both hunting-and-pecking loop and hash-to-element enabled
+# Note: The default value is likely to change from 0 to 2 once the new
+# hash-to-element mechanism has received more interoperability testing.
+#sae_pwe=0
+
 # FILS Cache Identifier (16-bit value in hexdump format)
 #fils_cache_id=0011
 
@@ -2041,6 +2213,20 @@
 # Allow cross connection
 #allow_cross_connection=1
 
+##### Device Provisioning Protocol (DPP) ######################################
+
+# Name for Enrollee's DPP Configuration Request
+#dpp_name=Test
+
+# MUD URL for Enrollee's DPP Configuration Request (optional)
+#dpp_mud_url=https://example.com/mud
+
+#dpp_connector
+#dpp_netaccesskey
+#dpp_netaccesskey_expiry
+#dpp_csign
+#dpp_controller
+
 #### TDLS (IEEE 802.11z-2010) #################################################
 
 # Prohibit use of TDLS in this BSS
@@ -2492,6 +2678,55 @@
 # that allows sending of such data. Default: 0.
 #stationary_ap=0
 
+##### Airtime policy configuration ###########################################
+
+# Set the airtime policy operating mode:
+# 0 = disabled (default)
+# 1 = static config
+# 2 = per-BSS dynamic config
+# 3 = per-BSS limit mode
+#airtime_mode=0
+
+# Interval (in milliseconds) to poll the kernel for updated station activity in
+# dynamic and limit modes
+#airtime_update_interval=200
+
+# Static configuration of station weights (when airtime_mode=1). Kernel default
+# weight is 256; set higher for larger airtime share, lower for smaller share.
+# Each entry is a MAC address followed by a weight.
+#airtime_sta_weight=02:01:02:03:04:05 256
+#airtime_sta_weight=02:01:02:03:04:06 512
+
+# Per-BSS airtime weight. In multi-BSS mode, set for each BSS and hostapd will
+# configure station weights to enforce the correct ratio between BSS weights
+# depending on the number of active stations. The *ratios* between different
+# BSSes is what's important, not the absolute numbers.
+# Must be set for all BSSes if airtime_mode=2 or 3, has no effect otherwise.
+#airtime_bss_weight=1
+
+# Whether the current BSS should be limited (when airtime_mode=3).
+#
+# If set, the BSS weight ratio will be applied in the case where the current BSS
+# would exceed the share defined by the BSS weight ratio. E.g., if two BSSes are
+# set to the same weights, and one is set to limited, the limited BSS will get
+# no more than half the available airtime, but if the non-limited BSS has more
+# stations active, that *will* be allowed to exceed its half of the available
+# airtime.
+#airtime_bss_limit=1
+
+##### EDMG support ############################################################
+#
+# Enable EDMG capability for AP mode in the 60 GHz band. Default value is false.
+# To configure channel bonding for an EDMG AP use edmg_channel below.
+# If enable_edmg is set and edmg_channel is not set, EDMG CB1 will be
+# configured.
+#enable_edmg=1
+#
+# Configure channel bonding for AP mode in the 60 GHz band.
+# This parameter is relevant only if enable_edmg is set.
+# Default value is 0 (no channel bonding).
+#edmg_channel=9
+
 ##### TESTING OPTIONS #########################################################
 #
 # The options in this section are only available when the build configuration
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 23c592a..440664e 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -54,7 +54,7 @@
 	fprintf(stderr, "%s\n", hostapd_cli_version);
 	fprintf(stderr,
 		"\n"
-		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
+		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvBr] "
 		"[-a<path>] \\\n"
 		"                   [-P<pid file>] [-G<ping interval>] [command..]\n"
 		"\n"
@@ -68,6 +68,9 @@
 		"   -a<file>     run in daemon mode executing the action file "
 		"based on events\n"
 		"                from hostapd\n"
+		"   -r           try to reconnect when client socket is "
+		"disconnected.\n"
+		"                This is useful only when used with -a.\n"
 		"   -B           run a daemon in the background\n"
 		"   -i<ifname>   Interface to listen on (default: first "
 		"interface found in the\n"
@@ -401,7 +404,6 @@
 #endif /* CONFIG_TAXONOMY */
 
 
-#ifdef CONFIG_IEEE80211W
 static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
 {
@@ -414,7 +416,6 @@
 	snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
 	return wpa_ctrl_command(ctrl, buf);
 }
-#endif /* CONFIG_IEEE80211W */
 
 
 #ifdef CONFIG_WPS
@@ -1214,6 +1215,13 @@
 }
 
 
+static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "UPDATE_BEACON");
+}
+
+
 static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -1304,24 +1312,17 @@
 }
 
 
+static int hostapd_cli_cmd_show_neighbor(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "SHOW_NEIGHBOR");
+}
+
+
 static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc,
 					   char *argv[])
 {
-	char cmd[400];
-	int res;
-
-	if (argc != 2) {
-		printf("Invalid remove_neighbor command: needs 2 arguments\n");
-		return -1;
-	}
-
-	res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s",
-			  argv[0], argv[1]);
-	if (os_snprintf_error(sizeof(cmd), res)) {
-		printf("Too long REMOVE_NEIGHBOR command.\n");
-		return -1;
-	}
-	return wpa_ctrl_command(ctrl, cmd);
+	return hostapd_cli_cmd(ctrl, "REMOVE_NEIGHBOR", 1, argc, argv);
 }
 
 
@@ -1535,10 +1536,8 @@
 	{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
 	  "<addr> = get taxonomy signature for a station" },
 #endif /* CONFIG_TAXONOMY */
-#ifdef CONFIG_IEEE80211W
 	{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
 	  "<addr> = send SA Query to a station" },
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WPS
 	{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
 	  "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
@@ -1617,6 +1616,8 @@
 	  "= reload configuration for current interface" },
 	{ "disable", hostapd_cli_cmd_disable, NULL,
 	  "= disable hostapd on current interface" },
+	{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
+	  "= update Beacon frame contents\n"},
 	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
 	  "= drop all ERP keys"},
 	{ "log_level", hostapd_cli_cmd_log_level, NULL,
@@ -1628,8 +1629,10 @@
 	{ "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL,
 	  "<addr> <ssid=> <nr=> [lci=] [civic=] [stat]\n"
 	  "  = add AP to neighbor database" },
+	{ "show_neighbor", hostapd_cli_cmd_show_neighbor, NULL,
+	  "  = show neighbor database entries" },
 	{ "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
-	  "<addr> <ssid=> = remove AP from neighbor database" },
+	  "<addr> [ssid=<hex>] = remove AP from neighbor database" },
 	{ "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
 	  "<addr> = send LCI request to a station"},
 	{ "req_range", hostapd_cli_cmd_req_range, NULL,
@@ -2002,12 +2005,13 @@
 	int warning_displayed = 0;
 	int c;
 	int daemonize = 0;
+	int reconnect = 0;
 
 	if (os_program_init())
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
+		c = getopt(argc, argv, "a:BhG:i:p:P:rs:v");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -2036,6 +2040,9 @@
 		case 'P':
 			pid_file = optarg;
 			break;
+		case 'r':
+			reconnect = 1;
+			break;
 		case 's':
 			client_socket_dir = optarg;
 			break;
@@ -2078,8 +2085,7 @@
 				printf("Connection established.\n");
 			break;
 		}
-
-		if (!interactive) {
+		if (!interactive && !reconnect) {
 			perror("Failed to connect to hostapd - "
 			       "wpa_ctrl_open");
 			return -1;
@@ -2097,8 +2103,14 @@
 		return -1;
 	if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
 		return -1;
-
-	if (interactive)
+	if (reconnect && action_file && ctrl_ifname) {
+		while (!hostapd_cli_quit) {
+			if (ctrl_conn)
+				hostapd_cli_action(ctrl_conn);
+			os_sleep(1, 0);
+			hostapd_cli_reconnect(ctrl_ifname);
+		}
+	} else if (interactive)
 		hostapd_cli_interactive();
 	else if (action_file)
 		hostapd_cli_action(ctrl_conn);
diff --git a/hostapd/main.c b/hostapd/main.c
index b9df584..f08d60e 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -83,9 +83,6 @@
 	case HOSTAPD_MODULE_DRIVER:
 		module_str = "DRIVER";
 		break;
-	case HOSTAPD_MODULE_IAPP:
-		module_str = "IAPP";
-		break;
 	case HOSTAPD_MODULE_MLME:
 		module_str = "MLME";
 		break;
@@ -655,6 +652,9 @@
 	int start_ifaces_in_sync = 0;
 	char **if_names = NULL;
 	size_t if_names_size = 0;
+#ifdef CONFIG_DPP
+	struct dpp_global_config dpp_conf;
+#endif /* CONFIG_DPP */
 
 	if (os_program_init())
 		return -1;
@@ -674,7 +674,9 @@
 	dl_list_init(&interfaces.eth_p_oui);
 #endif /* CONFIG_ETH_P_OUI */
 #ifdef CONFIG_DPP
-	interfaces.dpp = dpp_global_init();
+	os_memset(&dpp_conf, 0, sizeof(dpp_conf));
+	/* TODO: dpp_conf.msg_ctx? */
+	interfaces.dpp = dpp_global_init(&dpp_conf);
 	if (!interfaces.dpp)
 		return -1;
 #endif /* CONFIG_DPP */
diff --git a/hs20/client/.gitignore b/hs20/client/.gitignore
index d2fd60f..f6c13d3 100644
--- a/hs20/client/.gitignore
+++ b/hs20/client/.gitignore
@@ -1 +1,4 @@
 hs20-osu-client
+SP
+osu-ca.pem
+spp.xsd
diff --git a/hs20/client/est.c b/hs20/client/est.c
index db65334..97f9132 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -158,7 +158,7 @@
 		return -1;
 	}
 
-	pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+	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);
@@ -639,8 +639,7 @@
 			return -1;
 		}
 
-		attrs = base64_decode((unsigned char *) resp, resp_len,
-				      &attrs_len);
+		attrs = base64_decode(resp, resp_len, &attrs_len);
 		os_free(resp);
 
 		if (attrs == NULL) {
@@ -734,7 +733,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
 
-	pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+	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);
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 1f594ce..a94f40c 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -310,7 +310,7 @@
 	size_t len;
 	u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
 	int res;
-	unsigned char *b64;
+	char *b64;
 	FILE *f;
 
 	url_node = get_node(ctx->xml, params, "CertURL");
@@ -364,7 +364,7 @@
 		return -1;
 	}
 
-	b64 = base64_encode((unsigned char *) cert, len, NULL);
+	b64 = base64_encode(cert, len, NULL);
 	os_free(cert);
 	if (b64 == NULL)
 		return -1;
@@ -1588,6 +1588,7 @@
 				      xml_node_t *node, const char *fqdn)
 {
 	char buf[200], dir[200];
+	int res;
 
 	wpa_printf(MSG_INFO, "- Credential/DigitalCertificate");
 
@@ -1599,14 +1600,20 @@
 		wpa_printf(MSG_INFO, "Failed to set username");
 	}
 
-	snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, fqdn);
+	res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir,
+			  fqdn);
+	if (os_snprintf_error(sizeof(buf), res))
+		return;
 	if (os_file_exists(buf)) {
 		if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) {
 			wpa_printf(MSG_INFO, "Failed to set client_cert");
 		}
 	}
 
-	snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, fqdn);
+	res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir,
+			  fqdn);
+	if (os_snprintf_error(sizeof(buf), res))
+		return;
 	if (os_file_exists(buf)) {
 		if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) {
 			wpa_printf(MSG_INFO, "Failed to set private_key");
@@ -1620,6 +1627,7 @@
 {
 	char *str = xml_node_get_text(ctx->xml, node);
 	char buf[200], dir[200];
+	int res;
 
 	if (str == NULL)
 		return;
@@ -1634,7 +1642,9 @@
 
 	if (getcwd(dir, sizeof(dir)) == NULL)
 		return;
-	snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
+	res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
+	if (os_snprintf_error(sizeof(buf), res))
+		return;
 	if (os_file_exists(buf)) {
 		if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) {
 			wpa_printf(MSG_INFO, "Failed to set CA cert");
@@ -2717,6 +2727,8 @@
 
 	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");
@@ -2737,8 +2749,13 @@
 			    "SP/%s/pps.xml", ctx->fqdn);
 		pps_fname = pps_fname_buf;
 
-		os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
-			    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;
 	}
 
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
index 4bef0ff..a50e907 100644
--- a/hs20/server/spp_server.c
+++ b/hs20/server/spp_server.c
@@ -633,7 +633,7 @@
 
 	add_text_node(ctx, node, "Username", user);
 
-	b64 = (char *) base64_encode((unsigned char *) pw, strlen(pw), NULL);
+	b64 = base64_encode(pw, strlen(pw), NULL);
 	if (b64 == NULL)
 		return NULL;
 	len = os_strlen(b64);
@@ -1602,8 +1602,7 @@
 
 	xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
 
-	b64 = (char *) base64_encode((unsigned char *) password,
-				     strlen(password), NULL);
+	b64 = base64_encode(password, strlen(password), NULL);
 	if (b64 == NULL) {
 		xml_node_free(ctx->xml, spp_node);
 		return NULL;
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 9b07ee1..54e48a0 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -15,11 +15,10 @@
 CFLAGS += -DCONFIG_INTERWORKING
 CFLAGS += -DCONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R_AP
-CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_WPS
 CFLAGS += -DCONFIG_PROXYARP
 CFLAGS += -DCONFIG_IPV6
-CFLAGS += -DCONFIG_IAPP
+CFLAGS += -DCONFIG_AIRTIME_POLICY
 
 LIB_OBJS= \
 	accounting.o \
@@ -27,6 +26,7 @@
 	ap_drv_ops.o \
 	ap_list.o \
 	ap_mlme.o \
+	airtime_policy.o \
 	authsrv.o \
 	beacon.o \
 	bss_load.o \
@@ -40,7 +40,6 @@
 	hostapd.o \
 	hs20.o \
 	hw_features.o \
-	iapp.o \
 	ieee802_11_auth.o \
 	ieee802_11.o \
 	ieee802_11_ht.o \
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 0aacc3c..9fc1886 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -97,6 +97,9 @@
 				   msg) < 0)
 		goto fail;
 
+	if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
+		goto fail;
+
 	if (sta) {
 		for (i = 0; ; i++) {
 			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 3b45075..232afa8 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -594,12 +594,12 @@
 	    iface->conf->secondary_channel)
 		n_chans = 2;
 
-	if (iface->conf->ieee80211ac) {
-		switch (iface->conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_80MHZ:
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CHANWIDTH_80MHZ:
 			n_chans = 4;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
+		case CHANWIDTH_160MHZ:
 			n_chans = 8;
 			break;
 		}
@@ -607,7 +607,7 @@
 
 	bw = num_chan_to_bw(n_chans);
 
-	/* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */
+	/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
 
 	wpa_printf(MSG_DEBUG,
 		   "ACS: Survey analysis for selected bandwidth %d MHz", bw);
@@ -647,9 +647,9 @@
 		}
 
 		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
-		    iface->conf->ieee80211ac) {
-			if (iface->conf->vht_oper_chwidth ==
-			    VHT_CHANWIDTH_80MHZ &&
+		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+			if (hostapd_get_oper_chwidth(iface->conf) ==
+			    CHANWIDTH_80MHZ &&
 			    !acs_usable_vht80_chan(chan)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for VHT80",
@@ -657,8 +657,8 @@
 				continue;
 			}
 
-			if (iface->conf->vht_oper_chwidth ==
-			    VHT_CHANWIDTH_160MHZ &&
+			if (hostapd_get_oper_chwidth(iface->conf) ==
+			    CHANWIDTH_160MHZ &&
 			    !acs_usable_vht160_chan(chan)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for VHT160",
@@ -783,20 +783,20 @@
 }
 
 
-static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
+static void acs_adjust_center_freq(struct hostapd_iface *iface)
 {
 	int offset;
 
 	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
 
-	switch (iface->conf->vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
+	switch (hostapd_get_oper_chwidth(iface->conf)) {
+	case CHANWIDTH_USE_HT:
 		offset = 2 * iface->conf->secondary_channel;
 		break;
-	case VHT_CHANWIDTH_80MHZ:
+	case CHANWIDTH_80MHZ:
 		offset = 6;
 		break;
-	case VHT_CHANWIDTH_160MHZ:
+	case CHANWIDTH_160MHZ:
 		offset = 14;
 		break;
 	default:
@@ -807,8 +807,8 @@
 		return;
 	}
 
-	iface->conf->vht_oper_centr_freq_seg0_idx =
-		iface->conf->channel + offset;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+					     iface->conf->channel + offset);
 }
 
 
@@ -862,9 +862,10 @@
 	}
 
 	iface->conf->channel = ideal_chan->chan;
+	iface->freq = ideal_chan->freq;
 
-	if (iface->conf->ieee80211ac)
-		acs_adjust_vht_center_freq(iface);
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
+		acs_adjust_center_freq(iface);
 
 	err = 0;
 fail:
@@ -942,6 +943,12 @@
 	}
 	*freq = 0;
 
+	if (params.freqs == freq) {
+		wpa_printf(MSG_ERROR, "ACS: No available channels found");
+		os_free(params.freqs);
+		return -1;
+	}
+
 	iface->scan_cb = acs_scan_complete;
 
 	wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
new file mode 100644
index 0000000..f56ca5b
--- /dev/null
+++ b/src/ap/airtime_policy.c
@@ -0,0 +1,269 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "airtime_policy.h"
+
+/* Idea:
+ * Two modes of airtime enforcement:
+ * 1. Static weights: specify weights per MAC address with a per-BSS default
+ * 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
+ *    enforce relative total shares between BSSes.
+ *
+ * - Periodic per-station callback to update queue status.
+ *
+ * Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
+ * keep them updated in sta_info.
+ *
+ * - Separate periodic per-bss (or per-iface?) callback to update weights.
+ *
+ * Just need to loop through all interfaces, count sum the active stations (or
+ * should the per-STA callback just adjust that for the BSS?) and calculate new
+ * weights.
+ */
+
+static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
+					     unsigned int *sec,
+					     unsigned int *usec)
+{
+	unsigned int update_int = iface->conf->airtime_update_interval;
+
+	if (!update_int) {
+		wpa_printf(MSG_ERROR,
+			   "Airtime policy: Invalid airtime policy update interval %u",
+			   update_int);
+		return -1;
+	}
+
+	*sec = update_int / 1000;
+	*usec = (update_int % 1000) * 1000;
+
+	return 0;
+}
+
+
+static void set_new_backlog_time(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 struct os_reltime *now)
+{
+	sta->backlogged_until = *now;
+	sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
+		AIRTIME_BACKLOG_EXPIRY_FACTOR;
+	while (sta->backlogged_until.usec >= 1000000) {
+		sta->backlogged_until.sec++;
+		sta->backlogged_until.usec -= 1000000;
+	}
+}
+
+
+static void count_backlogged_sta(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	struct hostap_sta_driver_data data = {};
+	unsigned int num_backlogged = 0;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
+			continue;
+
+		if (data.backlog_bytes > 0)
+			set_new_backlog_time(hapd, sta, &now);
+		if (os_reltime_before(&now, &sta->backlogged_until))
+			num_backlogged++;
+	}
+	hapd->num_backlogged_sta = num_backlogged;
+}
+
+
+static int sta_set_airtime_weight(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  unsigned int weight)
+{
+	int ret = 0;
+
+	if (weight != sta->airtime_weight &&
+	    (ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
+		return ret;
+
+	sta->airtime_weight = weight;
+	return ret;
+}
+
+
+static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next)
+		sta_set_airtime_weight(hapd, sta, weight);
+}
+
+
+static unsigned int get_airtime_quantum(unsigned int max_wt)
+{
+	unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
+
+	if (quantum < AIRTIME_QUANTUM_MIN)
+		quantum = AIRTIME_QUANTUM_MIN;
+	else if (quantum > AIRTIME_QUANTUM_MAX)
+		quantum = AIRTIME_QUANTUM_MAX;
+
+	return quantum;
+}
+
+
+static void update_airtime_weights(void *eloop_data, void *user_data)
+{
+	struct hostapd_iface *iface = eloop_data;
+	struct hostapd_data *bss;
+	unsigned int sec, usec;
+	unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
+		wt_sum = 0;
+	unsigned int quantum;
+	Boolean all_div_min = TRUE;
+	Boolean apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
+	int wt, num_bss = 0, max_wt = 0;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+
+		count_backlogged_sta(bss);
+		if (!bss->num_backlogged_sta)
+			continue;
+
+		if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
+			num_sta_min = bss->num_backlogged_sta;
+
+		num_sta_prod *= bss->num_backlogged_sta;
+		num_sta_sum += bss->num_backlogged_sta;
+		wt_sum += bss->conf->airtime_weight;
+		num_bss++;
+	}
+
+	if (num_sta_min) {
+		for (i = 0; i < iface->num_bss; i++) {
+			bss = iface->bss[i];
+			if (!bss->started || !bss->conf->airtime_weight)
+				continue;
+
+			/* Check if we can divide all sta numbers by the
+			 * smallest number to keep weights as small as possible.
+			 * This is a lazy way to avoid having to factor
+			 * integers. */
+			if (bss->num_backlogged_sta &&
+			    bss->num_backlogged_sta % num_sta_min > 0)
+				all_div_min = FALSE;
+
+			/* If we're in LIMIT mode, we only apply the weight
+			 * scaling when the BSS(es) marked as limited would a
+			 * larger share than the relative BSS weights indicates
+			 * it should. */
+			if (!apply_limit && bss->conf->airtime_limit) {
+				if (bss->num_backlogged_sta * wt_sum >
+				    bss->conf->airtime_weight * num_sta_sum)
+					apply_limit = TRUE;
+			}
+		}
+		if (all_div_min)
+			num_sta_prod /= num_sta_min;
+	}
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+
+		/* We only set the calculated weight if the BSS has active
+		 * stations and there are other active interfaces as well -
+		 * otherwise we just set a unit weight. This ensures that
+		 * the weights are set reasonably when stations transition from
+		 * inactive to active. */
+		if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
+			wt = bss->conf->airtime_weight * num_sta_prod /
+				bss->num_backlogged_sta;
+		else
+			wt = 1;
+
+		bss->airtime_weight = wt;
+		if (wt > max_wt)
+			max_wt = wt;
+	}
+
+	quantum = get_airtime_quantum(max_wt);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+		set_sta_weights(bss, bss->airtime_weight * quantum);
+	}
+
+	if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+		return;
+
+	eloop_register_timeout(sec, usec, update_airtime_weights, iface,
+			       NULL);
+}
+
+
+static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+	struct airtime_sta_weight *wt;
+
+	wt = hapd->conf->airtime_weight_list;
+	while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
+		wt = wt->next;
+
+	return wt ? wt->weight : hapd->conf->airtime_weight;
+}
+
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	unsigned int weight;
+
+	if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+		weight = get_weight_for_sta(hapd, sta->addr);
+		if (weight)
+			return sta_set_airtime_weight(hapd, sta, weight);
+	}
+	return 0;
+}
+
+
+int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+	unsigned int sec, usec;
+
+	if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
+		return 0;
+
+	if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+		return -1;
+
+	eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
+	return 0;
+}
+
+
+void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+	eloop_cancel_timeout(update_airtime_weights, iface, NULL);
+}
diff --git a/src/ap/airtime_policy.h b/src/ap/airtime_policy.h
new file mode 100644
index 0000000..c2a9b00
--- /dev/null
+++ b/src/ap/airtime_policy.h
@@ -0,0 +1,48 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AIRTIME_POLICY_H
+#define AIRTIME_POLICY_H
+
+struct hostapd_iface;
+
+#ifdef CONFIG_AIRTIME_POLICY
+
+#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
+#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
+
+/* scale quantum so this becomes the effective quantum after applying the max
+ * weight, but never go below min or above max */
+#define AIRTIME_QUANTUM_MIN 8 /* usec */
+#define AIRTIME_QUANTUM_MAX 256 /* usec */
+#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
+int airtime_policy_update_init(struct hostapd_iface *iface);
+void airtime_policy_update_deinit(struct hostapd_iface *iface);
+
+#else /* CONFIG_AIRTIME_POLICY */
+
+static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
+					 struct sta_info *sta)
+{
+	return -1;
+}
+
+static inline int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+	return -1;
+}
+
+static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+}
+
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#endif /* AIRTIME_POLICY_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index e640e99..68af3c1 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -13,12 +13,15 @@
 #include "crypto/tls.h"
 #include "radius/radius_client.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_1x_defs.h"
 #include "common/eapol_common.h"
 #include "common/dhcp.h"
+#include "common/sae.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_server/eap.h"
 #include "wpa_auth.h"
 #include "sta_info.h"
+#include "airtime_policy.h"
 #include "ap_config.h"
 
 
@@ -76,6 +79,7 @@
 
 	bss->radius_server_auth_port = 1812;
 	bss->eap_sim_db_timeout = 1;
+	bss->eap_sim_id = 3;
 	bss->ap_max_inactivity = AP_MAX_INACTIVITY;
 	bss->eapol_version = EAPOL_VERSION;
 
@@ -83,11 +87,9 @@
 
 	bss->pwd_group = 19; /* ECC: GF(p=256) */
 
-#ifdef CONFIG_IEEE80211W
 	bss->assoc_sa_query_max_timeout = 1000;
 	bss->assoc_sa_query_retry_timeout = 201;
 	bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
-#endif /* CONFIG_IEEE80211W */
 #ifdef EAP_SERVER_FAST
 	 /* both anonymous and authenticated provisioning */
 	bss->eap_fast_prov = 3;
@@ -132,12 +134,20 @@
 	 * completed and tested with other implementations. */
 	bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
 
+	bss->max_auth_rounds = 100;
+	bss->max_auth_rounds_short = 50;
+
 	bss->send_probe_response = 1;
 
 #ifdef CONFIG_HS20
 	bss->hs20_release = (HS20_VERSION >> 4) + 1;
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MACSEC
+	bss->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+	bss->macsec_port = 1;
+#endif /* CONFIG_MACSEC */
+
 	/* Default to strict CRL checking. */
 	bss->check_crl_strict = 1;
 }
@@ -236,6 +246,13 @@
 	conf->acs_num_scans = 5;
 #endif /* CONFIG_ACS */
 
+#ifdef CONFIG_IEEE80211AX
+	conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+		HE_OPERATION_RTS_THRESHOLD_OFFSET;
+	/* Set default basic MCS/NSS set to single stream MCS 0-7 */
+	conf->he_op.he_basic_mcs_nss_set = 0xfffc;
+#endif /* CONFIG_IEEE80211AX */
+
 	/* The third octet of the country string uses an ASCII space character
 	 * by default to indicate that the regulations encompass all
 	 * environments for the current frequency band in the country. */
@@ -244,6 +261,10 @@
 	conf->rssi_reject_assoc_rssi = 0;
 	conf->rssi_reject_assoc_timeout = 30;
 
+#ifdef CONFIG_AIRTIME_POLICY
+	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+#endif /* CONFIG_AIRTIME_POLICY */
+
 	return conf;
 }
 
@@ -414,10 +435,50 @@
 }
 
 
+int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_SAE
+	struct hostapd_ssid *ssid = &conf->ssid;
+	struct sae_password_entry *pw;
+
+	if (conf->sae_pwe == 0 || !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
+		return 0; /* PT not needed */
+
+	sae_deinit_pt(ssid->pt);
+	ssid->pt = NULL;
+	if (ssid->wpa_passphrase) {
+		ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
+					 ssid->ssid_len,
+					 (const u8 *) ssid->wpa_passphrase,
+					 os_strlen(ssid->wpa_passphrase),
+					 NULL);
+		if (!ssid->pt)
+			return -1;
+	}
+
+	for (pw = conf->sae_passwords; pw; pw = pw->next) {
+		sae_deinit_pt(pw->pt);
+		pw->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
+				       ssid->ssid_len,
+				       (const u8 *) pw->password,
+				       os_strlen(pw->password),
+				       pw->identifier);
+		if (!pw->pt)
+			return -1;
+	}
+#endif /* CONFIG_SAE */
+
+	return 0;
+}
+
+
 int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
 {
 	struct hostapd_ssid *ssid = &conf->ssid;
 
+	if (hostapd_setup_sae_pt(conf) < 0)
+		return -1;
+
 	if (ssid->wpa_passphrase != NULL) {
 		if (ssid->wpa_psk != NULL) {
 			wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
@@ -458,7 +519,76 @@
 }
 
 
-static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
+struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value)
+{
+	const char *pos;
+	char syntax;
+	struct hostapd_radius_attr *attr;
+	size_t len;
+
+	attr = os_zalloc(sizeof(*attr));
+	if (!attr)
+		return NULL;
+
+	attr->type = atoi(value);
+
+	pos = os_strchr(value, ':');
+	if (!pos) {
+		attr->val = wpabuf_alloc(1);
+		if (!attr->val) {
+			os_free(attr);
+			return NULL;
+		}
+		wpabuf_put_u8(attr->val, 0);
+		return attr;
+	}
+
+	pos++;
+	if (pos[0] == '\0' || pos[1] != ':') {
+		os_free(attr);
+		return NULL;
+	}
+	syntax = *pos++;
+	pos++;
+
+	switch (syntax) {
+	case 's':
+		attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
+		break;
+	case 'x':
+		len = os_strlen(pos);
+		if (len & 1)
+			break;
+		len /= 2;
+		attr->val = wpabuf_alloc(len);
+		if (!attr->val)
+			break;
+		if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
+			wpabuf_free(attr->val);
+			os_free(attr);
+			return NULL;
+		}
+		break;
+	case 'd':
+		attr->val = wpabuf_alloc(4);
+		if (attr->val)
+			wpabuf_put_be32(attr->val, atoi(pos));
+		break;
+	default:
+		os_free(attr);
+		return NULL;
+	}
+
+	if (!attr->val) {
+		os_free(attr);
+		return NULL;
+	}
+
+	return attr;
+}
+
+
+void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
 {
 	struct hostapd_radius_attr *prev;
 
@@ -554,13 +684,34 @@
 		pw = pw->next;
 		str_clear_free(tmp->password);
 		os_free(tmp->identifier);
+#ifdef CONFIG_SAE
+		sae_deinit_pt(tmp->pt);
+#endif /* CONFIG_SAE */
 		os_free(tmp);
 	}
 }
 
 
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_controller_conf_free(struct dpp_controller_conf *conf)
+{
+	struct dpp_controller_conf *prev;
+
+	while (conf) {
+		prev = conf;
+		conf = conf->next;
+		os_free(prev);
+	}
+}
+#endif /* CONFIG_DPP2 */
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
+#if defined(CONFIG_WPS) || defined(CONFIG_HS20)
+	size_t i;
+#endif
+
 	if (conf == NULL)
 		return;
 
@@ -572,6 +723,9 @@
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 	os_free(conf->ssid.vlan_tagged_interface);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+#ifdef CONFIG_SAE
+	sae_deinit_pt(conf->ssid.pt);
+#endif /* CONFIG_SAE */
 
 	hostapd_config_free_eap_users(conf->eap_user);
 	os_free(conf->eap_user_sqlite);
@@ -589,12 +743,16 @@
 	}
 	hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
 	hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
+	os_free(conf->radius_req_attr_sqlite);
 	os_free(conf->rsn_preauth_interfaces);
 	os_free(conf->ctrl_interface);
 	os_free(conf->ca_cert);
 	os_free(conf->server_cert);
+	os_free(conf->server_cert2);
 	os_free(conf->private_key);
+	os_free(conf->private_key2);
 	os_free(conf->private_key_passwd);
+	os_free(conf->private_key_passwd2);
 	os_free(conf->check_cert_subject);
 	os_free(conf->ocsp_stapling_response);
 	os_free(conf->ocsp_stapling_response_multi);
@@ -653,12 +811,8 @@
 	os_free(conf->model_description);
 	os_free(conf->model_url);
 	os_free(conf->upc);
-	{
-		unsigned int i;
-
-		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
-			wpabuf_free(conf->wps_vendor_ext[i]);
-	}
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+		wpabuf_free(conf->wps_vendor_ext[i]);
 	wpabuf_free(conf->wps_nfc_dh_pubkey);
 	wpabuf_free(conf->wps_nfc_dh_privkey);
 	wpabuf_free(conf->wps_nfc_dev_pw);
@@ -684,7 +838,6 @@
 	os_free(conf->hs20_operating_class);
 	os_free(conf->hs20_icons);
 	if (conf->hs20_osu_providers) {
-		size_t i;
 		for (i = 0; i < conf->hs20_osu_providers_count; i++) {
 			struct hs20_osu_provider *p;
 			size_t j;
@@ -702,8 +855,6 @@
 		os_free(conf->hs20_osu_providers);
 	}
 	if (conf->hs20_operator_icon) {
-		size_t i;
-
 		for (i = 0; i < conf->hs20_operator_icon_count; i++)
 			os_free(conf->hs20_operator_icon[i]);
 		os_free(conf->hs20_operator_icon);
@@ -729,6 +880,7 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	wpabuf_free(conf->own_ie_override);
 	wpabuf_free(conf->sae_commit_override);
+	wpabuf_free(conf->rsnxe_override_eapol);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	os_free(conf->no_probe_resp_if_seen_on);
@@ -737,13 +889,32 @@
 	hostapd_config_free_fils_realms(conf);
 
 #ifdef CONFIG_DPP
+	os_free(conf->dpp_name);
+	os_free(conf->dpp_mud_url);
 	os_free(conf->dpp_connector);
 	wpabuf_free(conf->dpp_netaccesskey);
 	wpabuf_free(conf->dpp_csign);
+#ifdef CONFIG_DPP2
+	hostapd_dpp_controller_conf_free(conf->dpp_controller);
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 
 	hostapd_config_free_sae_passwords(conf);
 
+#ifdef CONFIG_AIRTIME_POLICY
+	{
+		struct airtime_sta_weight *wt, *wt_prev;
+
+		wt = conf->airtime_weight_list;
+		conf->airtime_weight_list = NULL;
+		while (wt) {
+			wt_prev = wt;
+			wt = wt->next;
+			os_free(wt_prev);
+		}
+	}
+#endif /* CONFIG_AIRTIME_POLICY */
+
 	os_free(conf);
 }
 
@@ -765,6 +936,7 @@
 	os_free(conf->supported_rates);
 	os_free(conf->basic_rates);
 	os_free(conf->acs_ch_list.range);
+	os_free(conf->acs_freq_list.range);
 	os_free(conf->driver_params);
 #ifdef CONFIG_ACS
 	os_free(conf->acs_chan_bias);
@@ -1140,6 +1312,13 @@
 		return -1;
 	}
 
+#ifdef CONFIG_AIRTIME_POLICY
+	if (full_config && conf->airtime_mode > AIRTIME_MODE_STATIC &&
+	    !conf->airtime_update_interval) {
+		wpa_printf(MSG_ERROR, "Airtime update interval cannot be zero");
+		return -1;
+	}
+#endif /* CONFIG_AIRTIME_POLICY */
 	for (i = 0; i < NUM_TX_QUEUES; i++) {
 		if (hostapd_config_check_cw(conf, i))
 			return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 509677a..83b8aee 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -15,6 +15,7 @@
 #include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "crypto/sha256.h"
 #include "wps/wps.h"
 #include "fst/fst.h"
 #include "vlan.h"
@@ -98,6 +99,7 @@
 	struct hostapd_wpa_psk *wpa_psk;
 	char *wpa_passphrase;
 	char *wpa_psk_file;
+	struct sae_pt *pt;
 
 	struct hostapd_wep_keys wep;
 
@@ -250,6 +252,19 @@
 	char *identifier;
 	u8 peer_addr[ETH_ALEN];
 	int vlan_id;
+	struct sae_pt *pt;
+};
+
+struct dpp_controller_conf {
+	struct dpp_controller_conf *next;
+	u8 pkhash[SHA256_MAC_LEN];
+	struct hostapd_ip_addr ipaddr;
+};
+
+struct airtime_sta_weight {
+	struct airtime_sta_weight *next;
+	unsigned int weight;
+	u8 addr[ETH_ALEN];
 };
 
 /**
@@ -288,6 +303,7 @@
 	int radius_request_cui;
 	struct hostapd_radius_attr *radius_auth_req_attr;
 	struct hostapd_radius_attr *radius_acct_req_attr;
+	char *radius_req_attr_sqlite;
 	int radius_das_port;
 	unsigned int radius_das_time_window;
 	int radius_das_require_event_timestamp;
@@ -311,10 +327,6 @@
 	int erp_send_reauth_start;
 	char *erp_domain;
 
-	int ieee802_11f; /* use IEEE 802.11f (IAPP) */
-	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
-					* frames */
-
 	enum macaddr_acl {
 		ACCEPT_UNLESS_DENIED = 0,
 		DENY_UNLESS_ACCEPTED = 1,
@@ -333,14 +345,12 @@
 
 	int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
 	int wpa_key_mgmt;
-#ifdef CONFIG_IEEE80211W
 	enum mfp_options ieee80211w;
 	int group_mgmt_cipher;
 	/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
 	unsigned int assoc_sa_query_max_timeout;
 	/* dot11AssociationSAQueryRetryTimeout (in TUs) */
 	int assoc_sa_query_retry_timeout;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	int ocv; /* Operating Channel Validation */
 #endif /* CONFIG_OCV */
@@ -390,14 +400,19 @@
 
 	char *ca_cert;
 	char *server_cert;
+	char *server_cert2;
 	char *private_key;
+	char *private_key2;
 	char *private_key_passwd;
+	char *private_key_passwd2;
 	char *check_cert_subject;
 	int check_crl;
 	int check_crl_strict;
 	unsigned int crl_reload_interval;
 	unsigned int tls_session_lifetime;
 	unsigned int tls_flags;
+	unsigned int max_auth_rounds;
+	unsigned int max_auth_rounds_short;
 	char *ocsp_stapling_response;
 	char *ocsp_stapling_response_multi;
 	char *dh_file;
@@ -410,7 +425,12 @@
 	int eap_fast_prov;
 	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_sim_aka_result_ind;
+	int eap_sim_id;
 	int tnc;
 	int fragment_size;
 	u16 pwd_group;
@@ -570,6 +590,7 @@
 	int osen;
 	int proxy_arp;
 	int na_mcast_to_ucast;
+
 #ifdef CONFIG_HS20
 	int hs20;
 	int hs20_release;
@@ -630,6 +651,8 @@
 	unsigned int sae_anti_clogging_threshold;
 	unsigned int sae_sync;
 	int sae_require_mfp;
+	int sae_confirm_immediate;
+	int sae_pwe;
 	int *sae_groups;
 	struct sae_password_entry *sae_passwords;
 
@@ -641,6 +664,7 @@
 	struct wpabuf *own_ie_override;
 	int sae_reflection_attack;
 	struct wpabuf *sae_commit_override;
+	struct wpabuf *rsnxe_override_eapol;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #define MESH_ENABLED BIT(0)
@@ -688,10 +712,15 @@
 	int broadcast_deauth;
 
 #ifdef CONFIG_DPP
+	char *dpp_name;
+	char *dpp_mud_url;
 	char *dpp_connector;
 	struct wpabuf *dpp_netaccesskey;
 	unsigned int dpp_netaccesskey_expiry;
 	struct wpabuf *dpp_csign;
+#ifdef CONFIG_DPP2
+	struct dpp_controller_conf *dpp_controller;
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 
 #ifdef CONFIG_OWE
@@ -709,6 +738,100 @@
 #define BACKHAUL_BSS 1
 #define FRONTHAUL_BSS 2
 	int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
+
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_weight;
+	int airtime_limit;
+	struct airtime_sta_weight *airtime_weight_list;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_MACSEC
+	/**
+	 * macsec_policy - Determines the policy for MACsec secure session
+	 *
+	 * 0: MACsec not in use (default)
+	 * 1: MACsec enabled - Should secure, accept key server's advice to
+	 *    determine whether to use a secure session or not.
+	 */
+	int macsec_policy;
+
+	/**
+	 * macsec_integ_only - Determines how MACsec are transmitted
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: Encrypt traffic (default)
+	 * 1: Integrity only
+	 */
+	int macsec_integ_only;
+
+	/**
+	 * macsec_replay_protect - Enable MACsec replay protection
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: Replay protection disabled (default)
+	 * 1: Replay protection enabled
+	 */
+	int macsec_replay_protect;
+
+	/**
+	 * macsec_replay_window - MACsec replay protection window
+	 *
+	 * A window in which replay is tolerated, to allow receipt of frames
+	 * that have been misordered by the network.
+	 *
+	 * This setting applies only when MACsec replay protection active, i.e.,
+	 *  - macsec_replay_protect is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: No replay window, strict check (default)
+	 * 1..2^32-1: number of packets that could be misordered
+	 */
+	u32 macsec_replay_window;
+
+	/**
+	 * macsec_port - MACsec port (in SCI)
+	 *
+	 * Port component of the SCI.
+	 *
+	 * Range: 1-65534 (default: 1)
+	 */
+	int macsec_port;
+
+	/**
+	 * mka_priority - Priority of MKA Actor
+	 *
+	 * Range: 0-255 (default: 255)
+	 */
+	int mka_priority;
+
+	/**
+	 * mka_ckn - MKA pre-shared CKN
+	 */
+#define MACSEC_CKN_MAX_LEN 32
+	size_t mka_ckn_len;
+	u8 mka_ckn[MACSEC_CKN_MAX_LEN];
+
+	/**
+	 * mka_cak - MKA pre-shared CAK
+	 */
+#define MACSEC_CAK_MAX_LEN 32
+	size_t mka_cak_len;
+	u8 mka_cak[MACSEC_CAK_MAX_LEN];
+
+#define MKA_PSK_SET_CKN BIT(0)
+#define MKA_PSK_SET_CAK BIT(1)
+#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
+	/**
+	 * mka_psk_set - Whether mka_ckn and mka_cak are set
+	 */
+	u8 mka_psk_set;
+#endif /* CONFIG_MACSEC */
 };
 
 /**
@@ -727,7 +850,20 @@
 	u8 he_bss_color;
 	u8 he_default_pe_duration;
 	u8 he_twt_required;
-	u8 he_rts_threshold;
+	u16 he_rts_threshold;
+	u16 he_basic_mcs_nss_set;
+};
+
+/**
+ * struct spatial_reuse - Spatial reuse
+ */
+struct spatial_reuse {
+	u8 sr_control;
+	u8 non_srg_obss_pd_max_offset;
+	u8 srg_obss_pd_min_offset;
+	u8 srg_obss_pd_max_offset;
+	u8 srg_obss_color_bitmap;
+	u8 srg_obss_color_partial_bitmap;
 };
 
 /**
@@ -740,11 +876,17 @@
 	u16 beacon_int;
 	int rts_threshold;
 	int fragm_threshold;
+	u8 op_class;
 	u8 channel;
+	int enable_edmg;
+	u8 edmg_channel;
 	u8 acs;
 	struct wpa_freq_range_list acs_ch_list;
+	struct wpa_freq_range_list acs_freq_list;
+	u8 acs_freq_list_present;
 	int acs_exclude_dfs;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+	int acs_exclude_6ghz_non_psc;
 	enum {
 		LONG_PREAMBLE = 0,
 		SHORT_PREAMBLE = 1
@@ -852,6 +994,10 @@
 	struct he_phy_capabilities_info he_phy_capab;
 	struct he_operation he_op;
 	struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
+	struct spatial_reuse spr;
+	u8 he_oper_chwidth;
+	u8 he_oper_centr_freq_seg0_idx;
+	u8 he_oper_centr_freq_seg1_idx;
 #endif /* CONFIG_IEEE80211AX */
 
 	/* VHT enable/disable config from CHAN_SWITCH */
@@ -861,12 +1007,87 @@
 
 	int rssi_reject_assoc_rssi;
 	int rssi_reject_assoc_timeout;
+
+#ifdef CONFIG_AIRTIME_POLICY
+	enum {
+		AIRTIME_MODE_OFF = 0,
+		AIRTIME_MODE_STATIC = 1,
+		AIRTIME_MODE_DYNAMIC = 2,
+		AIRTIME_MODE_LIMIT = 3,
+		__AIRTIME_MODE_MAX,
+	} airtime_mode;
+	unsigned int airtime_update_interval;
+#define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1)
+#endif /* CONFIG_AIRTIME_POLICY */
 };
 
 
+static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_chwidth;
+}
+
+static inline void
+hostapd_set_oper_chwidth(struct hostapd_config *conf, u8 oper_chwidth)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_chwidth = oper_chwidth;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_centr_freq_seg0_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
+				     u8 oper_centr_freq_seg0_idx)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg1_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_centr_freq_seg1_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
+				     u8 oper_centr_freq_seg1_idx)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+}
+
+
 int hostapd_mac_comp(const void *a, const void *b);
 struct hostapd_config * hostapd_config_defaults(void);
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr);
 void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
 void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
 void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
@@ -885,9 +1106,11 @@
 					int vlan_id);
 struct hostapd_radius_attr *
 hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
+struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value);
 int hostapd_config_check(struct hostapd_config *conf, int full_config);
 void hostapd_set_security_params(struct hostapd_bss_config *bss,
 				 int full_config);
 int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
+int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
 
 #endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 067cf86..7585866 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/hw_features_common.h"
 #include "wps/wps.h"
 #include "p2p/p2p.h"
@@ -107,6 +108,10 @@
 		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 (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
 	    add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
 		goto fail;
@@ -305,9 +310,7 @@
 			params.wpa_pairwise = hapd->conf->wpa_pairwise;
 		params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
 		params.rsn_preauth = hapd->conf->rsn_preauth;
-#ifdef CONFIG_IEEE80211W
 		params.ieee80211w = hapd->conf->ieee80211w;
-#endif /* CONFIG_IEEE80211W */
 	}
 	return hostapd_set_ieee8021x(hapd, &params);
 }
@@ -348,7 +351,7 @@
 			 u16 auth_alg)
 {
 	if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
-		return 0;
+		return -EOPNOTSUPP;
 	return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
 }
 
@@ -413,6 +416,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
+		    const struct ieee80211_he_capabilities *he_capab,
+		    size_t he_capab_len,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set)
 {
@@ -432,6 +437,8 @@
 	params.listen_interval = listen_interval;
 	params.ht_capabilities = ht_capab;
 	params.vht_capabilities = vht_capab;
+	params.he_capab = he_capab;
+	params.he_capab_len = he_capab_len;
 	params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
 	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
@@ -536,18 +543,23 @@
 
 
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
-		     int freq, int channel, int ht_enabled, int vht_enabled,
-		     int sec_channel_offset, int vht_oper_chwidth,
+		     int freq, int channel, int edmg, u8 edmg_channel,
+		     int ht_enabled, int vht_enabled,
+		     int he_enabled,
+		     int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1)
 {
 	struct hostapd_freq_params data;
+	struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
 
-	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
-				    vht_enabled, sec_channel_offset,
-				    vht_oper_chwidth,
+	if (hostapd_set_freq_params(&data, mode, freq, channel, edmg,
+				    edmg_channel, ht_enabled,
+				    vht_enabled, he_enabled, sec_channel_offset,
+				    oper_chwidth,
 				    center_segment0, center_segment1,
-				    hapd->iface->current_mode ?
-				    hapd->iface->current_mode->vht_capab : 0))
+				    cmode ? cmode->vht_capab : 0,
+				    cmode ?
+				    &cmode->he_capab[IEEE80211_MODE_AP] : NULL))
 		return -1;
 
 	if (hapd->driver == NULL)
@@ -583,6 +595,16 @@
 }
 
 
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+				   unsigned int weight)
+{
+	if (!hapd->driver || !hapd->driver->sta_set_airtime_weight)
+		return 0;
+	return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr,
+						    weight);
+}
+
+
 int hostapd_set_country(struct hostapd_data *hapd, const char *country)
 {
 	if (hapd->driver == NULL ||
@@ -775,14 +797,16 @@
 int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
-			  int sec_channel_offset, int vht_oper_chwidth,
+			  int he_enabled,
+			  int sec_channel_offset, int oper_chwidth,
 			  int center_segment0, int center_segment1)
 {
 	struct hostapd_data *hapd = iface->bss[0];
 	struct hostapd_freq_params data;
 	int res;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
 
-	if (!hapd->driver || !hapd->driver->start_dfs_cac)
+	if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
 		return 0;
 
 	if (!iface->conf->ieee80211h) {
@@ -791,11 +815,13 @@
 		return -1;
 	}
 
-	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
-				    vht_enabled, sec_channel_offset,
-				    vht_oper_chwidth, center_segment0,
+	if (hostapd_set_freq_params(&data, mode, freq, channel, 0, 0,
+				    ht_enabled,
+				    vht_enabled, he_enabled, sec_channel_offset,
+				    oper_chwidth, center_segment0,
 				    center_segment1,
-				    iface->current_mode->vht_capab)) {
+				    cmode->vht_capab,
+				    &cmode->he_capab[IEEE80211_MODE_AP])) {
 		wpa_printf(MSG_ERROR, "Can't set freq params");
 		return -1;
 	}
@@ -830,10 +856,24 @@
 	for (i = 0; i < mode->num_channels; i++) {
 		struct hostapd_channel_data *chan = &mode->channels[i];
 
-		if ((acs_ch_list_all ||
-		     freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
-					      chan->chan)) &&
-		    !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		if (!acs_ch_list_all &&
+		    (hapd->iface->conf->acs_freq_list.num &&
+		     !freq_range_list_includes(
+			     &hapd->iface->conf->acs_freq_list,
+			     chan->freq)))
+			continue;
+		if (!acs_ch_list_all &&
+		    (!hapd->iface->conf->acs_freq_list_present &&
+		     hapd->iface->conf->acs_ch_list.num &&
+		     !freq_range_list_includes(
+			     &hapd->iface->conf->acs_ch_list,
+			     chan->chan)))
+			continue;
+		if (is_6ghz_freq(chan->freq) &&
+		    hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+		    !is_6ghz_psc_frequency(chan->freq))
+			continue;
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
 		    !(hapd->iface->conf->acs_exclude_dfs &&
 		      (chan->flag & HOSTAPD_CHAN_RADAR)))
 			int_array_add_unique(freq_list, chan->freq);
@@ -859,10 +899,9 @@
 {
 	struct drv_acs_params params;
 	int ret, i, acs_ch_list_all = 0;
-	u8 *channels = NULL;
-	unsigned int num_channels = 0;
 	struct hostapd_hw_modes *mode;
 	int *freq_list = NULL;
+	enum hostapd_hw_mode selected_mode;
 
 	if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
 		return 0;
@@ -874,41 +913,25 @@
 	 * If no chanlist config parameter is provided, include all enabled
 	 * channels of the selected hw_mode.
 	 */
-	if (!hapd->iface->conf->acs_ch_list.num)
-		acs_ch_list_all = 1;
+	if (hapd->iface->conf->acs_freq_list_present)
+		acs_ch_list_all = !hapd->iface->conf->acs_freq_list.num;
+	else
+		acs_ch_list_all = !hapd->iface->conf->acs_ch_list.num;
 
-	mode = hapd->iface->current_mode;
-	if (mode) {
-		channels = os_malloc(mode->num_channels);
-		if (channels == NULL)
-			return -1;
+	if (hapd->iface->current_mode)
+		selected_mode = hapd->iface->current_mode->mode;
+	else
+		selected_mode = HOSTAPD_MODE_IEEE80211ANY;
 
-		for (i = 0; i < mode->num_channels; i++) {
-			struct hostapd_channel_data *chan = &mode->channels[i];
-			if (!acs_ch_list_all &&
-			    !freq_range_list_includes(
-				    &hapd->iface->conf->acs_ch_list,
-				    chan->chan))
-				continue;
-			if (hapd->iface->conf->acs_exclude_dfs &&
-			    (chan->flag & HOSTAPD_CHAN_RADAR))
-				continue;
-			if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
-				channels[num_channels++] = chan->chan;
-				int_array_add_unique(&freq_list, chan->freq);
-			}
-		}
-	} else {
-		for (i = 0; i < hapd->iface->num_hw_features; i++) {
-			mode = &hapd->iface->hw_features[i];
-			hostapd_get_hw_mode_any_channels(hapd, mode,
-							 acs_ch_list_all,
-							 &freq_list);
-		}
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		mode = &hapd->iface->hw_features[i];
+		if (selected_mode != HOSTAPD_MODE_IEEE80211ANY &&
+		    selected_mode != mode->mode)
+			continue;
+		hostapd_get_hw_mode_any_channels(hapd, mode, acs_ch_list_all,
+						 &freq_list);
 	}
 
-	params.ch_list = channels;
-	params.ch_list_len = num_channels;
 	params.freq_list = freq_list;
 
 	params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
@@ -919,20 +942,35 @@
 	if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
 		params.ch_width = 40;
 
-	/* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
+	/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
 	 */
-	if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
-		if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+	if ((hapd->iface->conf->ieee80211ax ||
+	     hapd->iface->conf->ieee80211ac) &&
+	    params.ht40_enabled) {
+		u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
+
+		if (oper_chwidth == CHANWIDTH_80MHZ)
 			params.ch_width = 80;
-		else if (hapd->iface->conf->vht_oper_chwidth ==
-			 VHT_CHANWIDTH_160MHZ ||
-			 hapd->iface->conf->vht_oper_chwidth ==
-			 VHT_CHANWIDTH_80P80MHZ)
+		else if (oper_chwidth == CHANWIDTH_160MHZ ||
+			 oper_chwidth == CHANWIDTH_80P80MHZ)
 			params.ch_width = 160;
 	}
 
+	if (hapd->iface->conf->op_class)
+		params.ch_width = op_class_to_bandwidth(
+			hapd->iface->conf->op_class);
 	ret = hapd->driver->do_acs(hapd->drv_priv, &params);
-	os_free(channels);
+	os_free(freq_list);
 
 	return ret;
 }
+
+
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+			     u16 reason_code, const u8 *ie, size_t ielen)
+{
+	if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code,
+					  ie, ielen);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index de40171..fa413df 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -41,6 +41,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
+		    const struct ieee80211_he_capabilities *he_capab,
+		    size_t he_capab_len,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
@@ -60,13 +62,16 @@
 		       const u8 *addr, int idx, u8 *seq);
 int hostapd_flush(struct hostapd_data *hapd);
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
-		     int freq, int channel, int ht_enabled, int vht_enabled,
-		     int sec_channel_offset, int vht_oper_chwidth,
+		     int freq, int channel, int edmg, u8 edmg_channel,
+		     int ht_enabled, int vht_enabled,
+		     int he_enabled, int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
 int hostapd_set_frag(struct hostapd_data *hapd, int frag);
 int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
 			  int total_flags, int flags_or, int flags_and);
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+				   unsigned int weight);
 int hostapd_set_country(struct hostapd_data *hapd, const char *country);
 int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
 				int cw_min, int cw_max, int burst_time);
@@ -122,9 +127,12 @@
 int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
-			  int sec_channel_offset, int vht_oper_chwidth,
+			  int he_enabled,
+			  int sec_channel_offset, int oper_chwidth,
 			  int center_segment0, int center_segment1);
 int hostapd_drv_do_acs(struct hostapd_data *hapd);
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+			     u16 reason_code, const u8 *ie, size_t ielen);
 
 
 #include "drivers/driver.h"
@@ -374,4 +382,12 @@
 	return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
 }
 
+static inline int
+hostapd_drv_set_band(struct hostapd_data *hapd, enum set_band band)
+{
+	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_band)
+		return -1;
+	return hapd->driver->set_band(hapd->drv_priv, band);
+}
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index eced6c7..8e12daf 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -110,25 +110,10 @@
 	srv.auth_port = conf->radius_server_auth_port;
 	srv.acct_port = conf->radius_server_acct_port;
 	srv.conf_ctx = hapd;
-	srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
-	srv.ssl_ctx = hapd->ssl_ctx;
-	srv.msg_ctx = hapd->msg_ctx;
-	srv.pac_opaque_encr_key = conf->pac_opaque_encr_key;
-	srv.eap_fast_a_id = conf->eap_fast_a_id;
-	srv.eap_fast_a_id_len = conf->eap_fast_a_id_len;
-	srv.eap_fast_a_id_info = conf->eap_fast_a_id_info;
-	srv.eap_fast_prov = conf->eap_fast_prov;
-	srv.pac_key_lifetime = conf->pac_key_lifetime;
-	srv.pac_key_refresh_time = conf->pac_key_refresh_time;
-	srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
-	srv.tnc = conf->tnc;
-	srv.wps = hapd->wps;
 	srv.ipv6 = conf->radius_server_ipv6;
 	srv.get_eap_user = hostapd_radius_get_eap_user;
 	srv.eap_req_id_text = conf->eap_req_id_text;
 	srv.eap_req_id_text_len = conf->eap_req_id_text_len;
-	srv.pwd_group = conf->pwd_group;
-	srv.server_id = conf->server_id ? conf->server_id : "hostapd";
 	srv.sqlite_file = conf->eap_user_sqlite;
 #ifdef CONFIG_RADIUS_TEST
 	srv.dump_msk_file = conf->dump_msk_file;
@@ -139,10 +124,8 @@
 	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 = conf->eap_server_erp;
 	srv.erp_domain = conf->erp_domain;
-	srv.tls_session_lifetime = conf->tls_session_lifetime;
-	srv.tls_flags = conf->tls_flags;
+	srv.eap_cfg = hapd->eap_cfg;
 
 	hapd->radius_srv = radius_server_init(&srv);
 	if (hapd->radius_srv == NULL) {
@@ -190,12 +173,67 @@
 #endif /* EAP_TLS_FUNCS */
 
 
+static struct eap_config * authsrv_eap_config(struct hostapd_data *hapd)
+{
+	struct eap_config *cfg;
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (!cfg)
+		return NULL;
+
+	cfg->eap_server = hapd->conf->eap_server;
+	cfg->ssl_ctx = hapd->ssl_ctx;
+	cfg->msg_ctx = hapd->msg_ctx;
+	cfg->eap_sim_db_priv = hapd->eap_sim_db_priv;
+	cfg->tls_session_lifetime = hapd->conf->tls_session_lifetime;
+	cfg->tls_flags = hapd->conf->tls_flags;
+	cfg->max_auth_rounds = hapd->conf->max_auth_rounds;
+	cfg->max_auth_rounds_short = hapd->conf->max_auth_rounds_short;
+	if (hapd->conf->pac_opaque_encr_key)
+		cfg->pac_opaque_encr_key =
+			os_memdup(hapd->conf->pac_opaque_encr_key, 16);
+	if (hapd->conf->eap_fast_a_id) {
+		cfg->eap_fast_a_id = os_memdup(hapd->conf->eap_fast_a_id,
+					       hapd->conf->eap_fast_a_id_len);
+		cfg->eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
+	}
+	if (hapd->conf->eap_fast_a_id_info)
+		cfg->eap_fast_a_id_info =
+			os_strdup(hapd->conf->eap_fast_a_id_info);
+	cfg->eap_fast_prov = hapd->conf->eap_fast_prov;
+	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_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
+	cfg->eap_sim_id = hapd->conf->eap_sim_id;
+	cfg->tnc = hapd->conf->tnc;
+	cfg->wps = hapd->wps;
+	cfg->fragment_size = hapd->conf->fragment_size;
+	cfg->pwd_group = hapd->conf->pwd_group;
+	cfg->pbc_in_m1 = hapd->conf->pbc_in_m1;
+	if (hapd->conf->server_id) {
+		cfg->server_id = (u8 *) os_strdup(hapd->conf->server_id);
+		cfg->server_id_len = os_strlen(hapd->conf->server_id);
+	} else {
+		cfg->server_id = (u8 *) os_strdup("hostapd");
+		cfg->server_id_len = 7;
+	}
+	cfg->erp = hapd->conf->eap_server_erp;
+
+	return cfg;
+}
+
+
 int authsrv_init(struct hostapd_data *hapd)
 {
 #ifdef EAP_TLS_FUNCS
 	if (hapd->conf->eap_server &&
 	    (hapd->conf->ca_cert || hapd->conf->server_cert ||
-	     hapd->conf->private_key || hapd->conf->dh_file)) {
+	     hapd->conf->private_key || hapd->conf->dh_file ||
+	     hapd->conf->server_cert2 || hapd->conf->private_key2)) {
 		struct tls_config conf;
 		struct tls_connection_params params;
 
@@ -224,8 +262,11 @@
 		os_memset(&params, 0, sizeof(params));
 		params.ca_cert = hapd->conf->ca_cert;
 		params.client_cert = hapd->conf->server_cert;
+		params.client_cert2 = hapd->conf->server_cert2;
 		params.private_key = hapd->conf->private_key;
+		params.private_key2 = hapd->conf->private_key2;
 		params.private_key_passwd = hapd->conf->private_key_passwd;
+		params.private_key_passwd2 = hapd->conf->private_key_passwd2;
 		params.dh_file = hapd->conf->dh_file;
 		params.openssl_ciphers = hapd->conf->openssl_ciphers;
 		params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
@@ -266,6 +307,14 @@
 	}
 #endif /* EAP_SIM_DB */
 
+	hapd->eap_cfg = authsrv_eap_config(hapd);
+	if (!hapd->eap_cfg) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to build EAP server configuration");
+		authsrv_deinit(hapd);
+		return -1;
+	}
+
 #ifdef RADIUS_SERVER
 	if (hapd->conf->radius_server_clients &&
 	    hostapd_setup_radius_srv(hapd))
@@ -296,4 +345,7 @@
 		hapd->eap_sim_db_priv = NULL;
 	}
 #endif /* EAP_SIM_DB */
+
+	eap_server_config_free(hapd->eap_cfg);
+	hapd->eap_cfg = NULL;
 }
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 3e62991..331c09b 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -347,7 +347,7 @@
 
 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
 					  hapd->iconf->secondary_channel,
-					  hapd->iconf->vht_oper_chwidth,
+					  hostapd_get_oper_chwidth(hapd->iconf),
 					  &op_class, &channel) ==
 	    NUM_HOSTAPD_MODES)
 		return eid;
@@ -398,7 +398,8 @@
 	if (hapd->iconf->ieee80211ax) {
 		buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
 			3 + sizeof(struct ieee80211_he_operation) +
-			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
+			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+			3 + sizeof(struct ieee80211_spatial_reuse);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -455,7 +456,8 @@
 	pos = hostapd_eid_ext_supp_rates(hapd, pos);
 
 	/* RSN, MDIE */
-	if (hapd->conf->wpa != WPA_PROTO_WPA)
+	if (!(hapd->conf->wpa == WPA_PROTO_WPA ||
+	      (hapd->conf->osen && !hapd->conf->wpa)))
 		pos = hostapd_eid_wpa(hapd, pos, epos - pos);
 
 	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
@@ -497,7 +499,8 @@
 #endif /* CONFIG_FST */
 
 #ifdef CONFIG_IEEE80211AC
-	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+	    !is_6ghz_op_class(hapd->iconf->op_class)) {
 		pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
 		pos = hostapd_eid_vht_operation(hapd, pos);
 		pos = hostapd_eid_txpower_envelope(hapd, pos);
@@ -509,9 +512,10 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		pos = hostapd_eid_he_capab(hapd, pos);
+		pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
 		pos = hostapd_eid_he_operation(hapd, pos);
 		pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
+		pos = hostapd_eid_spatial_reuse(hapd, pos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -521,7 +525,8 @@
 #endif /* CONFIG_IEEE80211AC */
 
 	/* WPA */
-	if (hapd->conf->wpa == WPA_PROTO_WPA)
+	if (hapd->conf->wpa == WPA_PROTO_WPA ||
+	    (hapd->conf->osen && !hapd->conf->wpa))
 		pos = hostapd_eid_wpa(hapd, pos, epos - pos);
 
 	/* Wi-Fi Alliance WMM */
@@ -551,7 +556,6 @@
 
 #ifdef CONFIG_HS20
 	pos = hostapd_eid_hs20_indication(hapd, pos);
-	pos = hostapd_eid_osen(hapd, pos);
 #endif /* CONFIG_HS20 */
 
 	pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
@@ -593,7 +597,7 @@
 
 	pos = ssid_list;
 	end = ssid_list + ssid_list_len;
-	while (end - pos >= 1) {
+	while (end - pos >= 2) {
 		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[1] == 0)
@@ -1088,7 +1092,8 @@
 	if (hapd->iconf->ieee80211ax) {
 		tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
 			3 + sizeof(struct ieee80211_he_operation) +
-			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
+			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+			3 + sizeof(struct ieee80211_spatial_reuse);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1161,7 +1166,8 @@
 	tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
 
 	/* RSN, MDIE */
-	if (hapd->conf->wpa != WPA_PROTO_WPA)
+	if (!(hapd->conf->wpa == WPA_PROTO_WPA ||
+	      (hapd->conf->osen && !hapd->conf->wpa)))
 		tailpos = hostapd_eid_wpa(hapd, tailpos,
 					  tail + BEACON_TAIL_BUF_SIZE -
 					  tailpos);
@@ -1223,9 +1229,11 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax) {
-		tailpos = hostapd_eid_he_capab(hapd, tailpos);
+		tailpos = hostapd_eid_he_capab(hapd, tailpos,
+					       IEEE80211_MODE_AP);
 		tailpos = hostapd_eid_he_operation(hapd, tailpos);
 		tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
+		tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1235,7 +1243,8 @@
 #endif /* CONFIG_IEEE80211AC */
 
 	/* WPA */
-	if (hapd->conf->wpa == WPA_PROTO_WPA)
+	if (hapd->conf->wpa == WPA_PROTO_WPA ||
+	    (hapd->conf->osen && !hapd->conf->wpa))
 		tailpos = hostapd_eid_wpa(hapd, tailpos,
 					  tail + BEACON_TAIL_BUF_SIZE -
 					  tailpos);
@@ -1266,7 +1275,6 @@
 
 #ifdef CONFIG_HS20
 	tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
-	tailpos = hostapd_eid_osen(hapd, tailpos);
 #endif /* CONFIG_HS20 */
 
 	tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
@@ -1394,6 +1402,7 @@
 	struct hostapd_freq_params freq;
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_config *iconf = iface->conf;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
 	struct wpabuf *beacon, *proberesp, *assocresp;
 	int res, ret = -1;
 
@@ -1415,17 +1424,26 @@
 	params.proberesp_ies = proberesp;
 	params.assocresp_ies = assocresp;
 	params.reenable = hapd->reenable_beacon;
+#ifdef CONFIG_IEEE80211AX
+	params.he_spr = !!hapd->iface->conf->spr.sr_control;
+	params.he_spr_srg_obss_pd_min_offset =
+		hapd->iface->conf->spr.srg_obss_pd_min_offset;
+	params.he_spr_srg_obss_pd_max_offset =
+		hapd->iface->conf->spr.srg_obss_pd_max_offset;
+#endif /* CONFIG_IEEE80211AX */
 	hapd->reenable_beacon = 0;
 
-	if (iface->current_mode &&
+	if (cmode &&
 	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
-				    iconf->channel, iconf->ieee80211n,
-				    iconf->ieee80211ac,
+				    iconf->channel, iconf->enable_edmg,
+				    iconf->edmg_channel, iconf->ieee80211n,
+				    iconf->ieee80211ac, iconf->ieee80211ax,
 				    iconf->secondary_channel,
-				    iconf->vht_oper_chwidth,
-				    iconf->vht_oper_centr_freq_seg0_idx,
-				    iconf->vht_oper_centr_freq_seg1_idx,
-				    iface->current_mode->vht_capab) == 0)
+				    hostapd_get_oper_chwidth(iconf),
+				    hostapd_get_oper_centr_freq_seg0_idx(iconf),
+				    hostapd_get_oper_centr_freq_seg1_idx(iconf),
+				    cmode->vht_capab,
+				    &cmode->he_capab[IEEE80211_MODE_AP]) == 0)
 		params.freq = &freq;
 
 	res = hostapd_drv_set_ap(hapd, &params);
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index c693715..9fd1b81 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -273,6 +273,36 @@
 		if (!os_snprintf_error(buflen - len, res))
 			len += res;
 	}
+
+	if (sta->sae && sta->sae->tmp) {
+		const u8 *pos;
+		unsigned int j, count;
+		struct wpabuf *groups = sta->sae->tmp->peer_rejected_groups;
+
+		res = os_snprintf(buf + len, buflen - len,
+				  "sae_rejected_groups=");
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+
+		if (groups) {
+			pos = wpabuf_head(groups);
+			count = wpabuf_len(groups) / 2;
+		} else {
+			pos = NULL;
+			count = 0;
+		}
+		for (j = 0; pos && j < count; j++) {
+			res = os_snprintf(buf + len, buflen - len, "%s%d",
+					  j == 0 ? "" : " ", WPA_GET_LE16(pos));
+			if (!os_snprintf_error(buflen - len, res))
+				len += res;
+			pos += 2;
+		}
+
+		res = os_snprintf(buf + len, buflen - len, "\n");
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
 #endif /* CONFIG_SAE */
 
 	if (sta->vlan_id > 0) {
@@ -709,22 +739,44 @@
 
 	ret = os_snprintf(buf + len, buflen - len,
 			  "channel=%u\n"
+			  "edmg_enable=%d\n"
+			  "edmg_channel=%d\n"
 			  "secondary_channel=%d\n"
 			  "ieee80211n=%d\n"
 			  "ieee80211ac=%d\n"
+			  "ieee80211ax=%d\n"
 			  "beacon_int=%u\n"
 			  "dtim_period=%d\n",
 			  iface->conf->channel,
+			  iface->conf->enable_edmg,
+			  iface->conf->edmg_channel,
 			  iface->conf->ieee80211n && !hapd->conf->disable_11n ?
 			  iface->conf->secondary_channel : 0,
 			  iface->conf->ieee80211n && !hapd->conf->disable_11n,
 			  iface->conf->ieee80211ac &&
 			  !hapd->conf->disable_11ac,
+			  iface->conf->ieee80211ax,
 			  iface->conf->beacon_int,
 			  hapd->conf->dtim_period);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
+
+#ifdef CONFIG_IEEE80211AX
+	if (iface->conf->ieee80211ax) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "he_oper_chwidth=%d\n"
+				  "he_oper_centr_freq_seg0_idx=%d\n"
+				  "he_oper_centr_freq_seg1_idx=%d\n",
+				  iface->conf->he_oper_chwidth,
+				  iface->conf->he_oper_centr_freq_seg0_idx,
+				  iface->conf->he_oper_centr_freq_seg1_idx);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
 		ret = os_snprintf(buf + len, buflen - len,
 				  "vht_oper_chwidth=%d\n"
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 79cd00f..f70ecc9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -28,17 +28,17 @@
 	if (iface->conf->ieee80211n && iface->conf->secondary_channel)
 		n_chans = 2;
 
-	if (iface->conf->ieee80211ac) {
-		switch (iface->conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_USE_HT:
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CHANWIDTH_USE_HT:
 			break;
-		case VHT_CHANWIDTH_80MHZ:
+		case CHANWIDTH_80MHZ:
 			n_chans = 4;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
+		case CHANWIDTH_160MHZ:
 			n_chans = 8;
 			break;
-		case VHT_CHANWIDTH_80P80MHZ:
+		case CHANWIDTH_80P80MHZ:
 			n_chans = 4;
 			*seg1 = 4;
 			break;
@@ -188,8 +188,8 @@
  * The function assumes HT40+ operation.
  * Make sure to adjust the following variables after calling this:
  *  - hapd->secondary_channel
- *  - hapd->vht_oper_centr_freq_seg0_idx
- *  - hapd->vht_oper_centr_freq_seg1_idx
+ *  - hapd->vht/he_oper_centr_freq_seg0_idx
+ *  - hapd->vht/he_oper_centr_freq_seg1_idx
  */
 static int dfs_find_channel(struct hostapd_iface *iface,
 			    struct hostapd_channel_data **ret_chan,
@@ -232,44 +232,44 @@
 }
 
 
-static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
-				       struct hostapd_channel_data *chan,
-				       int secondary_channel,
-				       u8 *vht_oper_centr_freq_seg0_idx,
-				       u8 *vht_oper_centr_freq_seg1_idx)
+static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+				   struct hostapd_channel_data *chan,
+				   int secondary_channel,
+				   u8 *oper_centr_freq_seg0_idx,
+				   u8 *oper_centr_freq_seg1_idx)
 {
-	if (!iface->conf->ieee80211ac)
+	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
 		return;
 
 	if (!chan)
 		return;
 
-	*vht_oper_centr_freq_seg1_idx = 0;
+	*oper_centr_freq_seg1_idx = 0;
 
-	switch (iface->conf->vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
+	switch (hostapd_get_oper_chwidth(iface->conf)) {
+	case CHANWIDTH_USE_HT:
 		if (secondary_channel == 1)
-			*vht_oper_centr_freq_seg0_idx = chan->chan + 2;
+			*oper_centr_freq_seg0_idx = chan->chan + 2;
 		else if (secondary_channel == -1)
-			*vht_oper_centr_freq_seg0_idx = chan->chan - 2;
+			*oper_centr_freq_seg0_idx = chan->chan - 2;
 		else
-			*vht_oper_centr_freq_seg0_idx = chan->chan;
+			*oper_centr_freq_seg0_idx = chan->chan;
 		break;
-	case VHT_CHANWIDTH_80MHZ:
-		*vht_oper_centr_freq_seg0_idx = chan->chan + 6;
+	case CHANWIDTH_80MHZ:
+		*oper_centr_freq_seg0_idx = chan->chan + 6;
 		break;
-	case VHT_CHANWIDTH_160MHZ:
-		*vht_oper_centr_freq_seg0_idx = chan->chan + 14;
+	case CHANWIDTH_160MHZ:
+		*oper_centr_freq_seg0_idx = chan->chan + 14;
 		break;
 	default:
 		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
-		*vht_oper_centr_freq_seg0_idx = 0;
+		*oper_centr_freq_seg0_idx = 0;
 		break;
 	}
 
 	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
-		   *vht_oper_centr_freq_seg0_idx,
-		   *vht_oper_centr_freq_seg1_idx);
+		   *oper_centr_freq_seg0_idx,
+		   *oper_centr_freq_seg1_idx);
 }
 
 
@@ -288,24 +288,24 @@
 	if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
 		channel_no -= 4;
 
-	/* VHT */
-	if (iface->conf->ieee80211ac) {
-		switch (iface->conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_USE_HT:
+	/* VHT/HE */
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CHANWIDTH_USE_HT:
 			break;
-		case VHT_CHANWIDTH_80MHZ:
-			channel_no =
-				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+		case CHANWIDTH_80MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 6;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
-			channel_no =
-				iface->conf->vht_oper_centr_freq_seg0_idx - 14;
+		case CHANWIDTH_160MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 14;
 			break;
-		case VHT_CHANWIDTH_80P80MHZ:
-			channel_no =
-				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
-			chan_seg1 =
-				iface->conf->vht_oper_centr_freq_seg1_idx - 6;
+		case CHANWIDTH_80P80MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 6;
+			chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
+				iface->conf) - 6;
 			break;
 		default:
 			wpa_printf(MSG_INFO,
@@ -348,7 +348,7 @@
 			   mode->num_channels, channel_no, iface->conf->channel,
 			   iface->conf->ieee80211n,
 			   iface->conf->secondary_channel,
-			   iface->conf->vht_oper_chwidth);
+			   hostapd_get_oper_chwidth(iface->conf));
 
 		for (i = 0; i < mode->num_channels; i++) {
 			wpa_printf(MSG_DEBUG, "Available channel: %d",
@@ -435,8 +435,8 @@
 static struct hostapd_channel_data *
 dfs_get_valid_channel(struct hostapd_iface *iface,
 		      int *secondary_channel,
-		      u8 *vht_oper_centr_freq_seg0_idx,
-		      u8 *vht_oper_centr_freq_seg1_idx,
+		      u8 *oper_centr_freq_seg0_idx,
+		      u8 *oper_centr_freq_seg1_idx,
 		      int skip_radar)
 {
 	struct hostapd_hw_modes *mode;
@@ -447,8 +447,8 @@
 
 	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
 	*secondary_channel = 0;
-	*vht_oper_centr_freq_seg0_idx = 0;
-	*vht_oper_centr_freq_seg1_idx = 0;
+	*oper_centr_freq_seg0_idx = 0;
+	*oper_centr_freq_seg1_idx = 0;
 
 	if (iface->current_mode == NULL)
 		return NULL;
@@ -473,10 +473,10 @@
 	else
 		*secondary_channel = 0;
 
-	dfs_adjust_vht_center_freq(iface, chan,
-				   *secondary_channel,
-				   vht_oper_centr_freq_seg0_idx,
-				   vht_oper_centr_freq_seg1_idx);
+	dfs_adjust_center_freq(iface, chan,
+			       *secondary_channel,
+			       oper_centr_freq_seg0_idx,
+			       oper_centr_freq_seg1_idx);
 
 	return chan;
 }
@@ -515,6 +515,7 @@
 	int n_chans = 1, i;
 	struct hostapd_hw_modes *mode;
 	int frequency = freq;
+	int frequency2 = 0;
 	int ret = 0;
 
 	mode = iface->current_mode;
@@ -542,6 +543,11 @@
 		n_chans = 4;
 		frequency = cf1 - 30;
 		break;
+	case CHAN_WIDTH_80P80:
+		n_chans = 4;
+		frequency = cf1 - 30;
+		frequency2 = cf2 - 30;
+		break;
 	case CHAN_WIDTH_160:
 		n_chans = 8;
 		frequency = cf1 - 70;
@@ -557,6 +563,11 @@
 	for (i = 0; i < n_chans; i++) {
 		ret += set_dfs_state_freq(iface, frequency, state);
 		frequency = frequency + 20;
+
+		if (chan_width == CHAN_WIDTH_80P80) {
+			ret += set_dfs_state_freq(iface, frequency2, state);
+			frequency2 = frequency2 + 20;
+		}
 	}
 
 	return ret;
@@ -662,6 +673,9 @@
 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
 	int skip_radar = 0;
 
+	if (is_6ghz_freq(iface->freq))
+		return 1;
+
 	if (!iface->current_mode) {
 		/*
 		 * This can happen with drivers that do not provide mode
@@ -724,8 +738,8 @@
 			iface->freq = channel->freq;
 			iface->conf->channel = channel->chan;
 			iface->conf->secondary_channel = sec;
-			iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
-			iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
+			hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+			hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
 		}
 	} while (res);
 
@@ -736,20 +750,19 @@
 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
 		iface->freq,
 		iface->conf->channel, iface->conf->secondary_channel,
-		iface->conf->vht_oper_chwidth,
-		iface->conf->vht_oper_centr_freq_seg0_idx,
-		iface->conf->vht_oper_centr_freq_seg1_idx,
+		hostapd_get_oper_chwidth(iface->conf),
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
 		iface->dfs_cac_ms / 1000);
 
-	res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-				    iface->freq,
-				    iface->conf->channel,
-				    iface->conf->ieee80211n,
-				    iface->conf->ieee80211ac,
-				    iface->conf->secondary_channel,
-				    iface->conf->vht_oper_chwidth,
-				    iface->conf->vht_oper_centr_freq_seg0_idx,
-				    iface->conf->vht_oper_centr_freq_seg1_idx);
+	res = hostapd_start_dfs_cac(
+		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+		iface->conf->ieee80211n, iface->conf->ieee80211ac,
+		iface->conf->ieee80211ax,
+		iface->conf->secondary_channel,
+		hostapd_get_oper_chwidth(iface->conf),
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
 
 	if (res) {
 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
@@ -842,16 +855,16 @@
 {
 	struct hostapd_channel_data *channel;
 	int secondary_channel;
-	u8 vht_oper_centr_freq_seg0_idx = 0;
-	u8 vht_oper_centr_freq_seg1_idx = 0;
+	u8 oper_centr_freq_seg0_idx = 0;
+	u8 oper_centr_freq_seg1_idx = 0;
 	int skip_radar = 0;
 	int err = 1;
 
 	/* Radar detected during active CAC */
 	iface->cac_started = 0;
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
-					&vht_oper_centr_freq_seg0_idx,
-					&vht_oper_centr_freq_seg1_idx,
+					&oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
 					skip_radar);
 
 	if (!channel) {
@@ -868,10 +881,10 @@
 	iface->freq = channel->freq;
 	iface->conf->channel = channel->chan;
 	iface->conf->secondary_channel = secondary_channel;
-	iface->conf->vht_oper_centr_freq_seg0_idx =
-		vht_oper_centr_freq_seg0_idx;
-	iface->conf->vht_oper_centr_freq_seg1_idx =
-		vht_oper_centr_freq_seg1_idx;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+					     oper_centr_freq_seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+					     oper_centr_freq_seg1_idx);
 	err = 0;
 
 	hostapd_setup_interface_complete(iface, err);
@@ -883,12 +896,13 @@
 {
 	struct hostapd_channel_data *channel;
 	int secondary_channel;
-	u8 vht_oper_centr_freq_seg0_idx;
-	u8 vht_oper_centr_freq_seg1_idx;
+	u8 oper_centr_freq_seg0_idx;
+	u8 oper_centr_freq_seg1_idx;
 	int skip_radar = 1;
 	struct csa_settings csa_settings;
 	unsigned int i;
 	int err = 1;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
 
 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
 		   __func__, iface->cac_started ? "yes" : "no",
@@ -911,8 +925,8 @@
 
 	/* Perform channel switch/CSA */
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
-					&vht_oper_centr_freq_seg0_idx,
-					&vht_oper_centr_freq_seg1_idx,
+					&oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
 					skip_radar);
 
 	if (!channel) {
@@ -923,8 +937,8 @@
 		 */
 		skip_radar = 0;
 		channel = dfs_get_valid_channel(iface, &secondary_channel,
-						&vht_oper_centr_freq_seg0_idx,
-						&vht_oper_centr_freq_seg1_idx,
+						&oper_centr_freq_seg0_idx,
+						&oper_centr_freq_seg1_idx,
 						skip_radar);
 		if (!channel) {
 			wpa_printf(MSG_INFO,
@@ -936,10 +950,10 @@
 		iface->freq = channel->freq;
 		iface->conf->channel = channel->chan;
 		iface->conf->secondary_channel = secondary_channel;
-		iface->conf->vht_oper_centr_freq_seg0_idx =
-			vht_oper_centr_freq_seg0_idx;
-		iface->conf->vht_oper_centr_freq_seg1_idx =
-			vht_oper_centr_freq_seg1_idx;
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+						     oper_centr_freq_seg0_idx);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+						     oper_centr_freq_seg1_idx);
 
 		hostapd_disable_iface(iface);
 		hostapd_enable_iface(iface);
@@ -960,13 +974,17 @@
 				      iface->conf->hw_mode,
 				      channel->freq,
 				      channel->chan,
+				      iface->conf->enable_edmg,
+				      iface->conf->edmg_channel,
 				      iface->conf->ieee80211n,
 				      iface->conf->ieee80211ac,
+				      iface->conf->ieee80211ax,
 				      secondary_channel,
-				      iface->conf->vht_oper_chwidth,
-				      vht_oper_centr_freq_seg0_idx,
-				      vht_oper_centr_freq_seg1_idx,
-				      iface->current_mode->vht_capab);
+				      hostapd_get_oper_chwidth(iface->conf),
+				      oper_centr_freq_seg0_idx,
+				      oper_centr_freq_seg1_idx,
+				      cmode->vht_capab,
+				      &cmode->he_capab[IEEE80211_MODE_AP]);
 
 	if (err) {
 		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
@@ -986,10 +1004,10 @@
 		iface->freq = channel->freq;
 		iface->conf->channel = channel->chan;
 		iface->conf->secondary_channel = secondary_channel;
-		iface->conf->vht_oper_centr_freq_seg0_idx =
-			vht_oper_centr_freq_seg0_idx;
-		iface->conf->vht_oper_centr_freq_seg1_idx =
-			vht_oper_centr_freq_seg1_idx;
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+						     oper_centr_freq_seg0_idx);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+						     oper_centr_freq_seg1_idx);
 
 		hostapd_disable_iface(iface);
 		hostapd_enable_iface(iface);
@@ -1091,11 +1109,18 @@
 			  int ht_enabled, int chan_offset, int chan_width,
 			  int cf1, int cf2)
 {
+	/* This is called when the driver indicates that an offloaded DFS has
+	 * started CAC. */
+	hostapd_set_state(iface, HAPD_IFACE_DFS);
+	/* TODO: How to check CAC time for ETSI weather channels? */
+	iface->dfs_cac_ms = 60000;
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
 		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
 		"seg1=%d cac_time=%ds",
-		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60);
+		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
+		iface->dfs_cac_ms / 1000);
 	iface->cac_started = 1;
+	os_get_reltime(&iface->dfs_cac_start);
 	return 0;
 }
 
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 75edbc9..1a3a815 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1,6 +1,7 @@
 /*
  * hostapd / DPP integration
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,6 +17,7 @@
 #include "hostapd.h"
 #include "ap_drv_ops.h"
 #include "gas_query_ap.h"
+#include "gas_serv.h"
 #include "wpa_auth.h"
 #include "dpp_hostapd.h"
 
@@ -61,6 +63,24 @@
 }
 
 
+/**
+ * hostapd_dpp_nfc_uri - Parse and add DPP bootstrapping info from NFC Tag (URI)
+ * @hapd: Pointer to hostapd_data
+ * @cmd: DPP URI read from a NFC Tag (URI NDEF message)
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, cmd);
+	if (!bi)
+		return -1;
+
+	return bi->id;
+}
+
+
 static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
 						void *timeout_ctx)
 {
@@ -557,6 +577,14 @@
 	 * received hash values */
 	dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
 				r_bootstrap, &own_bi, &peer_bi);
+#ifdef CONFIG_DPP2
+	if (!own_bi) {
+		if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+					src, hdr, buf, len, freq, i_bootstrap,
+					r_bootstrap) == 0)
+			return;
+	}
+#endif /* CONFIG_DPP2 */
 	if (!own_bi) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"No matching own bootstrapping key found - ignore message");
@@ -598,47 +626,48 @@
 
 
 static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
-					  struct dpp_authentication *auth)
+					  struct dpp_authentication *auth,
+					  struct dpp_config_obj *conf)
 {
 	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
 	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
-		dpp_akm_str(auth->akm));
-	if (auth->ssid_len)
+		dpp_akm_str(conf->akm));
+	if (conf->ssid_len)
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
-			wpa_ssid_txt(auth->ssid, auth->ssid_len));
-	if (auth->connector) {
+			wpa_ssid_txt(conf->ssid, conf->ssid_len));
+	if (conf->connector) {
 		/* TODO: Save the Connector and consider using a command
 		 * to fetch the value instead of sending an event with
 		 * it. The Connector could end up being larger than what
 		 * most clients are ready to receive as an event
 		 * message. */
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
-			auth->connector);
-	} else if (auth->passphrase[0]) {
+			conf->connector);
+	} else if (conf->passphrase[0]) {
 		char hex[64 * 2 + 1];
 
 		wpa_snprintf_hex(hex, sizeof(hex),
-				 (const u8 *) auth->passphrase,
-				 os_strlen(auth->passphrase));
+				 (const u8 *) conf->passphrase,
+				 os_strlen(conf->passphrase));
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
 			hex);
-	} else if (auth->psk_set) {
+	} else if (conf->psk_set) {
 		char hex[PMK_LEN * 2 + 1];
 
-		wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN);
+		wpa_snprintf_hex(hex, sizeof(hex), conf->psk, PMK_LEN);
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
 			hex);
 	}
-	if (auth->c_sign_key) {
+	if (conf->c_sign_key) {
 		char *hex;
 		size_t hexlen;
 
-		hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+		hexlen = 2 * wpabuf_len(conf->c_sign_key) + 1;
 		hex = os_malloc(hexlen);
 		if (hex) {
 			wpa_snprintf_hex(hex, hexlen,
-					 wpabuf_head(auth->c_sign_key),
-					 wpabuf_len(auth->c_sign_key));
+					 wpabuf_head(conf->c_sign_key),
+					 wpabuf_len(conf->c_sign_key));
 			wpa_msg(hapd->msg_ctx, MSG_INFO,
 				DPP_EVENT_C_SIGN_KEY "%s", hex);
 			os_free(hex);
@@ -711,7 +740,7 @@
 		goto fail;
 	}
 
-	hostapd_dpp_handle_config_obj(hapd, auth);
+	hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
 	status = DPP_STATUS_OK;
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_REJECT_CONFIG) {
@@ -756,18 +785,11 @@
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
 	struct wpabuf *buf;
-	char json[100];
 	int res;
-	int netrole_ap = 1;
 
-	os_snprintf(json, sizeof(json),
-		    "{\"name\":\"Test\","
-		    "\"wi-fi_tech\":\"infra\","
-		    "\"netRole\":\"%s\"}",
-		    netrole_ap ? "ap" : "sta");
-	wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
-
-	buf = dpp_build_conf_req(auth, json);
+	buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
+					DPP_NETROLE_AP,
+					hapd->conf->dpp_mud_url, NULL);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
@@ -913,6 +935,24 @@
 }
 
 
+static void hostapd_dpp_conn_status_result_wait_timeout(void *eloop_ctx,
+							void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->waiting_conf_result)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Timeout while waiting for Connection Status Result");
+	wpa_msg(hapd->msg_ctx, MSG_INFO,
+		DPP_EVENT_CONN_STATUS_RESULT "timeout");
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
 static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
 				       const u8 *hdr, const u8 *buf, size_t len)
 {
@@ -936,6 +976,20 @@
 
 	status = dpp_conf_result_rx(auth, hdr, buf, len);
 
+	if (status == DPP_STATUS_OK && auth->send_conn_status) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+		eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+				     hapd, NULL);
+		eloop_cancel_timeout(
+			hostapd_dpp_conn_status_result_wait_timeout,
+			hapd, NULL);
+		eloop_register_timeout(
+			16, 0, hostapd_dpp_conn_status_result_wait_timeout,
+			hapd, NULL);
+		return;
+	}
 	hostapd_drv_send_action_cancel_wait(hapd);
 	hostapd_dpp_listen_stop(hapd);
 	if (status == DPP_STATUS_OK)
@@ -948,6 +1002,41 @@
 			     NULL);
 }
 
+
+static void hostapd_dpp_rx_conn_status_result(struct hostapd_data *hapd,
+					      const u8 *src, const u8 *hdr,
+					      const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	enum dpp_status_error status;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len = 0;
+	char *channel_list = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+	if (!auth || !auth->waiting_conn_status_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for connection status result - drop");
+		return;
+	}
+
+	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+					   ssid, &ssid_len, &channel_list);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
+		"result=%d ssid=%s channel_list=%s",
+		status, wpa_ssid_txt(ssid, ssid_len),
+		channel_list ? channel_list : "N/A");
+	os_free(channel_list);
+	hostapd_drv_send_action_cancel_wait(hapd);
+	hostapd_dpp_listen_stop(hapd);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+	eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout,
+			     hapd, NULL);
+}
+
+
 #endif /* CONFIG_DPP2 */
 
 
@@ -1357,6 +1446,12 @@
 	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
 		" freq=%u type=%d", MAC2STR(src), freq, type);
 
+#ifdef CONFIG_DPP2
+	if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+				src, hdr, buf, len, freq, NULL, NULL) == 0)
+		return;
+#endif /* CONFIG_DPP2 */
+
 	switch (type) {
 	case DPP_PA_AUTHENTICATION_REQ:
 		hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
@@ -1388,6 +1483,9 @@
 	case DPP_PA_CONFIGURATION_RESULT:
 		hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
 		break;
+	case DPP_PA_CONNECTION_STATUS_RESULT:
+		hostapd_dpp_rx_conn_status_result(hapd, src, hdr, buf, len);
+		break;
 #endif /* CONFIG_DPP2 */
 	default:
 		wpa_printf(MSG_DEBUG,
@@ -1410,7 +1508,8 @@
 
 struct wpabuf *
 hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
-			    const u8 *query, size_t query_len)
+			    const u8 *query, size_t query_len,
+			    const u8 *data, size_t data_len)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
 	struct wpabuf *resp;
@@ -1418,6 +1517,13 @@
 	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
 	if (!auth || !auth->auth_success ||
 	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+#ifdef CONFIG_DPP2
+		if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
+				     data_len) == 0) {
+			/* Response will be forwarded once received over TCP */
+			return NULL;
+		}
+#endif /* CONFIG_DPP2 */
 		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
 		return NULL;
 	}
@@ -1483,7 +1589,7 @@
 	if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
 				 auth, cmd) == 0 &&
 	    dpp_configurator_own_config(auth, curve, 1) == 0) {
-		hostapd_dpp_handle_config_obj(hapd, auth);
+		hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
 		ret = 0;
 	}
 
@@ -1609,11 +1715,67 @@
 }
 
 
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_relay_tx(void *ctx, const u8 *addr, unsigned int freq,
+				 const u8 *msg, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	u8 *buf;
+
+	wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
+		   MAC2STR(addr), freq);
+	buf = os_malloc(2 + len);
+	if (!buf)
+		return;
+	buf[0] = WLAN_ACTION_PUBLIC;
+	buf[1] = WLAN_PA_VENDOR_SPECIFIC;
+	os_memcpy(buf + 2, msg, len);
+	hostapd_drv_send_action(hapd, freq, 0, addr, buf, 2 + len);
+	os_free(buf);
+}
+
+
+static void hostapd_dpp_relay_gas_resp_tx(void *ctx, const u8 *addr,
+					  u8 dialog_token, int prot,
+					  struct wpabuf *buf)
+{
+	struct hostapd_data *hapd = ctx;
+
+	gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static int hostapd_dpp_add_controllers(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_DPP2
+	struct dpp_controller_conf *ctrl;
+	struct dpp_relay_config config;
+
+	os_memset(&config, 0, sizeof(config));
+	config.cb_ctx = hapd;
+	config.tx = hostapd_dpp_relay_tx;
+	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+	for (ctrl = hapd->conf->dpp_controller; ctrl; ctrl = ctrl->next) {
+		config.ipaddr = &ctrl->ipaddr;
+		config.pkhash = ctrl->pkhash;
+		if (dpp_relay_add_controller(hapd->iface->interfaces->dpp,
+					     &config) < 0)
+			return -1;
+	}
+#endif /* CONFIG_DPP2 */
+
+	return 0;
+}
+
+
 int hostapd_dpp_init(struct hostapd_data *hapd)
 {
 	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
 	hapd->dpp_init_done = 1;
-	return 0;
+	return hostapd_dpp_add_controllers(hapd);
 }
 
 
@@ -1636,6 +1798,8 @@
 #ifdef CONFIG_DPP2
 	eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
 			     NULL);
+	eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
+			     NULL);
 #endif /* CONFIG_DPP2 */
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 449ca16..e151c2f 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -1,6 +1,7 @@
 /*
  * hostapd / DPP integration
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +11,7 @@
 #define DPP_HOSTAPD_H
 
 int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
 void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
@@ -19,7 +21,8 @@
 			   const u8 *data, size_t data_len, int ok);
 struct wpabuf *
 hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
-			    const u8 *query, size_t query_len);
+			    const u8 *query, size_t query_len,
+			    const u8 *data, size_t data_len);
 void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
 int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index e3844d9..8a4b0ef 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -16,6 +16,8 @@
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "common/dpp.h"
+#include "common/sae.h"
+#include "common/hw_features_common.h"
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
@@ -111,10 +113,8 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
-#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
 	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 	u8 *p = buf;
-#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS || CONFIG_OWE */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
 	u16 status = WLAN_STATUS_SUCCESS;
 	const u8 *p2p_dev_addr = NULL;
@@ -321,6 +321,8 @@
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  hapd->iface->freq,
 					  ie, ielen,
+					  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);
 		if (res != WPA_IE_OK) {
@@ -337,23 +339,22 @@
 			} else if (res == WPA_INVALID_AKMP) {
 				reason = WLAN_REASON_AKMP_NOT_VALID;
 				status = WLAN_STATUS_AKMP_NOT_VALID;
-			}
-#ifdef CONFIG_IEEE80211W
-			else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) {
+			} else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) {
 				reason = WLAN_REASON_INVALID_IE;
 				status = WLAN_STATUS_INVALID_IE;
 			} else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
 				reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
 				status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
-			}
-#endif /* CONFIG_IEEE80211W */
-			else {
+			} else if (res == WPA_INVALID_PMKID) {
+				reason = WLAN_REASON_INVALID_PMKID;
+				status = WLAN_STATUS_INVALID_PMKID;
+			} else {
 				reason = WLAN_REASON_INVALID_IE;
 				status = WLAN_STATUS_INVALID_IE;
 			}
 			goto fail;
 		}
-#ifdef CONFIG_IEEE80211W
+
 		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
 		    (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
 		    !sta->sa_query_timed_out &&
@@ -386,7 +387,6 @@
 			sta->flags |= WLAN_STA_MFP;
 		else
 			sta->flags &= ~WLAN_STA_MFP;
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
@@ -403,6 +403,20 @@
 			}
 		}
 #endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+		if (hapd->conf->sae_pwe == 2 &&
+		    sta->auth_alg == WLAN_AUTH_SAE &&
+		    sta->sae && sta->sae->tmp && !sta->sae->tmp->h2e &&
+		    elems.rsnxe && elems.rsnxe_len >= 1 &&
+		    (elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
+			wpa_printf(MSG_INFO, "SAE: " MACSTR
+				   " indicates support for SAE H2E, but did not use it",
+				   MAC2STR(sta->addr));
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			reason = WLAN_REASON_UNSPECIFIED;
+			goto fail;
+		}
+#endif /* CONFIG_SAE */
 	} else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
 		struct wpabuf *wps;
@@ -785,7 +799,8 @@
 
 
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
-			     int offset, int width, int cf1, int cf2)
+			     int offset, int width, int cf1, int cf2,
+			     int finished)
 {
 	/* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */
 
@@ -796,7 +811,8 @@
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
-		       "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       finished ? "had" : "starting",
 		       freq, ht, hapd->iconf->ch_switch_vht_config, offset,
 		       width, channel_width_to_string(width), cf1, cf2);
 
@@ -819,19 +835,19 @@
 
 	switch (width) {
 	case CHAN_WIDTH_80:
-		chwidth = VHT_CHANWIDTH_80MHZ;
+		chwidth = CHANWIDTH_80MHZ;
 		break;
 	case CHAN_WIDTH_80P80:
-		chwidth = VHT_CHANWIDTH_80P80MHZ;
+		chwidth = CHANWIDTH_80P80MHZ;
 		break;
 	case CHAN_WIDTH_160:
-		chwidth = VHT_CHANWIDTH_160MHZ;
+		chwidth = CHANWIDTH_160MHZ;
 		break;
 	case CHAN_WIDTH_20_NOHT:
 	case CHAN_WIDTH_20:
 	case CHAN_WIDTH_40:
 	default:
-		chwidth = VHT_CHANWIDTH_USE_HT;
+		chwidth = CHANWIDTH_USE_HT;
 		break;
 	}
 
@@ -864,13 +880,22 @@
 	hapd->iconf->ch_switch_vht_config = 0;
 
 	hapd->iconf->secondary_channel = offset;
-	hapd->iconf->vht_oper_chwidth = chwidth;
-	hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
-	hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
+	hostapd_set_oper_chwidth(hapd->iconf, chwidth);
+	hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
 
 	is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
 				  hapd->iface->num_hw_features);
 
+	wpa_msg(hapd->msg_ctx, MSG_INFO,
+		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d",
+		finished ? WPA_EVENT_CHANNEL_SWITCH :
+		WPA_EVENT_CHANNEL_SWITCH_STARTED,
+		freq, ht, offset, channel_width_to_string(width),
+		cf1, cf2, is_dfs);
+	if (!finished)
+		return;
+
 	if (hapd->csa_in_progress &&
 	    freq == hapd->cs_freq_params.freq) {
 		hostapd_cleanup_cs_params(hapd);
@@ -911,6 +936,7 @@
 {
 	int ret, i;
 	int err = 0;
+	struct hostapd_channel_data *pri_chan;
 
 	if (hapd->iconf->channel) {
 		wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
@@ -918,12 +944,20 @@
 		return;
 	}
 
+	hapd->iface->freq = acs_res->pri_freq;
+
 	if (!hapd->iface->current_mode) {
 		for (i = 0; i < hapd->iface->num_hw_features; i++) {
 			struct hostapd_hw_modes *mode =
 				&hapd->iface->hw_features[i];
 
 			if (mode->mode == acs_res->hw_mode) {
+				if (hapd->iface->freq > 0 &&
+				    !hw_get_chan(mode->mode,
+						 hapd->iface->freq,
+						 hapd->iface->hw_features,
+						 hapd->iface->num_hw_features))
+					continue;
 				hapd->iface->current_mode = mode;
 				break;
 			}
@@ -937,24 +971,33 @@
 		}
 	}
 
-	hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
-
-	if (!acs_res->pri_channel) {
+	if (!acs_res->pri_freq) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_WARNING,
 			       "driver switched to bad channel");
 		err = 1;
 		goto out;
 	}
+	pri_chan = hw_get_channel_freq(hapd->iface->current_mode->mode,
+				       acs_res->pri_freq, NULL,
+				       hapd->iface->hw_features,
+				       hapd->iface->num_hw_features);
+	if (!pri_chan) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Could not determine primary channel number from pri_freq %u",
+			   acs_res->pri_freq);
+		err = 1;
+		goto out;
+	}
 
-	hapd->iconf->channel = acs_res->pri_channel;
+	hapd->iconf->channel = pri_chan->chan;
 	hapd->iconf->acs = 1;
 
-	if (acs_res->sec_channel == 0)
+	if (acs_res->sec_freq == 0)
 		hapd->iconf->secondary_channel = 0;
-	else if (acs_res->sec_channel < acs_res->pri_channel)
+	else if (acs_res->sec_freq < acs_res->pri_freq)
 		hapd->iconf->secondary_channel = -1;
-	else if (acs_res->sec_channel > acs_res->pri_channel)
+	else if (acs_res->sec_freq > acs_res->pri_freq)
 		hapd->iconf->secondary_channel = 1;
 	else {
 		wpa_printf(MSG_ERROR, "Invalid secondary channel!");
@@ -962,28 +1005,31 @@
 		goto out;
 	}
 
-	if (hapd->iface->conf->ieee80211ac) {
+	if (hapd->iface->conf->ieee80211ac || hapd->iface->conf->ieee80211ax) {
 		/* set defaults for backwards compatibility */
-		hapd->iconf->vht_oper_centr_freq_seg1_idx = 0;
-		hapd->iconf->vht_oper_centr_freq_seg0_idx = 0;
-		hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
+		hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, 0);
+		hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_USE_HT);
 		if (acs_res->ch_width == 80) {
-			hapd->iconf->vht_oper_centr_freq_seg0_idx =
-				acs_res->vht_seg0_center_ch;
-			hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			hostapd_set_oper_centr_freq_seg0_idx(
+				hapd->iconf, acs_res->vht_seg0_center_ch);
+			hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_80MHZ);
 		} else if (acs_res->ch_width == 160) {
 			if (acs_res->vht_seg1_center_ch == 0) {
-				hapd->iconf->vht_oper_centr_freq_seg0_idx =
-					acs_res->vht_seg0_center_ch;
-				hapd->iconf->vht_oper_chwidth =
-					VHT_CHANWIDTH_160MHZ;
+				hostapd_set_oper_centr_freq_seg0_idx(
+					hapd->iconf,
+					acs_res->vht_seg0_center_ch);
+				hostapd_set_oper_chwidth(hapd->iconf,
+							 CHANWIDTH_160MHZ);
 			} else {
-				hapd->iconf->vht_oper_centr_freq_seg0_idx =
-					acs_res->vht_seg0_center_ch;
-				hapd->iconf->vht_oper_centr_freq_seg1_idx =
-					acs_res->vht_seg1_center_ch;
-				hapd->iconf->vht_oper_chwidth =
-					VHT_CHANWIDTH_80P80MHZ;
+				hostapd_set_oper_centr_freq_seg0_idx(
+					hapd->iconf,
+					acs_res->vht_seg0_center_ch);
+				hostapd_set_oper_centr_freq_seg1_idx(
+					hapd->iconf,
+					acs_res->vht_seg1_center_ch);
+				hostapd_set_oper_chwidth(hapd->iconf,
+							 CHANWIDTH_80P80MHZ);
 			}
 		}
 	}
@@ -1163,12 +1209,10 @@
 		return;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
 	if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) {
 		ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
 		return;
 	}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM_AP
 	if (mgmt->u.action.category == WLAN_ACTION_WNM) {
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
@@ -1588,6 +1632,73 @@
 }
 
 
+#ifdef CONFIG_OWE
+static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd,
+				      const u8 *peer, const u8 *ie,
+				      size_t ie_len)
+{
+	u16 status;
+	struct sta_info *sta;
+	struct ieee802_11_elems elems;
+
+	if (!hapd || !hapd->wpa_auth) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid hapd context");
+		return -1;
+	}
+	if (!peer) {
+		wpa_printf(MSG_DEBUG, "OWE: Peer unknown");
+		return -1;
+	}
+	if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) {
+		wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured");
+		status = WLAN_STATUS_AKMP_NOT_VALID;
+		goto err;
+	}
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "OWE: Failed to parse OWE IE for "
+			   MACSTR, MAC2STR(peer));
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto err;
+	}
+	status = owe_validate_request(hapd, peer, elems.rsn_ie,
+				      elems.rsn_ie_len,
+				      elems.owe_dh, elems.owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto err;
+
+	sta = ap_get_sta(hapd, peer);
+	if (sta) {
+		ap_sta_no_session_timeout(hapd, sta);
+		accounting_sta_stop(hapd, sta);
+
+		/*
+		 * Make sure that the previously registered inactivity timer
+		 * will not remove the STA immediately.
+		 */
+		sta->timeout_next = STA_NULLFUNC;
+	} else {
+		sta = ap_sta_add(hapd, peer);
+		if (!sta) {
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto err;
+		}
+	}
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+	status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie,
+				    elems.rsn_ie_len, elems.owe_dh,
+				    elems.owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		ap_free_sta(hapd, sta);
+
+	return 0;
+err:
+	hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0);
+	return 0;
+}
+#endif /* CONFIG_OWE */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -1693,6 +1804,15 @@
 				    data->assoc_info.req_ies_len,
 				    data->assoc_info.reassoc);
 		break;
+#ifdef CONFIG_OWE
+	case EVENT_UPDATE_DH:
+		if (!data)
+			return;
+		hostapd_notif_update_dh_ie(hapd, data->update_dh.peer,
+					   data->update_dh.ie,
+					   data->update_dh.ie_len);
+		break;
+#endif /* CONFIG_OWE */
 	case EVENT_DISASSOC:
 		if (data)
 			hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
@@ -1709,6 +1829,7 @@
 	case EVENT_AUTH:
 		hostapd_notif_auth(hapd, &data->auth);
 		break;
+	case EVENT_CH_SWITCH_STARTED:
 	case EVENT_CH_SWITCH:
 		if (!data)
 			break;
@@ -1717,7 +1838,8 @@
 					data->ch_switch.ch_offset,
 					data->ch_switch.ch_width,
 					data->ch_switch.cf1,
-					data->ch_switch.cf2);
+					data->ch_switch.cf2,
+					event == EVENT_CH_SWITCH);
 		break;
 	case EVENT_CONNECT_FAILED_REASON:
 		if (!data)
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index a7df810..9567e20 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1522,9 +1522,9 @@
 
 
 #ifdef CONFIG_DPP
-static void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
-					const u8 *sa, u8 dialog_token,
-					int prot, struct wpabuf *buf)
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+				 const u8 *sa, u8 dialog_token,
+				 int prot, struct wpabuf *buf)
 {
 	struct wpabuf *tx_buf;
 
@@ -1681,7 +1681,8 @@
 	if (dpp) {
 		struct wpabuf *msg;
 
-		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen);
+		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
+						  data, len);
 		if (!msg)
 			return;
 		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 2cf1817..1528af4 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -88,4 +88,8 @@
 int gas_serv_init(struct hostapd_data *hapd);
 void gas_serv_deinit(struct hostapd_data *hapd);
 
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+				 const u8 *sa, u8 dialog_token,
+				 int prot, struct wpabuf *buf);
+
 #endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 0bd6892..e4950e3 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -7,6 +7,9 @@
  */
 
 #include "utils/includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "utils/common.h"
 #include "utils/eloop.h"
@@ -25,7 +28,6 @@
 #include "accounting.h"
 #include "ap_list.h"
 #include "beacon.h"
-#include "iapp.h"
 #include "ieee802_1x.h"
 #include "ieee802_11_auth.h"
 #include "vlan_init.h"
@@ -50,6 +52,8 @@
 #include "fils_hlp.h"
 #include "acs.h"
 #include "hs20.h"
+#include "airtime_policy.h"
+#include "wpa_auth_kay.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -260,11 +264,14 @@
 		hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
 		hapd->iconf->ht_capab = oldconf->ht_capab;
 		hapd->iconf->vht_capab = oldconf->vht_capab;
-		hapd->iconf->vht_oper_chwidth = oldconf->vht_oper_chwidth;
-		hapd->iconf->vht_oper_centr_freq_seg0_idx =
-			oldconf->vht_oper_centr_freq_seg0_idx;
-		hapd->iconf->vht_oper_centr_freq_seg1_idx =
-			oldconf->vht_oper_centr_freq_seg1_idx;
+		hostapd_set_oper_chwidth(hapd->iconf,
+					 hostapd_get_oper_chwidth(oldconf));
+		hostapd_set_oper_centr_freq_seg0_idx(
+			hapd->iconf,
+			hostapd_get_oper_centr_freq_seg0_idx(oldconf));
+		hostapd_set_oper_centr_freq_seg1_idx(
+			hapd->iconf,
+			hostapd_get_oper_centr_freq_seg1_idx(oldconf));
 		hapd->conf = newconf->bss[j];
 		hostapd_reload_bss(hapd);
 	}
@@ -291,7 +298,6 @@
 				   ifname, i);
 		}
 	}
-#ifdef CONFIG_IEEE80211W
 	if (hapd->conf->ieee80211w) {
 		for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
 			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
@@ -303,7 +309,6 @@
 			}
 		}
 	}
-#endif /* CONFIG_IEEE80211W */
 }
 
 
@@ -355,8 +360,6 @@
 	hapd->beacon_set_done = 0;
 
 	wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
-	iapp_deinit(hapd->iapp);
-	hapd->iapp = NULL;
 	accounting_deinit(hapd);
 	hostapd_deinit_wpa(hapd);
 	vlan_deinit(hapd);
@@ -369,6 +372,7 @@
 #endif /* CONFIG_NO_RADIUS */
 
 	hostapd_deinit_wps(hapd);
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
 #ifdef CONFIG_DPP
 	hostapd_dpp_deinit(hapd);
 	gas_query_ap_deinit(hapd->gas);
@@ -491,6 +495,7 @@
 	iface->basic_rates = NULL;
 	ap_list_deinit(iface);
 	sta_track_deinit(iface);
+	airtime_policy_update_deinit(iface);
 }
 
 
@@ -1018,6 +1023,43 @@
 #define hostapd_das_coa NULL
 #endif /* CONFIG_HS20 */
 
+
+#ifdef CONFIG_SQLITE
+
+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_radius_attributes(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE radius_attributes("
+		" id INTEGER PRIMARY KEY,"
+		" sta TEXT,"
+		" reqtype TEXT,"
+		" attr TEXT"
+		");"
+		"CREATE INDEX idx_sta_reqtype ON radius_attributes(sta,reqtype);";
+
+	wpa_printf(MSG_DEBUG,
+		   "Adding database table for RADIUS attribute information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_SQLITE */
+
 #endif /* CONFIG_NO_RADIUS */
 
 
@@ -1171,6 +1213,24 @@
 	if (wpa_debug_level <= MSG_MSGDUMP)
 		conf->radius->msg_dumps = 1;
 #ifndef CONFIG_NO_RADIUS
+
+#ifdef CONFIG_SQLITE
+	if (conf->radius_req_attr_sqlite) {
+		if (sqlite3_open(conf->radius_req_attr_sqlite,
+				 &hapd->rad_attr_db)) {
+			wpa_printf(MSG_ERROR, "Could not open SQLite file '%s'",
+				   conf->radius_req_attr_sqlite);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "Opening RADIUS attribute database: %s",
+			   conf->radius_req_attr_sqlite);
+		if (!db_table_exists(hapd->rad_attr_db, "radius_attributes") &&
+		    db_table_create_radius_attributes(hapd->rad_attr_db) < 0)
+			return -1;
+	}
+#endif /* CONFIG_SQLITE */
+
 	hapd->radius = radius_client_init(hapd, conf->radius);
 	if (hapd->radius == NULL) {
 		wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
@@ -1233,13 +1293,6 @@
 		return -1;
 	}
 
-	if (conf->ieee802_11f &&
-	    (hapd->iapp = iapp_init(hapd, conf->iapp_iface)) == NULL) {
-		wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization "
-			   "failed.");
-		return -1;
-	}
-
 #ifdef CONFIG_INTERWORKING
 	if (gas_serv_init(hapd)) {
 		wpa_printf(MSG_ERROR, "GAS server initialization failed");
@@ -1519,6 +1572,51 @@
 }
 
 
+static int configured_fixed_chan_to_freq(struct hostapd_iface *iface)
+{
+	int freq, i, j;
+
+	if (!iface->conf->channel)
+		return 0;
+	if (iface->conf->op_class) {
+		freq = ieee80211_chan_to_freq(NULL, iface->conf->op_class,
+					      iface->conf->channel);
+		if (freq < 0) {
+			wpa_printf(MSG_INFO,
+				   "Could not convert op_class %u channel %u to operating frequency",
+				   iface->conf->op_class, iface->conf->channel);
+			return -1;
+		}
+		iface->freq = freq;
+		return 0;
+	}
+
+	/* Old configurations using only 2.4/5/60 GHz bands may not specify the
+	 * op_class parameter. Select a matching channel from the configured
+	 * mode using the channel parameter for these cases.
+	 */
+	for (j = 0; j < iface->num_hw_features; j++) {
+		struct hostapd_hw_modes *mode = &iface->hw_features[j];
+
+		if (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
+		    iface->conf->hw_mode != mode->mode)
+			continue;
+		for (i = 0; i < mode->num_channels; i++) {
+			struct hostapd_channel_data *chan = &mode->channels[i];
+
+			if (chan->chan == iface->conf->channel &&
+			    !is_6ghz_freq(chan->freq)) {
+				iface->freq = chan->freq;
+				return 0;
+			}
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Could not determine operating frequency");
+	return -1;
+}
+
+
 static int setup_interface2(struct hostapd_iface *iface)
 {
 	iface->wait_channel_update = 0;
@@ -1527,7 +1625,20 @@
 		/* Not all drivers support this yet, so continue without hw
 		 * feature data. */
 	} else {
-		int ret = hostapd_select_hw_mode(iface);
+		int ret;
+
+		ret = configured_fixed_chan_to_freq(iface);
+		if (ret < 0)
+			goto fail;
+
+		if (iface->conf->op_class) {
+			int ch_width;
+
+			ch_width = op_class_to_ch_width(iface->conf->op_class);
+			hostapd_set_oper_chwidth(iface->conf, ch_width);
+		}
+
+		ret = hostapd_select_hw_mode(iface);
 		if (ret < 0) {
 			wpa_printf(MSG_ERROR, "Could not select hw_mode and "
 				   "channel. (%d)", ret);
@@ -1537,6 +1648,9 @@
 			wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
 			return 0;
 		}
+		ret = hostapd_check_edmg_capab(iface);
+		if (ret < 0)
+			goto fail;
 		ret = hostapd_check_ht_capab(iface);
 		if (ret < 0)
 			goto fail;
@@ -1808,12 +1922,11 @@
 		goto fail;
 
 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
-	if (iface->conf->channel) {
+	if (iface->freq) {
 #ifdef NEED_AP_MLME
 		int res;
 #endif /* NEED_AP_MLME */
 
-		iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
 		wpa_printf(MSG_DEBUG, "Mode: %s  Channel: %d  "
 			   "Frequency: %d MHz",
 			   hostapd_hw_mode_txt(iface->conf->hw_mode),
@@ -1861,12 +1974,17 @@
 		if (!delay_apply_cfg &&
 		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
+				     hapd->iconf->enable_edmg,
+				     hapd->iconf->edmg_channel,
 				     hapd->iconf->ieee80211n,
 				     hapd->iconf->ieee80211ac,
+				     hapd->iconf->ieee80211ax,
 				     hapd->iconf->secondary_channel,
-				     hapd->iconf->vht_oper_chwidth,
-				     hapd->iconf->vht_oper_centr_freq_seg0_idx,
-				     hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
+				     hostapd_get_oper_chwidth(hapd->iconf),
+				     hostapd_get_oper_centr_freq_seg0_idx(
+					     hapd->iconf),
+				     hostapd_get_oper_centr_freq_seg1_idx(
+					     hapd->iconf))) {
 			wpa_printf(MSG_ERROR, "Could not set channel for "
 				   "kernel driver");
 			goto fail;
@@ -1976,6 +2094,7 @@
 
 	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
 	hostapd_owe_update_trans(iface);
+	airtime_policy_update_init(iface);
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
 	if (hapd->setup_complete_cb)
 		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
@@ -2183,6 +2302,12 @@
 		   hapd->conf ? hapd->conf->iface : "N/A");
 	hostapd_bss_deinit_no_free(hapd);
 	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_SQLITE
+	if (hapd->rad_attr_db) {
+		sqlite3_close(hapd->rad_attr_db);
+		hapd->rad_attr_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
 	hostapd_cleanup(hapd);
 }
 
@@ -2983,10 +3108,6 @@
 	hostapd_prune_associations(hapd, sta->addr);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 
-	/* IEEE 802.11F (IAPP) */
-	if (hapd->conf->ieee802_11f)
-		iapp_new_station(hapd->iapp, sta);
-
 #ifdef CONFIG_P2P
 	if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
 		sta->no_p2p_set = 1;
@@ -2996,6 +3117,8 @@
 	}
 #endif /* CONFIG_P2P */
 
+	airtime_policy_new_sta(hapd, sta);
+
 	/* 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. */
@@ -3036,6 +3159,14 @@
 		eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
 				       ap_handle_timer, hapd, sta);
 	}
+
+#ifdef CONFIG_MACSEC
+	if (hapd->conf->wpa_key_mgmt == WPA_KEY_MGMT_NONE &&
+	    hapd->conf->mka_psk_set)
+		ieee802_1x_create_preshared_mka_hapd(hapd, sta);
+	else
+		ieee802_1x_alloc_kay_sm_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
 }
 
 
@@ -3195,6 +3326,8 @@
 				      struct hostapd_freq_params *old_params)
 {
 	int channel;
+	u8 seg0, seg1;
+	struct hostapd_hw_modes *mode;
 
 	if (!params->channel) {
 		/* check if the new channel is supported by hw */
@@ -3205,33 +3338,38 @@
 	if (!channel)
 		return -1;
 
+	mode = hapd->iface->current_mode;
+
 	/* if a pointer to old_params is provided we save previous state */
 	if (old_params &&
 	    hostapd_set_freq_params(old_params, conf->hw_mode,
 				    hostapd_hw_get_freq(hapd, conf->channel),
-				    conf->channel, conf->ieee80211n,
-				    conf->ieee80211ac,
+				    conf->channel, conf->enable_edmg,
+				    conf->edmg_channel, conf->ieee80211n,
+				    conf->ieee80211ac, conf->ieee80211ax,
 				    conf->secondary_channel,
-				    conf->vht_oper_chwidth,
-				    conf->vht_oper_centr_freq_seg0_idx,
-				    conf->vht_oper_centr_freq_seg1_idx,
-				    conf->vht_capab))
+				    hostapd_get_oper_chwidth(conf),
+				    hostapd_get_oper_centr_freq_seg0_idx(conf),
+				    hostapd_get_oper_centr_freq_seg1_idx(conf),
+				    conf->vht_capab,
+				    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+				    NULL))
 		return -1;
 
 	switch (params->bandwidth) {
 	case 0:
 	case 20:
 	case 40:
-		conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
 		break;
 	case 80:
 		if (params->center_freq2)
-			conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
+			hostapd_set_oper_chwidth(conf, CHANWIDTH_80P80MHZ);
 		else
-			conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
 		break;
 	case 160:
-		conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+		hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
 		break;
 	default:
 		return -1;
@@ -3241,9 +3379,11 @@
 	conf->ieee80211n = params->ht_enabled;
 	conf->secondary_channel = params->sec_channel_offset;
 	ieee80211_freq_to_chan(params->center_freq1,
-			       &conf->vht_oper_centr_freq_seg0_idx);
+			       &seg0);
 	ieee80211_freq_to_chan(params->center_freq2,
-			       &conf->vht_oper_centr_freq_seg1_idx);
+			       &seg1);
+	hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
+	hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
 
 	/* TODO: maybe call here hostapd_config_check here? */
 
@@ -3257,7 +3397,7 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
-	u8 chan, vht_bandwidth;
+	u8 chan, bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
 	if (!iface || !iface->freq || hapd->csa_in_progress)
@@ -3266,29 +3406,30 @@
 	switch (settings->freq_params.bandwidth) {
 	case 80:
 		if (settings->freq_params.center_freq2)
-			vht_bandwidth = VHT_CHANWIDTH_80P80MHZ;
+			bandwidth = CHANWIDTH_80P80MHZ;
 		else
-			vht_bandwidth = VHT_CHANWIDTH_80MHZ;
+			bandwidth = CHANWIDTH_80MHZ;
 		break;
 	case 160:
-		vht_bandwidth = VHT_CHANWIDTH_160MHZ;
+		bandwidth = CHANWIDTH_160MHZ;
 		break;
 	default:
-		vht_bandwidth = VHT_CHANWIDTH_USE_HT;
+		bandwidth = CHANWIDTH_USE_HT;
 		break;
 	}
 
 	if (ieee80211_freq_to_channel_ext(
 		    settings->freq_params.freq,
 		    settings->freq_params.sec_channel_offset,
-		    vht_bandwidth,
+		    bandwidth,
 		    &hapd->iface->cs_oper_class,
 		    &chan) == NUM_HOSTAPD_MODES) {
 		wpa_printf(MSG_DEBUG,
-			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)",
+			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)",
 			   settings->freq_params.freq,
 			   settings->freq_params.sec_channel_offset,
-			   settings->freq_params.vht_enabled);
+			   settings->freq_params.vht_enabled,
+			   settings->freq_params.he_enabled);
 		return -1;
 	}
 
@@ -3388,29 +3529,29 @@
 hostapd_switch_channel_fallback(struct hostapd_iface *iface,
 				const struct hostapd_freq_params *freq_params)
 {
-	int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
+	int seg0_idx = 0, seg1_idx = 0, bw = CHANWIDTH_USE_HT;
 
 	wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
 
 	if (freq_params->center_freq1)
-		vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+		seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
 	if (freq_params->center_freq2)
-		vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+		seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
 
 	switch (freq_params->bandwidth) {
 	case 0:
 	case 20:
 	case 40:
-		vht_bw = VHT_CHANWIDTH_USE_HT;
+		bw = CHANWIDTH_USE_HT;
 		break;
 	case 80:
 		if (freq_params->center_freq2)
-			vht_bw = VHT_CHANWIDTH_80P80MHZ;
+			bw = CHANWIDTH_80P80MHZ;
 		else
-			vht_bw = VHT_CHANWIDTH_80MHZ;
+			bw = CHANWIDTH_80MHZ;
 		break;
 	case 160:
-		vht_bw = VHT_CHANWIDTH_160MHZ;
+		bw = CHANWIDTH_160MHZ;
 		break;
 	default:
 		wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
@@ -3421,11 +3562,12 @@
 	iface->freq = freq_params->freq;
 	iface->conf->channel = freq_params->channel;
 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
-	iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx;
-	iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx;
-	iface->conf->vht_oper_chwidth = vht_bw;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
+	hostapd_set_oper_chwidth(iface->conf, bw);
 	iface->conf->ieee80211n = freq_params->ht_enabled;
 	iface->conf->ieee80211ac = freq_params->vht_enabled;
+	iface->conf->ieee80211ax = freq_params->he_enabled;
 
 	/*
 	 * cs_params must not be cleared earlier because the freq_params
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 607bb95..016fe3b 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -9,6 +9,10 @@
 #ifndef HOSTAPD_H
 #define HOSTAPD_H
 
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
 #include "common/defs.h"
 #include "utils/list.h"
 #include "ap_config.h"
@@ -175,13 +179,12 @@
 	u64 acct_session_id;
 	struct radius_das_data *radius_das;
 
-	struct iapp_data *iapp;
-
 	struct hostapd_cached_radius_acl *acl_cache;
 	struct hostapd_acl_query_data *acl_queries;
 
 	struct wpa_authenticator *wpa_auth;
 	struct eapol_authenticator *eapol_auth;
+	struct eap_config *eap_cfg;
 
 	struct rsn_preauth_interface *preauth_iface;
 	struct os_reltime michael_mic_failure;
@@ -232,6 +235,10 @@
 	struct wps_stat wps_stats;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_MACSEC
+	struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
+
 	struct hostapd_probereq_cb *probereq_cb;
 	size_t num_probereq_cb;
 
@@ -329,12 +336,10 @@
 	u8 last_gtk[WPA_GTK_MAX_LEN];
 	size_t last_gtk_len;
 
-#ifdef CONFIG_IEEE80211W
 	enum wpa_alg last_igtk_alg;
 	int last_igtk_key_idx;
 	u8 last_igtk[WPA_IGTK_MAX_LEN];
 	size_t last_igtk_len;
-#endif /* CONFIG_IEEE80211W */
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_MBO
@@ -379,6 +384,17 @@
 	unsigned int dpp_ignore_netaccesskey_mismatch:1;
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_DPP */
+
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int num_backlogged_sta;
+	unsigned int airtime_weight;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	u8 last_1x_eapol_key_replay_counter[8];
+
+#ifdef CONFIG_SQLITE
+	sqlite3 *rad_attr_db;
+#endif /* CONFIG_SQLITE */
 };
 
 
@@ -542,6 +558,12 @@
 	unsigned int num_sta_seen;
 
 	u8 dfs_domain;
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_quantum;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	/* Previous WMM element information */
+	struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
 };
 
 /* hostapd.c */
@@ -608,7 +630,8 @@
 			 const u8 *bssid, const u8 *ie, size_t ie_len,
 			 int ssi_signal);
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
-			     int offset, int width, int cf1, int cf2);
+			     int offset, int width, int cf1, int cf2,
+			     int finished);
 struct survey_results;
 void hostapd_event_get_survey(struct hostapd_iface *iface,
 			      struct survey_results *survey_results);
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 532580e..543fa33 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -80,13 +80,11 @@
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
 	}
-#ifdef CONFIG_IEEE80211W
 	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		capab |= WPA_CAPABILITY_MFPC;
 		if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
 			capab |= WPA_CAPABILITY_MFPR;
 	}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	if (hapd->conf->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8ac33bb..ba10752 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -227,13 +227,25 @@
 #ifdef CONFIG_IEEE80211N
 static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 {
-	int pri_chan, sec_chan;
+	int pri_freq, sec_freq;
+	struct hostapd_channel_data *p_chan, *s_chan;
 
-	pri_chan = iface->conf->channel;
-	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+	pri_freq = iface->freq;
+	sec_freq = pri_freq + iface->conf->secondary_channel * 20;
 
-	return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
-					 sec_chan);
+	if (!iface->current_mode)
+		return 0;
+
+	p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL,
+				     iface->hw_features,
+				     iface->num_hw_features);
+
+	s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL,
+				     iface->hw_features,
+				     iface->num_hw_features);
+
+	return allowed_ht40_channel_pair(iface->current_mode->mode,
+					 p_chan, s_chan);
 }
 
 
@@ -241,9 +253,11 @@
 {
 	if (iface->conf->secondary_channel > 0) {
 		iface->conf->channel += 4;
+		iface->freq += 20;
 		iface->conf->secondary_channel = -1;
 	} else {
 		iface->conf->channel -= 4;
+		iface->freq -= 20;
 		iface->conf->secondary_channel = 1;
 	}
 }
@@ -252,13 +266,23 @@
 static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
 				     struct wpa_scan_results *scan_res)
 {
-	int pri_chan, sec_chan;
+	unsigned int pri_freq, sec_freq;
 	int res;
+	struct hostapd_channel_data *pri_chan, *sec_chan;
 
-	pri_chan = iface->conf->channel;
-	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+	pri_freq = iface->freq;
+	sec_freq = pri_freq + iface->conf->secondary_channel * 20;
 
-	res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq,
+				       NULL, iface->hw_features,
+				       iface->num_hw_features);
+	sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq,
+				       NULL, iface->hw_features,
+				       iface->num_hw_features);
+
+	res = check_40mhz_5g(scan_res, pri_chan, sec_chan);
 
 	if (res == 2) {
 		if (iface->conf->no_pri_sec_switch) {
@@ -329,9 +353,9 @@
 	res = ieee80211n_allowed_ht40_channel_pair(iface);
 	if (!res) {
 		iface->conf->secondary_channel = 0;
-		iface->conf->vht_oper_centr_freq_seg0_idx = 0;
-		iface->conf->vht_oper_centr_freq_seg1_idx = 0;
-		iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
+		hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT);
 		res = 1;
 		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
 	}
@@ -352,7 +376,7 @@
 	if (iface->current_mode == NULL)
 		return;
 
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	pri_freq = iface->freq;
 	if (iface->conf->secondary_channel > 0)
 		sec_freq = pri_freq + 20;
 	else
@@ -397,7 +421,7 @@
 	if (iface->current_mode == NULL)
 		return;
 
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	pri_freq = iface->freq;
 	if (iface->conf->secondary_channel > 0) {
 		affected_start = pri_freq - 10;
 		affected_end = pri_freq + 30;
@@ -655,6 +679,14 @@
 }
 #endif /* CONFIG_IEEE80211AC */
 
+
+#ifdef CONFIG_IEEE80211AX
+static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+{
+	return 1;
+}
+#endif /* CONFIG_IEEE80211AX */
+
 #endif /* CONFIG_IEEE80211N */
 
 
@@ -662,6 +694,9 @@
 {
 #ifdef CONFIG_IEEE80211N
 	int ret;
+
+	if (is_6ghz_freq(iface->freq))
+		return 0;
 	if (!iface->conf->ieee80211n)
 		return 0;
 
@@ -675,6 +710,11 @@
 
 	if (!ieee80211n_supported_ht_capab(iface))
 		return -1;
+#ifdef CONFIG_IEEE80211AX
+	if (iface->conf->ieee80211ax &&
+	    !ieee80211ax_supported_he_capab(iface))
+		return -1;
+#endif /* CONFIG_IEEE80211AX */
 #ifdef CONFIG_IEEE80211AC
 	if (iface->conf->ieee80211ac &&
 	    !ieee80211ac_supported_vht_capab(iface))
@@ -691,15 +731,42 @@
 }
 
 
+int hostapd_check_edmg_capab(struct hostapd_iface *iface)
+{
+	struct hostapd_hw_modes *mode = iface->hw_features;
+	struct ieee80211_edmg_config edmg;
+
+	if (!iface->conf->enable_edmg)
+		return 0;
+
+	hostapd_encode_edmg_chan(iface->conf->enable_edmg,
+				 iface->conf->edmg_channel,
+				 iface->conf->channel,
+				 &edmg);
+
+	if (mode->edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg))
+		return 0;
+
+	wpa_printf(MSG_WARNING, "Requested EDMG configuration is not valid");
+	wpa_printf(MSG_INFO, "EDMG capab: channels 0x%x, bw_config %d",
+		   mode->edmg.channels, mode->edmg.bw_config);
+	wpa_printf(MSG_INFO,
+		   "Requested EDMG configuration: channels 0x%x, bw_config %d",
+		   edmg.channels, edmg.bw_config);
+	return -1;
+}
+
+
 static int hostapd_is_usable_chan(struct hostapd_iface *iface,
-				  int channel, int primary)
+				  int frequency, int primary)
 {
 	struct hostapd_channel_data *chan;
 
 	if (!iface->current_mode)
 		return 0;
 
-	chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
+	chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL,
+				   iface->hw_features, iface->num_hw_features);
 	if (!chan)
 		return 0;
 
@@ -708,8 +775,8 @@
 		return 1;
 
 	wpa_printf(MSG_INFO,
-		   "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
-		   channel, primary ? "primary" : "secondary",
+		   "Frequency %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
+		   frequency, primary ? "primary" : "secondary",
 		   chan->flag,
 		   chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
 		   chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
@@ -717,17 +784,96 @@
 }
 
 
-static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+static int hostapd_is_usable_edmg(struct hostapd_iface *iface)
 {
-	int secondary_chan;
+	int i, contiguous = 0;
+	int num_of_enabled = 0;
+	int max_contiguous = 0;
+	struct ieee80211_edmg_config edmg;
 	struct hostapd_channel_data *pri_chan;
 
-	pri_chan = hw_get_channel_chan(iface->current_mode,
-				       iface->conf->channel, NULL);
-	if (!pri_chan)
+	if (!iface->conf->enable_edmg)
+		return 1;
+
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+				       iface->freq, NULL,
+				       iface->hw_features,
+				       iface->num_hw_features);
+	hostapd_encode_edmg_chan(iface->conf->enable_edmg,
+				 iface->conf->edmg_channel,
+				 pri_chan->chan,
+				 &edmg);
+	if (!(edmg.channels & BIT(pri_chan->chan - 1)))
 		return 0;
 
-	if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
+	/* 60 GHz channels 1..6 */
+	for (i = 0; i < 6; i++) {
+		int freq = 56160 + 2160 * i;
+
+		if (edmg.channels & BIT(i)) {
+			contiguous++;
+			num_of_enabled++;
+		} else {
+			contiguous = 0;
+			continue;
+		}
+
+		/* P802.11ay defines that the total number of subfields
+		 * set to one does not exceed 4.
+		 */
+		if (num_of_enabled > 4)
+			return 0;
+
+		if (!hostapd_is_usable_chan(iface, freq, 1))
+			return 0;
+
+		if (contiguous > max_contiguous)
+			max_contiguous = contiguous;
+	}
+
+	/* Check if the EDMG configuration is valid under the limitations
+	 * of P802.11ay.
+	 */
+	/* check bw_config against contiguous EDMG channels */
+	switch (edmg.bw_config) {
+	case EDMG_BW_CONFIG_4:
+		if (!max_contiguous)
+			return 0;
+		break;
+	case EDMG_BW_CONFIG_5:
+		if (max_contiguous < 2)
+			return 0;
+		break;
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+	int secondary_freq;
+	struct hostapd_channel_data *pri_chan;
+
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+				       iface->freq, NULL,
+				       iface->hw_features,
+				       iface->num_hw_features);
+	if (!pri_chan) {
+		wpa_printf(MSG_ERROR, "Primary frequency not present");
+		return 0;
+	}
+	if (!hostapd_is_usable_chan(iface, pri_chan->freq, 1)) {
+		wpa_printf(MSG_ERROR, "Primary frequency not allowed");
+		return 0;
+	}
+	if (!hostapd_is_usable_edmg(iface))
 		return 0;
 
 	if (!iface->conf->secondary_channel)
@@ -735,19 +881,19 @@
 
 	if (!iface->conf->ht40_plus_minus_allowed)
 		return hostapd_is_usable_chan(
-			iface, iface->conf->channel +
-			iface->conf->secondary_channel * 4, 0);
+			iface,
+			iface->freq + iface->conf->secondary_channel * 20, 0);
 
 	/* Both HT40+ and HT40- are set, pick a valid secondary channel */
-	secondary_chan = iface->conf->channel + 4;
-	if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+	secondary_freq = iface->freq + 20;
+	if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
 	    (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
 		iface->conf->secondary_channel = 1;
 		return 1;
 	}
 
-	secondary_chan = iface->conf->channel - 4;
-	if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+	secondary_freq = iface->freq - 20;
+	if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
 	    (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
 		iface->conf->secondary_channel = -1;
 		return 1;
@@ -760,7 +906,7 @@
 static enum hostapd_chan_status
 hostapd_check_chans(struct hostapd_iface *iface)
 {
-	if (iface->conf->channel) {
+	if (iface->freq) {
 		if (hostapd_is_usable_chans(iface))
 			return HOSTAPD_CHAN_VALID;
 		else
@@ -794,9 +940,9 @@
 	hostapd_logger(iface->bss[0], NULL,
 		       HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_WARNING,
-		       "Configured channel (%d) not found from the "
-		       "channel list of current mode (%d) %s",
+		       "Configured channel (%d) or frequency (%d) not found from the channel list of the current mode (%d) %s",
 		       iface->conf->channel,
+		       iface->freq,
 		       iface->current_mode->mode,
 		       hostapd_hw_mode_txt(iface->current_mode->mode));
 	hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
@@ -816,9 +962,7 @@
 	case HOSTAPD_CHAN_VALID:
 		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
 			ACS_EVENT_COMPLETED "freq=%d channel=%d",
-			hostapd_hw_get_freq(iface->bss[0],
-					    iface->conf->channel),
-			iface->conf->channel);
+			iface->freq, iface->conf->channel);
 		break;
 	case HOSTAPD_CHAN_ACS:
 		wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
@@ -863,18 +1007,25 @@
 		return -1;
 
 	if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
-	     iface->conf->ieee80211n || iface->conf->ieee80211ac) &&
+	     iface->conf->ieee80211n || iface->conf->ieee80211ac ||
+	     iface->conf->ieee80211ax) &&
 	    iface->conf->channel == 14) {
-		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14");
+		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14");
 		iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
 		iface->conf->ieee80211n = 0;
 		iface->conf->ieee80211ac = 0;
+		iface->conf->ieee80211ax = 0;
 	}
 
 	iface->current_mode = NULL;
 	for (i = 0; i < iface->num_hw_features; i++) {
 		struct hostapd_hw_modes *mode = &iface->hw_features[i];
 		if (mode->mode == iface->conf->hw_mode) {
+			if (iface->freq > 0 &&
+			    !hw_get_chan(mode->mode, iface->freq,
+					 iface->hw_features,
+					 iface->num_hw_features))
+				continue;
 			iface->current_mode = mode;
 			break;
 		}
@@ -937,7 +1088,9 @@
 	struct hostapd_hw_modes *mode;
 
 	if (hapd->iface->current_mode) {
-		channel = hw_get_chan(hapd->iface->current_mode, freq);
+		channel = hw_get_chan(hapd->iface->current_mode->mode, freq,
+				      hapd->iface->hw_features,
+				      hapd->iface->num_hw_features);
 		if (channel)
 			return channel;
 	}
@@ -948,7 +1101,9 @@
 		return 0;
 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
 		mode = &hapd->iface->hw_features[i];
-		channel = hw_get_chan(mode, freq);
+		channel = hw_get_chan(mode->mode, freq,
+				      hapd->iface->hw_features,
+				      hapd->iface->num_hw_features);
 		if (channel)
 			return channel;
 	}
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index ca7f22b..902a19f 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -21,6 +21,7 @@
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
 int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
 int hostapd_check_ht_capab(struct hostapd_iface *iface);
+int hostapd_check_edmg_capab(struct hostapd_iface *iface);
 int hostapd_prepare_rates(struct hostapd_iface *iface,
 			  struct hostapd_hw_modes *mode);
 void hostapd_stop_setup_timers(struct hostapd_iface *iface);
@@ -61,6 +62,11 @@
 	return 0;
 }
 
+static inline int hostapd_check_edmg_capab(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
 static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
 					struct hostapd_hw_modes *mode)
 {
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
deleted file mode 100644
index 2556da3..0000000
--- a/src/ap/iapp.c
+++ /dev/null
@@ -1,542 +0,0 @@
-/*
- * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
- * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- *
- * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
- * and IEEE has withdrawn it. In other words, it is likely better to look at
- * using some other mechanism for AP-to-AP communication than extending the
- * implementation here.
- */
-
-/* TODO:
- * Level 1: no administrative or security support
- *	(e.g., static BSSID to IP address mapping in each AP)
- * Level 2: support for dynamic mapping of BSSID to IP address
- * Level 3: support for encryption and authentication of IAPP messages
- * - add support for MOVE-notify and MOVE-response (this requires support for
- *   finding out IP address for previous AP using RADIUS)
- * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
- *   reassociation to another AP
- * - implement counters etc. for IAPP MIB
- * - verify endianness of fields in IAPP messages; are they big-endian as
- *   used here?
- * - RADIUS connection for AP registration and BSSID to IP address mapping
- * - TCP connection for IAPP MOVE, CACHE
- * - broadcast ESP for IAPP ADD-notify
- * - ESP for IAPP MOVE messages
- * - security block sending/processing
- * - IEEE 802.11 context transfer
- */
-
-#include "utils/includes.h"
-#include <net/if.h>
-#include <sys/ioctl.h>
-#include <netpacket/packet.h>
-
-#include "utils/common.h"
-#include "utils/eloop.h"
-#include "common/ieee802_11_defs.h"
-#include "hostapd.h"
-#include "ap_config.h"
-#include "ieee802_11.h"
-#include "sta_info.h"
-#include "iapp.h"
-
-
-#define IAPP_MULTICAST "224.0.1.178"
-#define IAPP_UDP_PORT 3517
-#define IAPP_TCP_PORT 3517
-
-struct iapp_hdr {
-	u8 version;
-	u8 command;
-	be16 identifier;
-	be16 length;
-	/* followed by length-6 octets of data */
-} __attribute__ ((packed));
-
-#define IAPP_VERSION 0
-
-enum IAPP_COMMAND {
-	IAPP_CMD_ADD_notify = 0,
-	IAPP_CMD_MOVE_notify = 1,
-	IAPP_CMD_MOVE_response = 2,
-	IAPP_CMD_Send_Security_Block = 3,
-	IAPP_CMD_ACK_Security_Block = 4,
-	IAPP_CMD_CACHE_notify = 5,
-	IAPP_CMD_CACHE_response = 6,
-};
-
-
-/* ADD-notify - multicast UDP on the local LAN */
-struct iapp_add_notify {
-	u8 addr_len; /* ETH_ALEN */
-	u8 reserved;
-	u8 mac_addr[ETH_ALEN];
-	be16 seq_num;
-} __attribute__ ((packed));
-
-
-/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
-struct iapp_layer2_update {
-	u8 da[ETH_ALEN]; /* broadcast */
-	u8 sa[ETH_ALEN]; /* STA addr */
-	be16 len; /* 6 */
-	u8 dsap; /* null DSAP address */
-	u8 ssap; /* null SSAP address, CR=Response */
-	u8 control;
-	u8 xid_info[3];
-} __attribute__ ((packed));
-
-
-/* MOVE-notify - unicast TCP */
-struct iapp_move_notify {
-	u8 addr_len; /* ETH_ALEN */
-	u8 reserved;
-	u8 mac_addr[ETH_ALEN];
-	u16 seq_num;
-	u16 ctx_block_len;
-	/* followed by ctx_block_len bytes */
-} __attribute__ ((packed));
-
-
-/* MOVE-response - unicast TCP */
-struct iapp_move_response {
-	u8 addr_len; /* ETH_ALEN */
-	u8 status;
-	u8 mac_addr[ETH_ALEN];
-	u16 seq_num;
-	u16 ctx_block_len;
-	/* followed by ctx_block_len bytes */
-} __attribute__ ((packed));
-
-enum {
-	IAPP_MOVE_SUCCESSFUL = 0,
-	IAPP_MOVE_DENIED = 1,
-	IAPP_MOVE_STALE_MOVE = 2,
-};
-
-
-/* CACHE-notify */
-struct iapp_cache_notify {
-	u8 addr_len; /* ETH_ALEN */
-	u8 reserved;
-	u8 mac_addr[ETH_ALEN];
-	u16 seq_num;
-	u8 current_ap[ETH_ALEN];
-	u16 ctx_block_len;
-	/* ctx_block_len bytes of context block followed by 16-bit context
-	 * timeout */
-} __attribute__ ((packed));
-
-
-/* CACHE-response - unicast TCP */
-struct iapp_cache_response {
-	u8 addr_len; /* ETH_ALEN */
-	u8 status;
-	u8 mac_addr[ETH_ALEN];
-	u16 seq_num;
-} __attribute__ ((packed));
-
-enum {
-	IAPP_CACHE_SUCCESSFUL = 0,
-	IAPP_CACHE_STALE_CACHE = 1,
-};
-
-
-/* Send-Security-Block - unicast TCP */
-struct iapp_send_security_block {
-	u8 iv[8];
-	u16 sec_block_len;
-	/* followed by sec_block_len bytes of security block */
-} __attribute__ ((packed));
-
-
-/* ACK-Security-Block - unicast TCP */
-struct iapp_ack_security_block {
-	u8 iv[8];
-	u8 new_ap_ack_authenticator[48];
-} __attribute__ ((packed));
-
-
-struct iapp_data {
-	struct hostapd_data *hapd;
-	u16 identifier; /* next IAPP identifier */
-	struct in_addr own, multicast;
-	int udp_sock;
-	int packet_sock;
-};
-
-
-static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
-{
-	char buf[128];
-	struct iapp_hdr *hdr;
-	struct iapp_add_notify *add;
-	struct sockaddr_in addr;
-
-	/* Send IAPP ADD-notify to remove possible association from other APs
-	 */
-
-	hdr = (struct iapp_hdr *) buf;
-	hdr->version = IAPP_VERSION;
-	hdr->command = IAPP_CMD_ADD_notify;
-	hdr->identifier = host_to_be16(iapp->identifier++);
-	hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
-
-	add = (struct iapp_add_notify *) (hdr + 1);
-	add->addr_len = ETH_ALEN;
-	add->reserved = 0;
-	os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
-
-	add->seq_num = host_to_be16(seq_num);
-	
-	os_memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_addr.s_addr = iapp->multicast.s_addr;
-	addr.sin_port = htons(IAPP_UDP_PORT);
-	if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
-		   (struct sockaddr *) &addr, sizeof(addr)) < 0)
-		wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
-}
-
-
-static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
-{
-	struct iapp_layer2_update msg;
-
-	/* Send Level 2 Update Frame to update forwarding tables in layer 2
-	 * bridge devices */
-
-	/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
-	 * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
-
-	os_memset(msg.da, 0xff, ETH_ALEN);
-	os_memcpy(msg.sa, addr, ETH_ALEN);
-	msg.len = host_to_be16(6);
-	msg.dsap = 0; /* NULL DSAP address */
-	msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
-	msg.control = 0xaf; /* XID response lsb.1111F101.
-			     * F=0 (no poll command; unsolicited frame) */
-	msg.xid_info[0] = 0x81; /* XID format identifier */
-	msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
-	msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
-				   * FIX: what is correct RW with 802.11? */
-
-	if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
-		wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
-}
-
-
-/**
- * iapp_new_station - IAPP processing for a new STA
- * @iapp: IAPP data
- * @sta: The associated station
- */
-void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
-{
-	u16 seq = 0; /* TODO */
-
-	if (iapp == NULL)
-		return;
-
-	/* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
-	hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
-		       HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
-	iapp_send_layer2_update(iapp, sta->addr);
-	iapp_send_add(iapp, sta->addr, seq);
-
-	/* TODO: If this was reassociation:
-	 * IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
-	 *                   Context Block, Timeout)
-	 * TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
-	 * IP address */
-}
-
-
-static void iapp_process_add_notify(struct iapp_data *iapp,
-				    struct sockaddr_in *from,
-				    struct iapp_hdr *hdr, int len)
-{
-	struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
-	struct sta_info *sta;
-
-	if (len != sizeof(*add)) {
-		wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
-			   len, (unsigned long) sizeof(*add));
-		return;
-	}
-
-	sta = ap_get_sta(iapp->hapd, add->mac_addr);
-
-	/* IAPP-ADD.indication(MAC Address, Sequence Number) */
-	hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
-		       HOSTAPD_LEVEL_INFO,
-		       "Received IAPP ADD-notify (seq# %d) from %s:%d%s",
-		       be_to_host16(add->seq_num),
-		       inet_ntoa(from->sin_addr), ntohs(from->sin_port),
-		       sta ? "" : " (STA not found)");
-
-	if (!sta)
-		return;
-
-	/* TODO: could use seq_num to try to determine whether last association
-	 * to this AP is newer than the one advertised in IAPP-ADD. Although,
-	 * this is not really a reliable verification. */
-
-	hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
-		       HOSTAPD_LEVEL_DEBUG,
-		       "Removing STA due to IAPP ADD-notify");
-	ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
-}
-
-
-/**
- * iapp_receive_udp - Process IAPP UDP frames
- * @sock: File descriptor for the socket
- * @eloop_ctx: IAPP data (struct iapp_data *)
- * @sock_ctx: Not used
- */
-static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
-{
-	struct iapp_data *iapp = eloop_ctx;
-	int len, hlen;
-	unsigned char buf[128];
-	struct sockaddr_in from;
-	socklen_t fromlen;
-	struct iapp_hdr *hdr;
-
-	/* Handle incoming IAPP frames (over UDP/IP) */
-
-	fromlen = sizeof(from);
-	len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
-		       (struct sockaddr *) &from, &fromlen);
-	if (len < 0) {
-		wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
-			   strerror(errno));
-		return;
-	}
-
-	if (from.sin_addr.s_addr == iapp->own.s_addr)
-		return; /* ignore own IAPP messages */
-
-	hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
-		       HOSTAPD_LEVEL_DEBUG,
-		       "Received %d byte IAPP frame from %s%s\n",
-		       len, inet_ntoa(from.sin_addr),
-		       len < (int) sizeof(*hdr) ? " (too short)" : "");
-
-	if (len < (int) sizeof(*hdr))
-		return;
-
-	hdr = (struct iapp_hdr *) buf;
-	hlen = be_to_host16(hdr->length);
-	hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
-		       HOSTAPD_LEVEL_DEBUG,
-		       "RX: version=%d command=%d id=%d len=%d\n",
-		       hdr->version, hdr->command,
-		       be_to_host16(hdr->identifier), hlen);
-	if (hdr->version != IAPP_VERSION) {
-		wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
-			   hdr->version);
-		return;
-	}
-	if (hlen > len) {
-		wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
-			   hlen, len);
-		return;
-	}
-	if (hlen < len) {
-		wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
-			   len - hlen);
-		len = hlen;
-	}
-
-	switch (hdr->command) {
-	case IAPP_CMD_ADD_notify:
-		iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
-		break;
-	case IAPP_CMD_MOVE_notify:
-		/* TODO: MOVE is using TCP; so move this to TCP handler once it
-		 * is implemented.. */
-		/* IAPP-MOVE.indication(MAC Address, New BSSID,
-		 * Sequence Number, AP Address, Context Block) */
-		/* TODO: process */
-		break;
-	default:
-		wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
-		break;
-	}
-}
-
-
-struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
-{
-	struct ifreq ifr;
-	struct sockaddr_ll addr;
-	int ifindex;
-	struct sockaddr_in *paddr, uaddr;
-	struct iapp_data *iapp;
-	struct ip_mreqn mreq;
-	int reuseaddr = 1;
-
-	iapp = os_zalloc(sizeof(*iapp));
-	if (iapp == NULL)
-		return NULL;
-	iapp->hapd = hapd;
-	iapp->udp_sock = iapp->packet_sock = -1;
-
-	/* TODO:
-	 * open socket for sending and receiving IAPP frames over TCP
-	 */
-
-	iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
-	if (iapp->udp_sock < 0) {
-		wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
-	if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
-		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-	ifindex = ifr.ifr_ifindex;
-
-	if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
-		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
-	if (paddr->sin_family != AF_INET) {
-		wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
-			   paddr->sin_family);
-		iapp_deinit(iapp);
-		return NULL;
-	}
-	iapp->own.s_addr = paddr->sin_addr.s_addr;
-
-	if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
-		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
-	if (paddr->sin_family != AF_INET) {
-		wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
-			   paddr->sin_family);
-		iapp_deinit(iapp);
-		return NULL;
-	}
-	inet_aton(IAPP_MULTICAST, &iapp->multicast);
-
-	os_memset(&uaddr, 0, sizeof(uaddr));
-	uaddr.sin_family = AF_INET;
-	uaddr.sin_port = htons(IAPP_UDP_PORT);
-
-	if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
-		       sizeof(reuseaddr)) < 0) {
-		wpa_printf(MSG_INFO,
-			   "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s",
-			   strerror(errno));
-		/*
-		 * Ignore this and try to continue. This is fine for single
-		 * BSS cases, but may fail if multiple BSSes enable IAPP.
-		 */
-	}
-
-	if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
-		 sizeof(uaddr)) < 0) {
-		wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-
-	os_memset(&mreq, 0, sizeof(mreq));
-	mreq.imr_multiaddr = iapp->multicast;
-	mreq.imr_address.s_addr = INADDR_ANY;
-	mreq.imr_ifindex = 0;
-	if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
-		       sizeof(mreq)) < 0) {
-		wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-
-	iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-	if (iapp->packet_sock < 0) {
-		wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-
-	os_memset(&addr, 0, sizeof(addr));
-	addr.sll_family = AF_PACKET;
-	addr.sll_ifindex = ifindex;
-	if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
-		 sizeof(addr)) < 0) {
-		wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
-			   strerror(errno));
-		iapp_deinit(iapp);
-		return NULL;
-	}
-
-	if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
-				     iapp, NULL)) {
-		wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
-		iapp_deinit(iapp);
-		return NULL;
-	}
-
-	wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
-
-	/* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
-	 * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
-	 * be openned only after receiving Initiate-Accept. If Initiate-Reject
-	 * is received, IAPP is not started. */
-
-	return iapp;
-}
-
-
-void iapp_deinit(struct iapp_data *iapp)
-{
-	struct ip_mreqn mreq;
-
-	if (iapp == NULL)
-		return;
-
-	if (iapp->udp_sock >= 0) {
-		os_memset(&mreq, 0, sizeof(mreq));
-		mreq.imr_multiaddr = iapp->multicast;
-		mreq.imr_address.s_addr = INADDR_ANY;
-		mreq.imr_ifindex = 0;
-		if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
-			       &mreq, sizeof(mreq)) < 0) {
-			wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
-				   strerror(errno));
-		}
-
-		eloop_unregister_read_sock(iapp->udp_sock);
-		close(iapp->udp_sock);
-	}
-	if (iapp->packet_sock >= 0) {
-		eloop_unregister_read_sock(iapp->packet_sock);
-		close(iapp->packet_sock);
-	}
-	os_free(iapp);
-}
diff --git a/src/ap/iapp.h b/src/ap/iapp.h
deleted file mode 100644
index c221183..0000000
--- a/src/ap/iapp.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
- * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef IAPP_H
-#define IAPP_H
-
-struct iapp_data;
-
-#ifdef CONFIG_IAPP
-
-void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta);
-struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface);
-void iapp_deinit(struct iapp_data *iapp);
-
-#else /* CONFIG_IAPP */
-
-static inline void iapp_new_station(struct iapp_data *iapp,
-				    struct sta_info *sta)
-{
-}
-
-static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
-					   const char *iface)
-{
-	return NULL;
-}
-
-static inline void iapp_deinit(struct iapp_data *iapp)
-{
-}
-
-#endif /* CONFIG_IAPP */
-
-#endif /* IAPP_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 36161c2..5b70425 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -23,6 +23,7 @@
 #include "common/sae.h"
 #include "common/dpp.h"
 #include "common/ocv.h"
+#include "common/wpa_common.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -97,6 +98,9 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt))
+		num++;
 	if (num > 8) {
 		/* rest of the rates are encoded in Extended supported
 		 * rates element */
@@ -123,6 +127,13 @@
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 	}
 
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+	    count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+	}
+
 	return pos;
 }
 
@@ -140,6 +151,9 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt))
+		num++;
 	if (num <= 8)
 		return eid;
 	num -= 8;
@@ -169,6 +183,13 @@
 			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 	}
 
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+	}
+
 	return pos;
 }
 
@@ -387,15 +408,25 @@
 
 
 static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
-					     struct sta_info *sta, int update)
+					     struct sta_info *sta, int update,
+					     int status_code)
 {
 	struct wpabuf *buf;
 	const char *password = NULL;
 	struct sae_password_entry *pw;
 	const char *rx_id = NULL;
+	int use_pt = 0;
+	struct sae_pt *pt = NULL;
 
-	if (sta->sae->tmp)
+	if (sta->sae->tmp) {
 		rx_id = sta->sae->tmp->pw_id;
+		use_pt = sta->sae->tmp->h2e;
+	}
+
+	if (status_code == WLAN_STATUS_SUCCESS)
+		use_pt = 0;
+	else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+		use_pt = 1;
 
 	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
 		if (!is_broadcast_ether_addr(pw->peer_addr) &&
@@ -407,16 +438,24 @@
 		    os_strcmp(rx_id, pw->identifier) != 0)
 			continue;
 		password = pw->password;
+		pt = pw->pt;
 		break;
 	}
-	if (!password)
-		password = hapd->conf->ssid.wpa_passphrase;
 	if (!password) {
+		password = hapd->conf->ssid.wpa_passphrase;
+		pt = hapd->conf->ssid.pt;
+	}
+	if (!password || (use_pt && !pt)) {
 		wpa_printf(MSG_DEBUG, "SAE: No password available");
 		return NULL;
 	}
 
-	if (update &&
+	if (update && use_pt &&
+	    sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
+				  NULL) < 0)
+		return NULL;
+
+	if (update && !use_pt &&
 	    sae_prepare_commit(hapd->own_addr, sta->addr,
 			       (u8 *) password, os_strlen(password), rx_id,
 			       sta->sae) < 0) {
@@ -461,19 +500,22 @@
 
 static int auth_sae_send_commit(struct hostapd_data *hapd,
 				struct sta_info *sta,
-				const u8 *bssid, int update)
+				const u8 *bssid, int update, int status_code)
 {
 	struct wpabuf *data;
 	int reply_res;
+	u16 status;
 
-	data = auth_build_sae_commit(hapd, sta, update);
+	data = auth_build_sae_commit(hapd, sta, update, status_code);
 	if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
 		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 	if (data == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
+	status = (sta->sae->tmp && sta->sae->tmp->h2e) ?
+		WLAN_STATUS_SAE_HASH_TO_ELEMENT : WLAN_STATUS_SUCCESS;
 	reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
-				    WLAN_STATUS_SUCCESS, wpabuf_head(data),
+				    status, wpabuf_head(data),
 				    wpabuf_len(data), "sae-send-commit");
 
 	wpabuf_free(data);
@@ -662,7 +704,7 @@
 
 	switch (sta->sae->state) {
 	case SAE_COMMITTED:
-		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
 		eloop_register_timeout(0,
 				       hapd->dot11RSNASAERetransPeriod * 1000,
 				       auth_sae_retransmit_timer, hapd, sta);
@@ -709,7 +751,8 @@
 	os_memset(&params, 0, sizeof(params));
 	params.status = status;
 	params.bssid = sta->addr;
-	if (status == WLAN_STATUS_SUCCESS && sta->sae)
+	if (status == WLAN_STATUS_SUCCESS && sta->sae &&
+	    !hapd->conf->disable_pmksa_caching)
 		params.pmkid = sta->sae->pmkid;
 
 	hostapd_drv_send_external_auth_status(hapd, &params);
@@ -759,8 +802,8 @@
 
 
 static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
-		       const u8 *bssid, u8 auth_transaction, int allow_reuse,
-		       int *sta_removed)
+		       const u8 *bssid, u16 auth_transaction, u16 status_code,
+		       int allow_reuse, int *sta_removed)
 {
 	int ret;
 
@@ -775,8 +818,11 @@
 	switch (sta->sae->state) {
 	case SAE_NOTHING:
 		if (auth_transaction == 1) {
+			if (sta->sae->tmp)
+				sta->sae->tmp->h2e = status_code ==
+					WLAN_STATUS_SAE_HASH_TO_ELEMENT;
 			ret = auth_sae_send_commit(hapd, sta, bssid,
-						   !allow_reuse);
+						   !allow_reuse, status_code);
 			if (ret)
 				return ret;
 			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
@@ -785,14 +831,17 @@
 				return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 			/*
-			 * In mesh case, both Commit and Confirm can be sent
-			 * immediately. In infrastructure BSS, only a single
-			 * Authentication frame (Commit) is expected from the AP
-			 * here and the second one (Confirm) will be sent once
-			 * the STA has sent its second Authentication frame
-			 * (Confirm).
+			 * In mesh case, both Commit and Confirm are sent
+			 * immediately. In infrastructure BSS, by default, only
+			 * a single Authentication frame (Commit) is expected
+			 * from the AP here and the second one (Confirm) will
+			 * be sent once the STA has sent its second
+			 * Authentication frame (Confirm). This behavior can be
+			 * overridden with explicit configuration so that the
+			 * infrastructure BSS case sends both frames together.
 			 */
-			if (hapd->conf->mesh & MESH_ENABLED) {
+			if ((hapd->conf->mesh & MESH_ENABLED) ||
+			    hapd->conf->sae_confirm_immediate) {
 				/*
 				 * Send both Commit and Confirm immediately
 				 * based on SAE finite state machine
@@ -843,7 +892,8 @@
 				return WLAN_STATUS_SUCCESS;
 			sta->sae->sync++;
 
-			ret = auth_sae_send_commit(hapd, sta, bssid, 0);
+			ret = auth_sae_send_commit(hapd, sta, bssid, 0,
+						   status_code);
 			if (ret)
 				return ret;
 
@@ -866,7 +916,7 @@
 			 * additional events.
 			 */
 			return sae_sm_step(hapd, sta, bssid, auth_transaction,
-					   0, sta_removed);
+					   WLAN_STATUS_SUCCESS, 0, sta_removed);
 		}
 		break;
 	case SAE_CONFIRMED:
@@ -876,7 +926,8 @@
 				return WLAN_STATUS_SUCCESS;
 			sta->sae->sync++;
 
-			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1,
+						   status_code);
 			if (ret)
 				return ret;
 
@@ -904,7 +955,8 @@
 			*sta_removed = 1;
 		} else if (auth_transaction == 1) {
 			wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
-			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1,
+						   status_code);
 			if (ret)
 				return ret;
 			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
@@ -974,6 +1026,64 @@
 }
 
 
+static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
+{
+	return (hapd->conf->sae_pwe == 0 &&
+		status_code == WLAN_STATUS_SUCCESS) ||
+		(hapd->conf->sae_pwe == 1 &&
+		 status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) ||
+		(hapd->conf->sae_pwe == 2 &&
+		 (status_code == WLAN_STATUS_SUCCESS ||
+		  status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT));
+}
+
+
+static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
+{
+	int *groups = hapd->conf->sae_groups;
+	int default_groups[] = { 19, 0 };
+	int i;
+
+	if (!groups)
+		groups = default_groups;
+
+	for (i = 0; groups[i] > 0; i++) {
+		if (groups[i] == group)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int check_sae_rejected_groups(struct hostapd_data *hapd,
+				     const struct wpabuf *groups)
+{
+	size_t i, count;
+	const u8 *pos;
+
+	if (!groups)
+		return 0;
+
+	pos = wpabuf_head(groups);
+	count = wpabuf_len(groups) / 2;
+	for (i = 0; i < count; i++) {
+		int enabled;
+		u16 group;
+
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		enabled = sae_is_group_enabled(hapd, group);
+		wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
+			   group, enabled ? "enabled" : "disabled");
+		if (enabled)
+			return 1;
+	}
+
+	return 0;
+}
+
+
 static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 			    const struct ieee80211_mgmt *mgmt, size_t len,
 			    u16 auth_transaction, u16 status_code)
@@ -1011,9 +1121,11 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (!sta->sae) {
 		if (auth_transaction != 1 ||
-		    status_code != WLAN_STATUS_SUCCESS) {
-			resp = -1;
-			goto remove_sta;
+		    !sae_status_success(hapd, status_code)) {
+			wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
+				   status_code);
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto reply;
 		}
 		sta->sae = os_zalloc(sizeof(*sta->sae));
 		if (!sta->sae) {
@@ -1038,8 +1150,8 @@
 
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "start SAE authentication (RX commit, status=%u)",
-			       status_code);
+			       "start SAE authentication (RX commit, status=%u (%s))",
+			       status_code, status2str(status_code));
 
 		if ((hapd->conf->mesh & MESH_ENABLED) &&
 		    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
@@ -1078,7 +1190,8 @@
 			 * Authentication frame, and the commit-scalar and
 			 * COMMIT-ELEMENT previously sent.
 			 */
-			resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0);
+			resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0,
+						    status_code);
 			if (resp != WLAN_STATUS_SUCCESS) {
 				wpa_printf(MSG_ERROR,
 					   "SAE: Failed to send commit message");
@@ -1101,7 +1214,7 @@
 			goto remove_sta;
 		}
 
-		if (status_code != WLAN_STATUS_SUCCESS)
+		if (!sae_status_success(hapd, status_code))
 			goto remove_sta;
 
 		if (!(hapd->conf->mesh & MESH_ENABLED) &&
@@ -1134,7 +1247,8 @@
 		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
 					((const u8 *) mgmt) + len -
 					mgmt->u.auth.variable, &token,
-					&token_len, groups);
+					&token_len, groups, status_code ==
+					WLAN_STATUS_SAE_HASH_TO_ELEMENT);
 		if (resp == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1164,6 +1278,13 @@
 		if (resp != WLAN_STATUS_SUCCESS)
 			goto reply;
 
+		if (sta->sae->tmp &&
+		    check_sae_rejected_groups(
+			    hapd, sta->sae->tmp->peer_rejected_groups)) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto reply;
+		}
+
 		if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Request anti-clogging token from "
@@ -1178,12 +1299,12 @@
 		}
 
 		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
-				   allow_reuse, &sta_removed);
+				   status_code, allow_reuse, &sta_removed);
 	} else if (auth_transaction == 2) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "SAE authentication (RX confirm, status=%u)",
-			       status_code);
+			       "SAE authentication (RX confirm, status=%u (%s))",
+			       status_code, status2str(status_code));
 		if (status_code != WLAN_STATUS_SUCCESS)
 			goto remove_sta;
 		if (sta->sae->state >= SAE_CONFIRMED ||
@@ -1219,13 +1340,14 @@
 			}
 			sta->sae->rc = peer_send_confirm;
 		}
-		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0,
-			&sta_removed);
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+				   status_code, 0, &sta_removed);
 	} else {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "unexpected SAE authentication transaction %u (status=%u)",
-			       auth_transaction, status_code);
+			       "unexpected SAE authentication transaction %u (status=%u (%s))",
+			       auth_transaction, status_code,
+			       status2str(status_code));
 		if (status_code != WLAN_STATUS_SUCCESS)
 			goto remove_sta;
 		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
@@ -1280,7 +1402,7 @@
 	if (sta->sae->state != SAE_NOTHING)
 		return -1;
 
-	ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+	ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
 	if (ret)
 		return -1;
 
@@ -1403,12 +1525,10 @@
 		return WLAN_STATUS_AKMP_NOT_VALID;
 	if (res == WPA_ALLOC_FAIL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-#ifdef CONFIG_IEEE80211W
 	if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
 		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
 	if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
 		return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
-#endif /* CONFIG_IEEE80211W */
 	if (res == WPA_INVALID_MDIE)
 		return WLAN_STATUS_INVALID_MDIE;
 	if (res == WPA_INVALID_PMKID)
@@ -1551,6 +1671,8 @@
 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 				  hapd->iface->freq,
 				  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);
 	resp = wpa_res_to_status_code(res);
 	if (resp != WLAN_STATUS_SUCCESS)
@@ -1821,6 +1943,8 @@
 			}
 
 			sta->fils_erp_pmkid_set = 0;
+			wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
+						    sta->fils_erp_pmkid);
 			if (!hapd->conf->disable_pmksa_caching &&
 			    wpa_auth_pmksa_add2(
 				    hapd->wpa_auth, sta->addr,
@@ -2321,8 +2445,11 @@
 		sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
 				WLAN_STA_AUTHORIZED);
 
-		if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0,
-				    NULL, NULL, sta->flags, 0, 0, 0, 0)) {
+		if (hostapd_sta_add(hapd, sta->addr, 0, 0,
+				    sta->supported_rates,
+				    sta->supported_rates_len,
+				    0, NULL, NULL, NULL, 0,
+				    sta->flags, 0, 0, 0, 0)) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_NOTICE,
@@ -2788,6 +2915,123 @@
 	return WLAN_STATUS_SUCCESS;
 }
 
+
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+			 const u8 *rsn_ie, size_t rsn_ie_len,
+			 const u8 *owe_dh, size_t owe_dh_len)
+{
+	struct wpa_ie_data data;
+	int res;
+
+	if (!rsn_ie || rsn_ie_len < 2) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
+			   MAC2STR(peer));
+		return WLAN_STATUS_INVALID_IE;
+	}
+	rsn_ie -= 2;
+	rsn_ie_len += 2;
+
+	res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR
+			   " (res=%d)", MAC2STR(peer), res);
+		wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len);
+		return wpa_res_to_status_code(res);
+	}
+	if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Unexpected key mgmt 0x%x from " MACSTR,
+			   (unsigned int) data.key_mgmt, MAC2STR(peer));
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	}
+	if (!owe_dh) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: No Diffie-Hellman Parameter element from "
+			   MACSTR, MAC2STR(peer));
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 owe_process_rsn_ie(struct hostapd_data *hapd,
+		       struct sta_info *sta,
+		       const u8 *rsn_ie, size_t rsn_ie_len,
+		       const u8 *owe_dh, size_t owe_dh_len)
+{
+	u16 status;
+	u8 *owe_buf, ie[256 * 2];
+	size_t ie_len = 0;
+	int res;
+
+	if (!rsn_ie || rsn_ie_len < 2) {
+		wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
+		status = WLAN_STATUS_INVALID_IE;
+		goto end;
+	}
+
+	if (!sta->wpa_sm)
+		sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,	sta->addr,
+						NULL);
+	if (!sta->wpa_sm) {
+		wpa_printf(MSG_WARNING,
+			   "OWE: Failed to initialize WPA state machine");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto end;
+	}
+	rsn_ie -= 2;
+	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);
+	status = wpa_res_to_status_code(res);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto end;
+	status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto end;
+	owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
+						NULL, 0);
+	if (!owe_buf) {
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto end;
+	}
+
+	if (sta->owe_ecdh) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto end;
+		}
+
+		/* OWE Diffie-Hellman Parameter element */
+		*owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+		*owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+							 */
+		WPA_PUT_LE16(owe_buf, sta->owe_group);
+		owe_buf += 2;
+		os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+		owe_buf += wpabuf_len(pub);
+		wpabuf_free(pub);
+		sta->external_dh_updated = 1;
+	}
+	ie_len = owe_buf - ie;
+
+end:
+	wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
+			      MACSTR, status, (unsigned int) ie_len,
+			      MAC2STR(sta->addr));
+	hostapd_drv_update_dh_ie(hapd, sta->addr, status,
+				 status == WLAN_STATUS_SUCCESS ? ie : NULL,
+				 ie_len);
+
+	return status;
+}
+
 #endif /* CONFIG_OWE */
 
 
@@ -2843,10 +3087,6 @@
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 
-		resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation);
-		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
-
 		resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
@@ -2867,6 +3107,15 @@
 			return resp;
 	}
 #endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
+					 elems.he_capabilities,
+					 elems.he_capabilities_len);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
+#endif /* CONFIG_IEEE80211AX */
 
 #ifdef CONFIG_P2P
 	if (elems.p2p) {
@@ -2943,12 +3192,13 @@
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  hapd->iface->freq,
 					  wpa_ie, wpa_ie_len,
+					  elems.rsnxe ? elems.rsnxe - 2 : NULL,
+					  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
 					  elems.mdie, elems.mdie_len,
 					  elems.owe_dh, elems.owe_dh_len);
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
-#ifdef CONFIG_IEEE80211W
 		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
 		    (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
 		    !sta->sa_query_timed_out &&
@@ -2975,7 +3225,6 @@
 			sta->flags |= WLAN_STA_MFP;
 		else
 			sta->flags &= ~WLAN_STA_MFP;
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
@@ -3020,6 +3269,17 @@
 				   MAC2STR(sta->addr), sta->auth_alg);
 			return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
 		}
+
+		if (hapd->conf->sae_pwe == 2 &&
+		    sta->auth_alg == WLAN_AUTH_SAE &&
+		    sta->sae && sta->sae->tmp && !sta->sae->tmp->h2e &&
+		    elems.rsnxe && elems.rsnxe_len >= 1 &&
+		    (elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
+			wpa_printf(MSG_INFO, "SAE: " MACSTR
+				   " indicates support for SAE H2E, but did not use it",
+				   MAC2STR(sta->addr));
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
 #endif /* CONFIG_SAE */
 
 #ifdef CONFIG_OWE
@@ -3113,7 +3373,8 @@
 		sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
 						 elems.hs20_len - 4);
 		release = ((elems.hs20[4] >> 4) & 0x0f) + 1;
-		if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) {
+		if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
+		    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 			wpa_printf(MSG_DEBUG,
 				   "HS 2.0: PMF not negotiated by release %d station "
 				   MACSTR, release, MAC2STR(sta->addr));
@@ -3229,6 +3490,7 @@
 {
 	struct ieee80211_ht_capabilities ht_cap;
 	struct ieee80211_vht_capabilities vht_cap;
+	struct ieee80211_he_capabilities he_cap;
 	int set = 1;
 
 	/*
@@ -3281,6 +3543,12 @@
 	if (sta->flags & WLAN_STA_VHT)
 		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
 #endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	if (sta->flags & WLAN_STA_HE) {
+		hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
+				     sta->he_capab_len);
+	}
+#endif /* CONFIG_IEEE80211AX */
 
 	/*
 	 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
@@ -3292,6 +3560,8 @@
 			    sta->listen_interval,
 			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
 			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+			    sta->flags & WLAN_STA_HE ? &he_cap : NULL,
+			    sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
 			    set)) {
@@ -3329,6 +3599,8 @@
 #ifdef CONFIG_FILS
 	if (sta && sta->fils_hlp_resp)
 		buflen += wpabuf_len(sta->fils_hlp_resp);
+	if (sta)
+		buflen += 150;
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_OWE
 	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
@@ -3390,6 +3662,15 @@
 		}
 	}
 #endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_FILS
+	if (sta && status_code == WLAN_STATUS_SUCCESS &&
+	    (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK))
+		p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
+						   buf + buflen - p,
+						   ies, ies_len);
+#endif /* CONFIG_FILS */
 
 #ifdef CONFIG_OWE
 	if (sta && status_code == WLAN_STATUS_SUCCESS &&
@@ -3399,10 +3680,8 @@
 						  ies, ies_len);
 #endif /* CONFIG_OWE */
 
-#ifdef CONFIG_IEEE80211W
 	if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
 		p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_IEEE80211N
 	p = hostapd_eid_ht_capabilities(hapd, p);
@@ -3410,7 +3689,8 @@
 #endif /* CONFIG_IEEE80211N */
 
 #ifdef CONFIG_IEEE80211AC
-	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+	    !is_6ghz_op_class(hapd->iconf->op_class)) {
 		u32 nsts = 0, sta_nsts;
 
 		if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
@@ -3432,6 +3712,15 @@
 	}
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+		p = hostapd_eid_he_operation(hapd, p);
+		p = hostapd_eid_spatial_reuse(hapd, p);
+		p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	p = hostapd_eid_ext_capab(hapd, p);
 	p = hostapd_eid_bss_max_idle_period(hapd, p);
 	if (sta && sta->qos_map_enabled)
@@ -3445,6 +3734,8 @@
 	}
 #endif /* CONFIG_FST */
 
+	p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p);
+
 #ifdef CONFIG_OWE
 	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
 	    sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
@@ -3608,6 +3899,12 @@
 		return owe_buf;
 	}
 
+	if (sta->owe_pmk && sta->external_dh_updated) {
+		wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK");
+		*reason = WLAN_STATUS_SUCCESS;
+		return owe_buf;
+	}
+
 	*reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
 	if (*reason != WLAN_STATUS_SUCCESS)
 		return NULL;
@@ -3985,7 +4282,6 @@
 	 */
 	sta->flags |= WLAN_STA_ASSOC_REQ_OK;
 
-#ifdef CONFIG_IEEE80211W
 	if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
 		wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
 			   "SA Query procedure", reassoc ? "re" : "");
@@ -3996,7 +4292,6 @@
 		 * trying to associate.
 		 */
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	/* Make sure that the previously registered inactivity timer will not
 	 * remove the STA immediately. */
@@ -4221,13 +4516,11 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
 static int robust_action_frame(u8 category)
 {
 	return category != WLAN_ACTION_PUBLIC &&
 		category != WLAN_ACTION_HT;
 }
-#endif /* CONFIG_IEEE80211W */
 
 
 static int handle_action(struct hostapd_data *hapd,
@@ -4261,7 +4554,6 @@
 		return 0;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (sta && (sta->flags & WLAN_STA_MFP) &&
 	    !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
 	    robust_action_frame(mgmt->u.action.category)) {
@@ -4271,7 +4563,6 @@
 			       "an MFP STA");
 		return 0;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	if (sta) {
 		u16 fc = le_to_host16(mgmt->frame_control);
@@ -4305,11 +4596,9 @@
 	case WLAN_ACTION_WMM:
 		hostapd_wmm_action(hapd, mgmt, len);
 		return 1;
-#ifdef CONFIG_IEEE80211W
 	case WLAN_ACTION_SA_QUERY:
 		ieee802_11_sa_query_action(hapd, mgmt, len);
 		return 1;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM_AP
 	case WLAN_ACTION_WNM:
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
@@ -4709,9 +4998,7 @@
 	else
 		mlme_associate_indication(hapd, sta);
 
-#ifdef CONFIG_IEEE80211W
 	sta->sa_query_timed_out = 0;
-#endif /* CONFIG_IEEE80211W */
 
 	if (sta->eapol_sm == NULL) {
 		/*
@@ -5104,8 +5391,10 @@
 
 	wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
 		   MACSTR, MAC2STR(src));
-	if (is_multicast_ether_addr(src)) {
-		/* Broadcast bit set in SA?! Ignore the frame silently. */
+	if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
+	    os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) {
+		/* Broadcast bit set in SA or unexpected SA?! Ignore the frame
+		 * silently. */
 		return;
 	}
 
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index db7badc..f592da5 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -18,6 +18,7 @@
 struct ieee80211_mgmt;
 struct vlan_description;
 struct hostapd_sta_wpa_psk_short;
+enum ieee80211_op_mode;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 		    struct hostapd_frame_info *fi);
@@ -57,9 +58,11 @@
 u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+			  enum ieee80211_op_mode opmode);
 u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
 
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -70,6 +73,10 @@
 void hostapd_get_vht_capab(struct hostapd_data *hapd,
 			   struct ieee80211_vht_capabilities *vht_cap,
 			   struct ieee80211_vht_capabilities *neg_vht_cap);
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+			  const struct ieee80211_he_capabilities *he_cap,
+			  struct ieee80211_he_capabilities *neg_he_cap,
+			  size_t he_capab_len);
 int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab);
@@ -85,6 +92,9 @@
 		      const u8 *vht_oper);
 u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_opmode);
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      enum ieee80211_op_mode opmode, const u8 *he_capab,
+		      size_t he_capab_len);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 		       const u8 *buf, size_t len, int ack);
 void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -153,6 +163,12 @@
 u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
 			   const u8 *owe_dh, u8 owe_dh_len,
 			   u8 *owe_buf, size_t owe_buf_len, u16 *reason);
+u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *rsn_ie, size_t rsn_ie_len,
+		       const u8 *owe_dh, size_t owe_dh_len);
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+			 const u8 *rsn_ie, size_t rsn_ie_len,
+			 const u8 *owe_dh, size_t owe_dh_len);
 void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
 void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
 void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
@@ -176,5 +192,6 @@
 		      int ap_seg1_idx, int *bandwidth, int *seg1_idx);
 
 void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
+u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 0721358..abd3940 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -1,6 +1,7 @@
 /*
  * hostapd / IEEE 802.11ax HE
  * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019 John Crispin <john@phrozen.org>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,40 +11,151 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "beacon.h"
+#include "sta_info.h"
 #include "ieee802_11.h"
 #include "dfs.h"
 
-u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
+static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+	u8 sz = 0, ru;
+
+	if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+	     HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+		return 0;
+
+	ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
+		HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
+	while (ru) {
+		if (ru & 0x1)
+			sz++;
+		ru >>= 1;
+	}
+
+	sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
+	sz = (sz * 6) + 7;
+	if (sz % 8)
+		sz += 8;
+	sz /= 8;
+
+	return sz;
+}
+
+
+static u8 ieee80211_he_mcs_set_size(const u8 *phy_cap_info)
+{
+	u8 sz = 4;
+
+	if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	    HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
+		sz += 4;
+	if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	    HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
+		sz += 4;
+
+	return sz;
+}
+
+
+static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len)
 {
 	struct ieee80211_he_capabilities *cap;
-	u8 *pos = eid;
+	size_t cap_len;
 
-	if (!hapd->iface->current_mode)
+	cap = (struct ieee80211_he_capabilities *) buf;
+	cap_len = sizeof(*cap) - sizeof(cap->optional);
+	if (len < cap_len)
+		return 1;
+
+	cap_len += ieee80211_he_mcs_set_size(cap->he_phy_capab_info);
+	if (len < cap_len)
+		return 1;
+
+	cap_len += ieee80211_he_ppet_size(buf[cap_len], cap->he_phy_capab_info);
+
+	return len != cap_len;
+}
+
+
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+			  enum ieee80211_op_mode opmode)
+{
+	struct ieee80211_he_capabilities *cap;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+	u8 *pos = eid;
+	u8 ie_size = 0, mcs_nss_size = 4, ppet_size = 0;
+
+	if (!mode)
 		return eid;
 
+	ie_size = sizeof(*cap) - sizeof(cap->optional);
+	ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
+					   mode->he_capab[opmode].phy_cap);
+
+	switch (hapd->iface->conf->he_oper_chwidth) {
+	case CHANWIDTH_80P80MHZ:
+		he_oper_chwidth |=
+			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+		mcs_nss_size += 4;
+		/* fall through */
+	case CHANWIDTH_160MHZ:
+		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+		mcs_nss_size += 4;
+		/* fall through */
+	case CHANWIDTH_80MHZ:
+	case CHANWIDTH_USE_HT:
+		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+		break;
+	}
+
+	ie_size += mcs_nss_size + ppet_size;
+
 	*pos++ = WLAN_EID_EXTENSION;
-	*pos++ = 1 + sizeof(struct ieee80211_he_capabilities);
+	*pos++ = 1 + ie_size;
 	*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
 
 	cap = (struct ieee80211_he_capabilities *) pos;
 	os_memset(cap, 0, sizeof(*cap));
 
+	os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
+		  HE_MAX_MAC_CAPAB_SIZE);
+	os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
+		  HE_MAX_PHY_CAPAB_SIZE);
+	os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
+	if (ppet_size)
+		os_memcpy(&cap->optional[mcs_nss_size],
+			  mode->he_capab[opmode].ppet,  ppet_size);
+
 	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
 			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
+			~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
 
 	if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
 			HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
+			~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
 
 	if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
 		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
 			HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
+			~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
 
-	pos += sizeof(*cap);
+	cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
+		he_oper_chwidth;
+
+	pos += ie_size;
 
 	return pos;
 }
@@ -53,36 +165,63 @@
 {
 	struct ieee80211_he_operation *oper;
 	u8 *pos = eid;
+	int oper_size = 6;
+	u32 params = 0;
 
 	if (!hapd->iface->current_mode)
 		return eid;
 
+	if (is_6ghz_op_class(hapd->iconf->op_class))
+		oper_size += 5;
+
 	*pos++ = WLAN_EID_EXTENSION;
-	*pos++ = 1 + sizeof(struct ieee80211_he_operation);
+	*pos++ = 1 + oper_size;
 	*pos++ = WLAN_EID_EXT_HE_OPERATION;
 
 	oper = (struct ieee80211_he_operation *) pos;
 	os_memset(oper, 0, sizeof(*oper));
 
-	if (hapd->iface->conf->he_op.he_bss_color)
-		oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color;
-
 	if (hapd->iface->conf->he_op.he_default_pe_duration)
-		oper->he_oper_params |=
-			(hapd->iface->conf->he_op.he_default_pe_duration <<
-			 HE_OPERATION_DFLT_PE_DURATION_OFFSET);
+		params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
+			   HE_OPERATION_DFLT_PE_DURATION_OFFSET);
 
 	if (hapd->iface->conf->he_op.he_twt_required)
-		oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED;
+		params |= HE_OPERATION_TWT_REQUIRED;
 
 	if (hapd->iface->conf->he_op.he_rts_threshold)
-		oper->he_oper_params |=
-			(hapd->iface->conf->he_op.he_rts_threshold <<
-			 HE_OPERATION_RTS_THRESHOLD_OFFSET);
+		params |= (hapd->iface->conf->he_op.he_rts_threshold <<
+			   HE_OPERATION_RTS_THRESHOLD_OFFSET);
+
+	if (hapd->iface->conf->he_op.he_bss_color)
+		params |= (hapd->iface->conf->he_op.he_bss_color <<
+			   HE_OPERATION_BSS_COLOR_OFFSET);
+
+	/* HE minimum required basic MCS and NSS for STAs */
+	oper->he_mcs_nss_set =
+		host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
 
 	/* TODO: conditional MaxBSSID Indicator subfield */
 
-	pos += sizeof(*oper);
+	pos += 6; /* skip the fixed part */
+
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+		u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
+
+		if (!seg0)
+			seg0 = hapd->iconf->channel;
+
+		params |= HE_OPERATION_6GHZ_OPER_INFO;
+		*pos++ = hapd->iconf->channel; /* Primary Channel */
+		*pos++ = center_idx_to_bw_6ghz(seg0); /* Control: Channel Width
+						       */
+		/* Channel Center Freq Seg0/Seg0 */
+		*pos++ = seg0;
+		*pos++ = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
+		/* Minimum Rate */
+		*pos++ = 6; /* TODO: what should be set here? */
+	}
+
+	oper->he_oper_params = host_to_le32(params);
 
 	return pos;
 }
@@ -117,3 +256,149 @@
 
 	return pos;
 }
+
+
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_spatial_reuse *spr;
+	u8 *pos = eid, *spr_param;
+	u8 sz = 1;
+
+	if (!hapd->iface->conf->spr.sr_control)
+		return eid;
+
+	if (hapd->iface->conf->spr.sr_control &
+	    SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
+		sz++;
+
+	if (hapd->iface->conf->spr.sr_control &
+	    SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
+		sz += 18;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sz;
+	*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
+
+	spr = (struct ieee80211_spatial_reuse *) pos;
+	os_memset(spr, 0, sizeof(*spr));
+
+	spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
+	pos++;
+	spr_param = spr->params;
+	if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
+		*spr_param++ =
+			hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
+		pos++;
+	}
+	if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
+		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
+		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
+		pos += 18;
+	}
+
+	return pos;
+}
+
+
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+			  const struct ieee80211_he_capabilities *he_cap,
+			  struct ieee80211_he_capabilities *neg_he_cap,
+			  size_t he_capab_len)
+{
+	if (!he_cap)
+		return;
+
+	if (he_capab_len > sizeof(*neg_he_cap))
+		he_capab_len = sizeof(*neg_he_cap);
+	/* TODO: mask out unsupported features */
+
+	os_memcpy(neg_he_cap, he_cap, he_capab_len);
+}
+
+
+static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
+			      enum ieee80211_op_mode opmode)
+{
+	u16 sta_rx_mcs_set, ap_tx_mcs_set;
+	u8 mcs_count = 0;
+	const u16 *ap_mcs_set, *sta_mcs_set;
+	int i;
+
+	if (!hapd->iface->current_mode)
+		return 1;
+	ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
+	sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
+			       sta_he_capab)->optional;
+
+	/*
+	 * Disable HE capabilities for STAs for which there is not even a single
+	 * allowed MCS in any supported number of streams, i.e., STA is
+	 * advertising 3 (not supported) as HE MCS rates for all supported
+	 * band/stream cases.
+	 */
+	switch (hapd->iface->conf->he_oper_chwidth) {
+	case CHANWIDTH_80P80MHZ:
+		mcs_count = 3;
+		break;
+	case CHANWIDTH_160MHZ:
+		mcs_count = 2;
+		break;
+	default:
+		mcs_count = 1;
+		break;
+	}
+
+	for (i = 0; i < mcs_count; i++) {
+		int j;
+
+		/* AP Tx MCS map vs. STA Rx MCS map */
+		sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
+		ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
+					     &ap_mcs_set[(i * 2) + 1]);
+
+		for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
+			if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
+				continue;
+
+			if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
+				continue;
+
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching HE MCS found between AP TX and STA RX");
+
+	return 0;
+}
+
+
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      enum ieee80211_op_mode opmode, const u8 *he_capab,
+		      size_t he_capab_len)
+{
+	if (!he_capab || !hapd->iconf->ieee80211ax ||
+	    !check_valid_he_mcs(hapd, he_capab, opmode) ||
+	    ieee80211_invalid_he_cap_size(he_capab, he_capab_len) ||
+	    he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
+		sta->flags &= ~WLAN_STA_HE;
+		os_free(sta->he_capab);
+		sta->he_capab = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (!sta->he_capab) {
+		sta->he_capab =
+			os_zalloc(sizeof(struct ieee80211_he_capabilities));
+		if (!sta->he_capab)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_HE;
+	os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
+	os_memcpy(sta->he_capab, he_capab, he_capab_len);
+	sta->he_capab_len = he_capab_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 214855d..6db9365 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -27,7 +27,7 @@
 	u8 *pos = eid;
 
 	if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
-	    hapd->conf->disable_11n)
+	    hapd->conf->disable_11n || is_6ghz_op_class(hapd->iconf->op_class))
 		return eid;
 
 	*pos++ = WLAN_EID_HT_CAP;
@@ -84,7 +84,8 @@
 	struct ieee80211_ht_operation *oper;
 	u8 *pos = eid;
 
-	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+	    is_6ghz_op_class(hapd->iconf->op_class))
 		return eid;
 
 	*pos++ = WLAN_EID_HT_OPERATION;
@@ -113,7 +114,8 @@
 	u8 sec_ch;
 
 	if (!hapd->cs_freq_params.channel ||
-	    !hapd->cs_freq_params.sec_channel_offset)
+	    !hapd->cs_freq_params.sec_channel_offset ||
+	    is_6ghz_op_class(hapd->iconf->op_class))
 		return eid;
 
 	if (hapd->cs_freq_params.sec_channel_offset == -1)
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 707381f..1e1cc38 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -19,8 +19,6 @@
 #include "ieee802_11.h"
 
 
-#ifdef CONFIG_IEEE80211W
-
 u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
 				     struct sta_info *sta, u8 *eid)
 {
@@ -304,8 +302,6 @@
 	ap_sta_stop_sa_query(hapd, sta);
 }
 
-#endif /* CONFIG_IEEE80211W */
-
 
 static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
 {
@@ -404,14 +400,22 @@
 	u8 *pos = eid;
 	u8 len = 0, i;
 
-	if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
+	if (hapd->conf->qos_map_set_len ||
+	    (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)))
 		len = 5;
-	if (len < 4 && hapd->conf->interworking)
+	if (len < 4 &&
+	    (hapd->conf->time_advertisement == 2 || hapd->conf->interworking))
 		len = 4;
-	if (len < 3 && hapd->conf->wnm_sleep_mode)
+	if (len < 3 &&
+	    (hapd->conf->wnm_sleep_mode || hapd->conf->bss_transition))
 		len = 3;
-	if (len < 1 && hapd->iconf->obss_interval)
+	if (len < 1 &&
+	    (hapd->iconf->obss_interval ||
+	     (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)))
 		len = 1;
+	if (len < 2 &&
+	    (hapd->conf->proxy_arp || hapd->conf->coloc_intf_reporting))
+		len = 2;
 	if (len < 7 && hapd->conf->ssid.utf8_ssid)
 		len = 7;
 	if (len < 9 &&
@@ -1000,3 +1004,23 @@
 	return 0;
 }
 #endif /* CONFIG_OCV */
+
+
+u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	u8 *pos = eid;
+
+	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	    !wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) ||
+	    (hapd->conf->sae_pwe != 1 && hapd->conf->sae_pwe != 2) ||
+	    len < 3)
+		return pos;
+
+	*pos++ = WLAN_EID_RSNX;
+	*pos++ = 1;
+	/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
+	 * used for now */
+	*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+
+	return pos;
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 54ee080..f50f142 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -26,7 +26,7 @@
 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
 	u8 *pos = eid;
 
-	if (!mode)
+	if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
 		return eid;
 
 	if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
@@ -76,6 +76,9 @@
 	struct ieee80211_vht_operation *oper;
 	u8 *pos = eid;
 
+	if (is_6ghz_op_class(hapd->iconf->op_class))
+		return eid;
+
 	*pos++ = WLAN_EID_VHT_OPERATION;
 	*pos++ = sizeof(*oper);
 
@@ -242,7 +245,7 @@
 		return eid;
 
 	switch (iface->conf->vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
+	case CHANWIDTH_USE_HT:
 		if (iconf->secondary_channel == 0) {
 			/* Max Transmit Power count = 0 (20 MHz) */
 			tx_pwr_count = 0;
@@ -251,12 +254,12 @@
 			tx_pwr_count = 1;
 		}
 		break;
-	case VHT_CHANWIDTH_80MHZ:
+	case CHANWIDTH_80MHZ:
 		/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
 		tx_pwr_count = 2;
 		break;
-	case VHT_CHANWIDTH_80P80MHZ:
-	case VHT_CHANWIDTH_160MHZ:
+	case CHANWIDTH_80P80MHZ:
+	case CHANWIDTH_160MHZ:
 		/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
 		tx_pwr_count = 3;
 		break;
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 870329a..d081031 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -7,6 +7,9 @@
  */
 
 #include "utils/includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "utils/common.h"
 #include "utils/eloop.h"
@@ -34,6 +37,7 @@
 /* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
 #include "ieee802_11.h"
 #include "ieee802_1x.h"
+#include "wpa_auth_kay.h"
 
 
 #ifdef CONFIG_HS20
@@ -54,15 +58,18 @@
 
 	len = sizeof(*xhdr) + datalen;
 	buf = os_zalloc(len);
-	if (buf == NULL) {
-		wpa_printf(MSG_ERROR, "malloc() failed for "
-			   "ieee802_1x_send(len=%lu)",
-			   (unsigned long) len);
+	if (!buf) {
+		wpa_printf(MSG_ERROR, "malloc() failed for %s(len=%lu)",
+			   __func__, (unsigned long) len);
 		return;
 	}
 
 	xhdr = (struct ieee802_1x_hdr *) buf;
 	xhdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (xhdr->version > 2 && hapd->conf->macsec_policy == 0)
+		xhdr->version = 2;
+#endif /* CONFIG_MACSEC */
 	xhdr->type = type;
 	xhdr->length = host_to_be16(datalen);
 
@@ -144,12 +151,12 @@
 	size_t len, ekey_len;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-	if (sm == NULL)
+	if (!sm)
 		return;
 
 	len = sizeof(*key) + key_len;
 	buf = os_zalloc(sizeof(*hdr) + len);
-	if (buf == NULL)
+	if (!buf)
 		return;
 
 	hdr = (struct ieee802_1x_hdr *) buf;
@@ -157,6 +164,21 @@
 	key->type = EAPOL_KEY_TYPE_RC4;
 	WPA_PUT_BE16(key->key_length, key_len);
 	wpa_get_ntp_timestamp(key->replay_counter);
+	if (os_memcmp(key->replay_counter,
+		      hapd->last_1x_eapol_key_replay_counter,
+		      IEEE8021X_REPLAY_COUNTER_LEN) <= 0) {
+		/* NTP timestamp did not increment from last EAPOL-Key frame;
+		 * use previously used value + 1 instead. */
+		inc_byte_array(hapd->last_1x_eapol_key_replay_counter,
+			       IEEE8021X_REPLAY_COUNTER_LEN);
+		os_memcpy(key->replay_counter,
+			  hapd->last_1x_eapol_key_replay_counter,
+			  IEEE8021X_REPLAY_COUNTER_LEN);
+	} else {
+		os_memcpy(hapd->last_1x_eapol_key_replay_counter,
+			  key->replay_counter,
+			  IEEE8021X_REPLAY_COUNTER_LEN);
+	}
 
 	if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
 		wpa_printf(MSG_ERROR, "Could not get random numbers");
@@ -175,16 +197,16 @@
 
 	/* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and
 	 * MSK[32..63] is used to sign the message. */
-	if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) {
-		wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting "
-			   "and signing EAPOL-Key");
+	if (!sm->eap_if->eapKeyData || sm->eap_if->eapKeyDataLen < 64) {
+		wpa_printf(MSG_ERROR,
+			   "No eapKeyData available for encrypting and signing EAPOL-Key");
 		os_free(buf);
 		return;
 	}
 	os_memcpy((u8 *) (key + 1), key_data, key_len);
 	ekey_len = sizeof(key->key_iv) + 32;
 	ekey = os_malloc(ekey_len);
-	if (ekey == NULL) {
+	if (!ekey) {
 		wpa_printf(MSG_ERROR, "Could not encrypt key");
 		os_free(buf);
 		return;
@@ -197,6 +219,10 @@
 	/* This header is needed here for HMAC-MD5, but it will be regenerated
 	 * in ieee802_1x_send() */
 	hdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (hdr->version > 2)
+		hdr->version = 2;
+#endif /* CONFIG_MACSEC */
 	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
 	hdr->length = host_to_be16(len);
 	hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
@@ -217,7 +243,7 @@
 	struct eapol_authenticator *eapol = hapd->eapol_auth;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-	if (sm == NULL || !sm->eap_if->eapKeyData)
+	if (!sm || !sm->eap_if->eapKeyData)
 		return;
 
 	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
@@ -238,12 +264,13 @@
 
 	if (hapd->conf->individual_wep_key_len > 0) {
 		u8 *ikey;
+
 		ikey = os_malloc(hapd->conf->individual_wep_key_len);
-		if (ikey == NULL ||
+		if (!ikey ||
 		    random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
 		{
-			wpa_printf(MSG_ERROR, "Could not generate random "
-				   "individual WEP key.");
+			wpa_printf(MSG_ERROR,
+				   "Could not generate random individual WEP key");
 			os_free(ikey);
 			return;
 		}
@@ -259,8 +286,8 @@
 		if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
 					sta->addr, 0, 1, NULL, 0, ikey,
 					hapd->conf->individual_wep_key_len)) {
-			wpa_printf(MSG_ERROR, "Could not set individual WEP "
-				   "encryption.");
+			wpa_printf(MSG_ERROR,
+				   "Could not set individual WEP encryption");
 		}
 
 		os_free(ikey);
@@ -320,13 +347,13 @@
 
 	eap_erp_update_identity(sm->eap, eap, len);
 	identity = eap_get_identity(sm->eap, &identity_len);
-	if (identity == NULL)
+	if (!identity)
 		return;
 
 	/* Save station identity for future RADIUS packets */
 	os_free(sm->identity);
 	sm->identity = (u8 *) dup_binstr(identity, identity_len);
-	if (sm->identity == NULL) {
+	if (!sm->identity) {
 		sm->identity_len = 0;
 		return;
 	}
@@ -381,7 +408,6 @@
 		return -1;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		suite = wpa_cipher_to_suite(WPA_PROTO_RSN,
 					    hapd->conf->group_mgmt_cipher);
@@ -394,7 +420,6 @@
 			return -1;
 		}
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	return 0;
 }
@@ -581,8 +606,7 @@
 		if (!radius_msg_add_attr(msg, attr->type,
 					 wpabuf_head(attr->val),
 					 wpabuf_len(attr->val))) {
-			wpa_printf(MSG_ERROR, "Could not add RADIUS "
-				   "attribute");
+			wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
 			return -1;
 		}
 	}
@@ -591,6 +615,63 @@
 }
 
 
+int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
+			   struct radius_msg *msg, int acct)
+{
+#ifdef CONFIG_SQLITE
+	const char *attrtxt;
+	char addrtxt[3 * ETH_ALEN];
+	char *sql;
+	sqlite3_stmt *stmt = NULL;
+
+	if (!hapd->rad_attr_db)
+		return 0;
+
+	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(sta->addr));
+
+	sql = "SELECT attr FROM radius_attributes WHERE sta=? AND (reqtype=? OR reqtype IS NULL);";
+	if (sqlite3_prepare_v2(hapd->rad_attr_db, sql, os_strlen(sql), &stmt,
+			       NULL) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "DB: Failed to prepare SQL statement: %s",
+			   sqlite3_errmsg(hapd->rad_attr_db));
+		return -1;
+	}
+	sqlite3_bind_text(stmt, 1, addrtxt, os_strlen(addrtxt), SQLITE_STATIC);
+	sqlite3_bind_text(stmt, 2, acct ? "acct" : "auth", 4, SQLITE_STATIC);
+	while (sqlite3_step(stmt) == SQLITE_ROW) {
+		struct hostapd_radius_attr *attr;
+		struct radius_attr_hdr *hdr;
+
+		attrtxt = (const char *) sqlite3_column_text(stmt, 0);
+		attr = hostapd_parse_radius_attr(attrtxt);
+		if (!attr) {
+			wpa_printf(MSG_ERROR,
+				   "Skipping invalid attribute from SQL: %s",
+				   attrtxt);
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "Adding RADIUS attribute from SQL: %s",
+			   attrtxt);
+		hdr = radius_msg_add_attr(msg, attr->type,
+					  wpabuf_head(attr->val),
+					  wpabuf_len(attr->val));
+		hostapd_config_free_radius_attr(attr);
+		if (!hdr) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add RADIUS attribute from SQL");
+			continue;
+		}
+	}
+
+	sqlite3_reset(stmt);
+	sqlite3_clear_bindings(stmt);
+	sqlite3_finalize(stmt);
+#endif /* CONFIG_SQLITE */
+
+	return 0;
+}
+
+
 void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
 				   struct sta_info *sta,
 				   const u8 *eap, size_t len)
@@ -598,18 +679,17 @@
 	struct radius_msg *msg;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-	if (sm == NULL)
+	if (!sm)
 		return;
 
 	ieee802_1x_learn_identity(hapd, sm, eap, len);
 
-	wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
-		   "packet");
+	wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS packet");
 
 	sm->radius_identifier = radius_client_get_id(hapd->radius);
 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
 			     sm->radius_identifier);
-	if (msg == NULL) {
+	if (!msg) {
 		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
 		return;
 	}
@@ -630,6 +710,9 @@
 				   msg) < 0)
 		goto fail;
 
+	if (sta && add_sqlite_radius_attr(hapd, sta, msg, 0) < 0)
+		goto fail;
+
 	/* TODO: should probably check MTU from driver config; 2304 is max for
 	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
 	 */
@@ -653,12 +736,12 @@
 		int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
 					       RADIUS_ATTR_STATE);
 		if (res < 0) {
-			wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge");
+			wpa_printf(MSG_INFO,
+				   "Could not copy State attribute from previous Access-Challenge");
 			goto fail;
 		}
-		if (res > 0) {
+		if (res > 0)
 			wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
-		}
 	}
 
 	if (hapd->conf->radius_request_cui) {
@@ -687,8 +770,8 @@
 		if (!radius_msg_add_wfa(
 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
 			    &ver, 1)) {
-			wpa_printf(MSG_ERROR, "Could not add HS 2.0 AP "
-				   "version");
+			wpa_printf(MSG_ERROR,
+				   "Could not add HS 2.0 AP version");
 			goto fail;
 		}
 
@@ -696,6 +779,7 @@
 			const u8 *pos;
 			u8 buf[3];
 			u16 id;
+
 			pos = wpabuf_head_u8(sta->hs20_ie);
 			buf[0] = (*pos) >> 4;
 			if (((*pos) & HS20_PPS_MO_ID_PRESENT) &&
@@ -708,8 +792,8 @@
 				    msg,
 				    RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
 				    buf, sizeof(buf))) {
-				wpa_printf(MSG_ERROR, "Could not add HS 2.0 "
-					   "STA version");
+				wpa_printf(MSG_ERROR,
+					   "Could not add HS 2.0 STA version");
 				goto fail;
 			}
 		}
@@ -768,13 +852,14 @@
 {
 	u8 type, *data;
 	struct eapol_state_machine *sm = sta->eapol_sm;
-	if (sm == NULL)
+
+	if (!sm)
 		return;
 
 	data = (u8 *) (eap + 1);
 
 	if (len < sizeof(*eap) + 1) {
-		wpa_printf(MSG_INFO, "handle_eap_response: too short response data");
+		wpa_printf(MSG_INFO, "%s: too short response data", __func__);
 		return;
 	}
 
@@ -802,12 +887,11 @@
 	u8 type, *data;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-	if (sm == NULL)
+	if (!sm)
 		return;
 
 	if (len < sizeof(*eap) + 1) {
-		wpa_printf(MSG_INFO,
-			   "handle_eap_initiate: too short response data");
+		wpa_printf(MSG_INFO, "%s: too short response data", __func__);
 		return;
 	}
 
@@ -815,8 +899,8 @@
 	type = data[0];
 
 	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
-		       HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
-		       "id=%d len=%d) from STA: EAP Initiate type %u",
+		       HOSTAPD_LEVEL_DEBUG,
+		       "received EAP packet (code=%d id=%d len=%d) from STA: EAP Initiate type %u",
 		       eap->code, eap->identifier, be_to_host16(eap->length),
 		       type);
 
@@ -827,6 +911,29 @@
 }
 
 
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_code_str(u8 code)
+{
+	switch (code) {
+	case EAP_CODE_REQUEST:
+		return "request";
+	case EAP_CODE_RESPONSE:
+		return "response";
+	case EAP_CODE_SUCCESS:
+		return "success";
+	case EAP_CODE_FAILURE:
+		return "failure";
+	case EAP_CODE_INITIATE:
+		return "initiate";
+	case EAP_CODE_FINISH:
+		return "finish";
+	default:
+		return "unknown";
+	}
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
 /* Process incoming EAP packet from Supplicant */
 static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
 		       u8 *buf, size_t len)
@@ -842,44 +949,29 @@
 	eap = (struct eap_hdr *) buf;
 
 	eap_len = be_to_host16(eap->length);
-	wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d",
-		   eap->code, eap->identifier, eap_len);
+	wpa_printf(MSG_DEBUG, "EAP: code=%d (%s) identifier=%d length=%d",
+		   eap->code, eap_code_str(eap->code), eap->identifier,
+		   eap_len);
 	if (eap_len < sizeof(*eap)) {
 		wpa_printf(MSG_DEBUG, "   Invalid EAP length");
 		return;
 	} else if (eap_len > len) {
-		wpa_printf(MSG_DEBUG, "   Too short frame to contain this EAP "
-			   "packet");
+		wpa_printf(MSG_DEBUG,
+			   "   Too short frame to contain this EAP packet");
 		return;
 	} else if (eap_len < len) {
-		wpa_printf(MSG_DEBUG, "   Ignoring %lu extra bytes after EAP "
-			   "packet", (unsigned long) len - eap_len);
+		wpa_printf(MSG_DEBUG,
+			   "   Ignoring %lu extra bytes after EAP packet",
+			   (unsigned long) len - eap_len);
 	}
 
 	switch (eap->code) {
-	case EAP_CODE_REQUEST:
-		wpa_printf(MSG_DEBUG, " (request)");
-		return;
 	case EAP_CODE_RESPONSE:
-		wpa_printf(MSG_DEBUG, " (response)");
 		handle_eap_response(hapd, sta, eap, eap_len);
 		break;
-	case EAP_CODE_SUCCESS:
-		wpa_printf(MSG_DEBUG, " (success)");
-		return;
-	case EAP_CODE_FAILURE:
-		wpa_printf(MSG_DEBUG, " (failure)");
-		return;
 	case EAP_CODE_INITIATE:
-		wpa_printf(MSG_DEBUG, " (initiate)");
 		handle_eap_initiate(hapd, sta, eap, eap_len);
 		break;
-	case EAP_CODE_FINISH:
-		wpa_printf(MSG_DEBUG, " (finish)");
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, " (unknown code)");
-		return;
 	}
 }
 
@@ -888,6 +980,7 @@
 ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int flags = 0;
+
 	if (sta->flags & WLAN_STA_PREAUTH)
 		flags |= EAPOL_SM_PREAUTH;
 	if (sta->wpa_sm) {
@@ -952,8 +1045,8 @@
 	sta = ap_get_sta(hapd, sa);
 	if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
 		     !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
-		wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
-			   "associated/Pre-authenticating STA");
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X data frame from not associated/Pre-authenticating STA");
 
 		if (sta && (sta->flags & WLAN_STA_AUTH)) {
 			wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR
@@ -975,14 +1068,15 @@
 		   hdr->version, hdr->type, datalen);
 
 	if (len - sizeof(*hdr) < datalen) {
-		wpa_printf(MSG_INFO, "   frame too short for this IEEE 802.1X packet");
+		wpa_printf(MSG_INFO,
+			   "   frame too short for this IEEE 802.1X packet");
 		if (sta->eapol_sm)
 			sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
 		return;
 	}
 	if (len - sizeof(*hdr) > datalen) {
-		wpa_printf(MSG_DEBUG, "   ignoring %lu extra octets after "
-			   "IEEE 802.1X packet",
+		wpa_printf(MSG_DEBUG,
+			   "   ignoring %lu extra octets after IEEE 802.1X packet",
 			   (unsigned long) len - sizeof(*hdr) - datalen);
 	}
 
@@ -1003,8 +1097,8 @@
 
 	if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
 	    !(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");
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used");
 		return;
 	}
 
@@ -1012,8 +1106,8 @@
 	if (key_mgmt != -1 &&
 	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
 	     key_mgmt == WPA_KEY_MGMT_DPP)) {
-		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
-			   "STA is using PSK");
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Ignore EAPOL message - STA is using PSK");
 		return;
 	}
 
@@ -1036,9 +1130,8 @@
 				 * skipped if the STA is known to support WPS
 				 * 2.0.
 				 */
-				wpa_printf(MSG_DEBUG, "WPS: Do not start "
-					   "EAPOL until EAPOL-Start is "
-					   "received");
+				wpa_printf(MSG_DEBUG,
+					   "WPS: Do not start EAPOL until EAPOL-Start is received");
 				sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
 			}
 		}
@@ -1061,15 +1154,14 @@
 
 	case IEEE802_1X_TYPE_EAPOL_START:
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
-			       HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start "
-			       "from STA");
+			       HOSTAPD_LEVEL_DEBUG,
+			       "received EAPOL-Start from STA");
 		sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
 		pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 		if (pmksa) {
 			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
-				       HOSTAPD_LEVEL_DEBUG, "cached PMKSA "
-				       "available - ignore it since "
-				       "STA sent EAPOL-Start");
+				       HOSTAPD_LEVEL_DEBUG,
+				       "cached PMKSA available - ignore it since STA sent EAPOL-Start");
 			wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
 		}
 		sta->eapol_sm->eapolStart = TRUE;
@@ -1080,8 +1172,8 @@
 
 	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
-			       HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff "
-			       "from STA");
+			       HOSTAPD_LEVEL_DEBUG,
+			       "received EAPOL-Logoff from STA");
 		sta->acct_terminate_cause =
 			RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
 		accounting_sta_stop(hapd, sta);
@@ -1093,8 +1185,8 @@
 	case IEEE802_1X_TYPE_EAPOL_KEY:
 		wpa_printf(MSG_DEBUG, "   EAPOL-Key");
 		if (!ap_sta_is_authorized(sta)) {
-			wpa_printf(MSG_DEBUG, "   Dropped key data from "
-				   "unauthorized Supplicant");
+			wpa_printf(MSG_DEBUG,
+				   "   Dropped key data from unauthorized Supplicant");
 			break;
 		}
 		break;
@@ -1104,6 +1196,13 @@
 		/* TODO: implement support for this; show data */
 		break;
 
+#ifdef CONFIG_MACSEC
+	case IEEE802_1X_TYPE_EAPOL_MKA:
+		wpa_printf(MSG_EXCESSIVE,
+			   "EAPOL type %d will be handled by MKA", hdr->type);
+		break;
+#endif /* CONFIG_MACSEC */
+
 	default:
 		wpa_printf(MSG_DEBUG, "   unknown IEEE 802.1X packet type");
 		sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
@@ -1143,8 +1242,8 @@
 #endif /* CONFIG_WPS */
 
 	if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
-		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
-			   "802.1X not enabled or forced for WPS");
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS");
 		/*
 		 * Clear any possible EAPOL authenticator state to support
 		 * reassociation change from WPS to PSK.
@@ -1166,11 +1265,11 @@
 		return;
 	}
 
-	if (sta->eapol_sm == NULL) {
+	if (!sta->eapol_sm) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_DEBUG, "start authentication");
 		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
-		if (sta->eapol_sm == NULL) {
+		if (!sta->eapol_sm) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE8021X,
 				       HOSTAPD_LEVEL_INFO,
@@ -1189,8 +1288,8 @@
 		 * initiates the handshake with EAPOL-Start. Only allow the
 		 * wait to be skipped if the STA is known to support WPS 2.0.
 		 */
-		wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until "
-			   "EAPOL-Start is received");
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Do not start EAPOL until EAPOL-Start is received");
 		sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
 	}
 #endif /* CONFIG_WPS */
@@ -1236,6 +1335,7 @@
 		sta->eapol_sm->portValid = TRUE;
 		if (sta->eapol_sm->eap)
 			eap_sm_notify_cached(sta->eapol_sm->eap);
+		wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
 		return;
 	}
 #endif /* CONFIG_FILS */
@@ -1285,7 +1385,7 @@
 		sta->pending_eapol_rx = NULL;
 	}
 
-	if (sm == NULL)
+	if (!sm)
 		return;
 
 	sta->eapol_sm = NULL;
@@ -1310,7 +1410,7 @@
 	struct radius_msg *msg;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-	if (sm == NULL || sm->last_recv_radius == NULL) {
+	if (!sm || !sm->last_recv_radius) {
 		if (sm)
 			sm->eap_if->aaaEapNoReq = TRUE;
 		return;
@@ -1319,21 +1419,21 @@
 	msg = sm->last_recv_radius;
 
 	eap = radius_msg_get_eap(msg);
-	if (eap == NULL) {
+	if (!eap) {
 		/* RFC 3579, Chap. 2.6.3:
 		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
 		 * attribute */
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
-			       HOSTAPD_LEVEL_WARNING, "could not extract "
-			       "EAP-Message from RADIUS message");
+			       HOSTAPD_LEVEL_WARNING,
+			       "could not extract EAP-Message from RADIUS message");
 		sm->eap_if->aaaEapNoReq = TRUE;
 		return;
 	}
 
 	if (wpabuf_len(eap) < sizeof(*hdr)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
-			       HOSTAPD_LEVEL_WARNING, "too short EAP packet "
-			       "received from authentication server");
+			       HOSTAPD_LEVEL_WARNING,
+			       "too short EAP packet received from authentication server");
 		wpabuf_free(eap);
 		sm->eap_if->aaaEapNoReq = TRUE;
 		return;
@@ -1366,8 +1466,8 @@
 	}
 	buf[sizeof(buf) - 1] = '\0';
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
-		       HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d "
-		       "id=%d len=%d) from RADIUS server: %s",
+		       HOSTAPD_LEVEL_DEBUG,
+		       "decapsulated EAP packet (code=%d id=%d len=%d) from RADIUS server: %s",
 		       hdr->code, hdr->identifier, be_to_host16(hdr->length),
 		       buf);
 	sm->eap_if->aaaEapReq = TRUE;
@@ -1384,15 +1484,18 @@
 				size_t shared_secret_len)
 {
 	struct radius_ms_mppe_keys *keys;
+	u8 *buf;
+	size_t len;
 	struct eapol_state_machine *sm = sta->eapol_sm;
-	if (sm == NULL)
+
+	if (!sm)
 		return;
 
 	keys = radius_msg_get_ms_keys(msg, req, shared_secret,
 				      shared_secret_len);
 
 	if (keys && keys->send && keys->recv) {
-		size_t len = keys->send_len + keys->recv_len;
+		len = keys->send_len + keys->recv_len;
 		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
 				keys->send, keys->send_len);
 		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
@@ -1420,6 +1523,20 @@
 		os_free(keys->recv);
 		os_free(keys);
 	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
+				    NULL) == 0) {
+		os_free(sm->eap_if->eapSessionId);
+		sm->eap_if->eapSessionId = os_memdup(buf, len);
+		if (sm->eap_if->eapSessionId) {
+			sm->eap_if->eapSessionIdLen = len;
+			wpa_hexdump(MSG_DEBUG, "EAP-Key Name",
+				    sm->eap_if->eapSessionId,
+				    sm->eap_if->eapSessionIdLen);
+		}
+	} else {
+		sm->eap_if->eapSessionIdLen = 0;
+	}
 }
 
 
@@ -1434,8 +1551,7 @@
 	struct radius_attr_data *nclass;
 	size_t nclass_count;
 
-	if (!hapd->conf->radius->acct_server || hapd->radius == NULL ||
-	    sm == NULL)
+	if (!hapd->conf->radius->acct_server || !hapd->radius || !sm)
 		return;
 
 	radius_free_class(&sm->radius_class);
@@ -1444,7 +1560,7 @@
 		return;
 
 	nclass = os_calloc(count, sizeof(struct radius_attr_data));
-	if (nclass == NULL)
+	if (!nclass)
 		return;
 
 	nclass_count = 0;
@@ -1461,7 +1577,7 @@
 		} while (class_len < 1);
 
 		nclass[nclass_count].data = os_memdup(attr_class, class_len);
-		if (nclass[nclass_count].data == NULL)
+		if (!nclass[nclass_count].data)
 			break;
 
 		nclass[nclass_count].len = class_len;
@@ -1470,8 +1586,9 @@
 
 	sm->radius_class.attr = nclass;
 	sm->radius_class.count = nclass_count;
-	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class "
-		   "attributes for " MACSTR,
+	wpa_printf(MSG_DEBUG,
+		   "IEEE 802.1X: Stored %lu RADIUS Class attributes for "
+		   MACSTR,
 		   (unsigned long) sm->radius_class.count,
 		   MAC2STR(sta->addr));
 }
@@ -1486,7 +1603,7 @@
 	size_t len;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-	if (sm == NULL)
+	if (!sm)
 		return;
 
 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
@@ -1494,12 +1611,12 @@
 		return;
 
 	identity = (u8 *) dup_binstr(buf, len);
-	if (identity == NULL)
+	if (!identity)
 		return;
 
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
-		       HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with "
-		       "User-Name from Access-Accept '%s'",
+		       HOSTAPD_LEVEL_DEBUG,
+		       "old identity '%s' updated with User-Name from Access-Accept '%s'",
 		       sm->identity ? (char *) sm->identity : "N/A",
 		       (char *) identity);
 
@@ -1519,7 +1636,7 @@
 	u8 *buf;
 	size_t len;
 
-	if (sm == NULL)
+	if (!sm)
 		return;
 
 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
@@ -1527,7 +1644,7 @@
 		return;
 
 	cui = wpabuf_alloc_copy(buf, len);
-	if (cui == NULL)
+	if (!cui)
 		return;
 
 	wpabuf_free(sm->radius_cui);
@@ -1548,14 +1665,16 @@
 		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",
+		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));
+		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 */
 }
@@ -1568,8 +1687,8 @@
 	if (len < 3)
 		return; /* Malformed information */
 	sta->hs20_deauth_requested = 1;
-	wpa_printf(MSG_DEBUG, "HS 2.0: Deauthentication request - Code %u  "
-		   "Re-auth Delay %u",
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Deauthentication request - Code %u  Re-auth Delay %u",
 		   *pos, WPA_GET_LE16(pos + 1));
 	wpabuf_free(sta->hs20_deauth_req);
 	sta->hs20_deauth_req = wpabuf_alloc(len + 1);
@@ -1593,16 +1712,17 @@
 		return; /* Malformed information */
 	os_free(sta->hs20_session_info_url);
 	sta->hs20_session_info_url = os_malloc(len);
-	if (sta->hs20_session_info_url == NULL)
+	if (!sta->hs20_session_info_url)
 		return;
 	swt = pos[0];
 	os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1);
 	sta->hs20_session_info_url[len - 1] = '\0';
-	wpa_printf(MSG_DEBUG, "HS 2.0: Session Information URL='%s' SWT=%u "
-		   "(session_timeout=%d)",
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Session Information URL='%s' SWT=%u (session_timeout=%d)",
 		   sta->hs20_session_info_url, swt, session_timeout);
 	if (session_timeout < 0) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: No Session-Timeout set - ignore session info URL");
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: No Session-Timeout set - ignore session info URL");
 		return;
 	}
 	if (swt == 255)
@@ -1735,6 +1855,7 @@
 ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
 {
 	struct sta_id_search id_search;
+
 	id_search.identifier = identifier;
 	id_search.sm = NULL;
 	ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
@@ -1805,9 +1926,9 @@
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
 
 	sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
-	if (sm == NULL) {
-		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching "
-			   "station for this RADIUS message");
+	if (!sm) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Could not find matching station for this RADIUS message");
 		return RADIUS_RX_UNKNOWN;
 	}
 	sta = sm->sta;
@@ -1818,12 +1939,12 @@
 	    radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
 				0) < 0 &&
 	    radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
-		wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without "
-			   "Message-Authenticator since it does not include "
-			   "EAP-Message");
+		wpa_printf(MSG_DEBUG,
+			   "Allowing RADIUS Access-Reject without Message-Authenticator since it does not include EAP-Message");
 	} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
 				     req, 1)) {
-		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
+		wpa_printf(MSG_INFO,
+			   "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
 		return RADIUS_RX_INVALID_AUTHENTICATOR;
 	}
 
@@ -1856,8 +1977,7 @@
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE8021X,
 				       HOSTAPD_LEVEL_INFO,
-				       "ignored too small "
-				       "Acct-Interim-Interval %d",
+				       "ignored too small Acct-Interim-Interval %d",
 				       acct_interim_interval);
 		} else
 			sta->acct_interim_interval = acct_interim_interval;
@@ -1926,8 +2046,7 @@
 			hostapd_logger(hapd, sm->addr,
 				       HOSTAPD_MODULE_IEEE8021X,
 				       HOSTAPD_LEVEL_DEBUG,
-				       "using EAP timeout of %d seconds (from "
-				       "RADIUS)",
+				       "using EAP timeout of %d seconds (from RADIUS)",
 				       sm->eap_if->aaaMethodTimeout);
 		} else {
 			/*
@@ -1966,7 +2085,8 @@
 void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	struct eapol_state_machine *sm = sta->eapol_sm;
-	if (sm == NULL)
+
+	if (!sm)
 		return;
 
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
@@ -2002,7 +2122,7 @@
 
 	os_free(eapol->default_wep_key);
 	eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
-	if (eapol->default_wep_key == NULL ||
+	if (!eapol->default_wep_key ||
 	    random_get_bytes(eapol->default_wep_key,
 			     hapd->conf->default_wep_key_len)) {
 		wpa_printf(MSG_INFO, "Could not generate random WEP key");
@@ -2046,8 +2166,8 @@
 
 	if (ieee802_1x_rekey_broadcast(hapd)) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
-			       HOSTAPD_LEVEL_WARNING, "failed to generate a "
-			       "new broadcast key");
+			       HOSTAPD_LEVEL_WARNING,
+			       "failed to generate a new broadcast key");
 		os_free(eapol->default_wep_key);
 		eapol->default_wep_key = NULL;
 		return;
@@ -2061,8 +2181,8 @@
 				eapol->default_wep_key,
 				hapd->conf->default_wep_key_len)) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
-			       HOSTAPD_LEVEL_WARNING, "failed to configure a "
-			       "new broadcast key");
+			       HOSTAPD_LEVEL_WARNING,
+			       "failed to configure a new broadcast key");
 		os_free(eapol->default_wep_key);
 		eapol->default_wep_key = NULL;
 		return;
@@ -2097,8 +2217,8 @@
 		     (identity_len == WSC_ID_REGISTRAR_LEN &&
 		      os_memcmp(identity, WSC_ID_REGISTRAR,
 				WSC_ID_REGISTRAR_LEN) == 0))) {
-			wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> "
-				   "WLAN_STA_WPS");
+			wpa_printf(MSG_DEBUG,
+				   "WPS: WLAN_STA_MAYBE_WPS -> WLAN_STA_WPS");
 			sta->flags |= WLAN_STA_WPS;
 		}
 	}
@@ -2125,6 +2245,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
+
 	if (preauth)
 		rsn_preauth_finished(hapd, sta, success);
 	else
@@ -2142,7 +2263,7 @@
 	int rv = -1;
 
 	eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
-	if (eap_user == NULL)
+	if (!eap_user)
 		goto out;
 
 	os_memset(user, 0, sizeof(*user));
@@ -2155,7 +2276,7 @@
 	if (eap_user->password) {
 		user->password = os_memdup(eap_user->password,
 					   eap_user->password_len);
-		if (user->password == NULL)
+		if (!user->password)
 			goto out;
 		user->password_len = eap_user->password_len;
 		user->password_hash = eap_user->password_hash;
@@ -2185,8 +2306,9 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta;
+
 	sta = ap_get_sta(hapd, addr);
-	if (sta == NULL || sta->eapol_sm == NULL)
+	if (!sta || !sta->eapol_sm)
 		return 0;
 	return 1;
 }
@@ -2223,6 +2345,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
+
 	ieee802_1x_set_sta_authorized(hapd, sta, authorized);
 }
 
@@ -2231,6 +2354,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
+
 	ieee802_1x_abort_auth(hapd, sta);
 }
 
@@ -2241,6 +2365,7 @@
 #ifndef CONFIG_NO_RC4
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
+
 	ieee802_1x_tx_key(hapd, sta);
 #endif /* CONFIG_NO_RC4 */
 #endif /* CONFIG_FIPS */
@@ -2252,6 +2377,7 @@
 {
 	/* struct hostapd_data *hapd = ctx; */
 	struct sta_info *sta = sta_ctx;
+
 	switch (type) {
 	case EAPOL_AUTH_SM_CHANGE:
 		wpa_auth_sm_notify(sta->wpa_sm);
@@ -2301,41 +2427,15 @@
 	dl_list_init(&hapd->erp_keys);
 
 	os_memset(&conf, 0, sizeof(conf));
+	conf.eap_cfg = hapd->eap_cfg;
 	conf.ctx = hapd;
 	conf.eap_reauth_period = hapd->conf->eap_reauth_period;
 	conf.wpa = hapd->conf->wpa;
 	conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
-	conf.eap_server = hapd->conf->eap_server;
-	conf.ssl_ctx = hapd->ssl_ctx;
-	conf.msg_ctx = hapd->msg_ctx;
-	conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
 	conf.eap_req_id_text = hapd->conf->eap_req_id_text;
 	conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
 	conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
 	conf.erp_domain = hapd->conf->erp_domain;
-	conf.erp = hapd->conf->eap_server_erp;
-	conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
-	conf.tls_flags = hapd->conf->tls_flags;
-	conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
-	conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
-	conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
-	conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info;
-	conf.eap_fast_prov = hapd->conf->eap_fast_prov;
-	conf.pac_key_lifetime = hapd->conf->pac_key_lifetime;
-	conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
-	conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
-	conf.tnc = hapd->conf->tnc;
-	conf.wps = hapd->wps;
-	conf.fragment_size = hapd->conf->fragment_size;
-	conf.pwd_group = hapd->conf->pwd_group;
-	conf.pbc_in_m1 = hapd->conf->pbc_in_m1;
-	if (hapd->conf->server_id) {
-		conf.server_id = (const u8 *) hapd->conf->server_id;
-		conf.server_id_len = os_strlen(hapd->conf->server_id);
-	} else {
-		conf.server_id = (const u8 *) "hostapd";
-		conf.server_id_len = 7;
-	}
 
 	os_memset(&cb, 0, sizeof(cb));
 	cb.eapol_send = ieee802_1x_eapol_send;
@@ -2354,7 +2454,7 @@
 #endif /* CONFIG_ERP */
 
 	hapd->eapol_auth = eapol_auth_init(&conf, &cb);
-	if (hapd->eapol_auth == NULL)
+	if (!hapd->eapol_auth)
 		return -1;
 
 	if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
@@ -2375,7 +2475,7 @@
 
 		ieee802_1x_rekey(hapd, NULL);
 
-		if (hapd->eapol_auth->default_wep_key == NULL)
+		if (!hapd->eapol_auth->default_wep_key)
 			return -1;
 	}
 
@@ -2418,7 +2518,7 @@
 	const unsigned char rfc1042_hdr[ETH_ALEN] =
 		{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 
-	if (sta == NULL)
+	if (!sta)
 		return -1;
 	if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2)
 		return 0;
@@ -2447,8 +2547,8 @@
 
 	if (len < (int) sizeof(*xhdr))
 		return 0;
-	wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d "
-		   "type=%d length=%d - ack=%d",
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+		   " TX status - version=%d type=%d length=%d - ack=%d",
 		   MAC2STR(sta->addr), xhdr->version, xhdr->type,
 		   be_to_host16(xhdr->length), ack);
 
@@ -2467,6 +2567,7 @@
 
 	if (pos + sizeof(struct wpa_eapol_key) <= buf + len) {
 		const struct wpa_eapol_key *wpa;
+
 		wpa = (const struct wpa_eapol_key *) pos;
 		if (wpa->type == EAPOL_KEY_TYPE_RSN ||
 		    wpa->type == EAPOL_KEY_TYPE_WPA)
@@ -2482,8 +2583,8 @@
 	if (!ack && pos + sizeof(*key) <= buf + len) {
 		key = (struct ieee802_1x_eapol_key *) pos;
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
-			       HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key "
-			       "frame (%scast index=%d)",
+			       HOSTAPD_LEVEL_DEBUG,
+			       "did not Ack EAPOL-Key frame (%scast index=%d)",
 			       key->key_index & BIT(7) ? "uni" : "broad",
 			       key->key_index & ~BIT(7));
 		/* TODO: re-send EAPOL-Key couple of times (with short delay
@@ -2503,7 +2604,7 @@
 
 u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
 {
-	if (sm == NULL || sm->identity == NULL)
+	if (!sm || !sm->identity)
 		return NULL;
 
 	*len = sm->identity_len;
@@ -2514,7 +2615,7 @@
 u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
 				 int idx)
 {
-	if (sm == NULL || sm->radius_class.attr == NULL ||
+	if (!sm || !sm->radius_class.attr ||
 	    idx >= (int) sm->radius_class.count)
 		return NULL;
 
@@ -2525,7 +2626,7 @@
 
 struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
 {
-	if (sm == NULL)
+	if (!sm)
 		return NULL;
 	return sm->radius_cui;
 }
@@ -2534,7 +2635,7 @@
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
 {
 	*len = 0;
-	if (sm == NULL)
+	if (!sm)
 		return NULL;
 
 	*len = sm->eap_if->eapKeyDataLen;
@@ -2542,10 +2643,24 @@
 }
 
 
+#ifdef CONFIG_MACSEC
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+				     size_t *len)
+{
+	*len = 0;
+	if (!sm || !sm->eap_if)
+		return NULL;
+
+	*len = sm->eap_if->eapSessionIdLen;
+	return sm->eap_if->eapSessionId;
+}
+#endif /* CONFIG_MACSEC */
+
+
 void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 				    int enabled)
 {
-	if (sm == NULL)
+	if (!sm)
 		return;
 	sm->eap_if->portEnabled = enabled ? TRUE : FALSE;
 	eapol_auth_step(sm);
@@ -2555,7 +2670,7 @@
 void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
 				  int valid)
 {
-	if (sm == NULL)
+	if (!sm)
 		return;
 	sm->portValid = valid ? TRUE : FALSE;
 	eapol_auth_step(sm);
@@ -2564,7 +2679,7 @@
 
 void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth)
 {
-	if (sm == NULL)
+	if (!sm)
 		return;
 	if (pre_auth)
 		sm->flags |= EAPOL_SM_PREAUTH;
@@ -2596,7 +2711,7 @@
 	const char *name2;
 	char *identity_buf = NULL;
 
-	if (sm == NULL)
+	if (!sm)
 		return 0;
 
 	ret = os_snprintf(buf + len, buflen - len,
@@ -2832,6 +2947,10 @@
 	}
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MACSEC
+	ieee802_1x_notify_create_actor_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
+
 	key = ieee802_1x_get_key(sta->eapol_sm, &len);
 	if (sta->session_timeout_set) {
 		os_get_reltime(&now);
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index 9594661..bb85b93 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -39,6 +39,8 @@
 				 int idx);
 struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+				     size_t *len);
 void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 				    int enabled);
 void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
@@ -57,6 +59,8 @@
 			   struct hostapd_radius_attr *req_attr,
 			   struct sta_info *sta,
 			   struct radius_msg *msg);
+int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
+			   struct radius_msg *msg, int acct);
 void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
 				   struct sta_info *sta,
 				   const u8 *eap, size_t len);
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 2b6f727..4012ae4 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -34,6 +34,60 @@
 }
 
 
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	struct hostapd_neighbor_entry *nr;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		int ret;
+		char nrie[2 * 255 + 1];
+		char lci[2 * 255 + 1];
+		char civic[2 * 255 + 1];
+		char ssid[SSID_MAX_LEN * 2 + 1];
+
+		ssid[0] = '\0';
+		wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
+				 nr->ssid.ssid_len);
+
+		nrie[0] = '\0';
+		if (nr->nr)
+			wpa_snprintf_hex(nrie, sizeof(nrie),
+					 wpabuf_head(nr->nr),
+					 wpabuf_len(nr->nr));
+
+		lci[0] = '\0';
+		if (nr->lci)
+			wpa_snprintf_hex(lci, sizeof(lci),
+					 wpabuf_head(nr->lci),
+					 wpabuf_len(nr->lci));
+
+		civic[0] = '\0';
+		if (nr->civic)
+			wpa_snprintf_hex(civic, sizeof(civic),
+					 wpabuf_head(nr->civic),
+					 wpabuf_len(nr->civic));
+
+		ret = os_snprintf(pos, end - pos, MACSTR
+				  " ssid=%s%s%s%s%s%s%s%s\n",
+				  MAC2STR(nr->bssid), ssid,
+				  nr->nr ? " nr=" : "", nrie,
+				  nr->lci ? " lci=" : "", lci,
+				  nr->civic ? " civic=" : "", civic,
+				  nr->stationary ? " stat" : "");
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
 static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
 {
 	wpabuf_free(nr->nr);
@@ -139,19 +193,21 @@
 
 #ifdef NEED_AP_MLME
 static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
-						    int ht, int vht)
+						    int ht, int vht, int he)
 {
-	if (!ht && !vht)
+	u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+
+	if (!ht && !vht && !he)
 		return NR_CHAN_WIDTH_20;
 	if (!hapd->iconf->secondary_channel)
 		return NR_CHAN_WIDTH_20;
-	if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+	if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
 		return NR_CHAN_WIDTH_40;
-	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+	if (oper_chwidth == CHANWIDTH_80MHZ)
 		return NR_CHAN_WIDTH_80;
-	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
+	if (oper_chwidth == CHANWIDTH_160MHZ)
 		return NR_CHAN_WIDTH_160;
-	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
+	if (oper_chwidth == CHANWIDTH_80P80MHZ)
 		return NR_CHAN_WIDTH_80P80;
 	return NR_CHAN_WIDTH_20;
 }
@@ -164,6 +220,7 @@
 	u16 capab = hostapd_own_capab_info(hapd);
 	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
 	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+	int he = hapd->iconf->ieee80211ax;
 	struct wpa_ssid_value ssid;
 	u8 channel, op_class;
 	u8 center_freq1_idx = 0, center_freq2_idx = 0;
@@ -205,16 +262,18 @@
 
 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
 					  hapd->iconf->secondary_channel,
-					  hapd->iconf->vht_oper_chwidth,
+					  hostapd_get_oper_chwidth(hapd->iconf),
 					  &op_class, &channel) ==
 	    NUM_HOSTAPD_MODES)
 		return;
-	width = hostapd_get_nr_chan_width(hapd, ht, vht);
+	width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
 	if (vht) {
-		center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+		center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
+			hapd->iconf);
 		if (width == NR_CHAN_WIDTH_80P80)
 			center_freq2_idx =
-				hapd->iconf->vht_oper_centr_freq_seg1_idx;
+				hostapd_get_oper_centr_freq_seg1_idx(
+					hapd->iconf);
 	} else if (ht) {
 		ieee80211_freq_to_chan(hapd->iface->freq +
 				       10 * hapd->iconf->secondary_channel,
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index 9c8f4f2..bed0a2f 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -13,6 +13,7 @@
 struct hostapd_neighbor_entry *
 hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
 		     const struct wpa_ssid_value *ssid);
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen);
 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
 			 const struct wpa_ssid_value *ssid,
 			 const struct wpabuf *nr, const struct wpabuf *lci,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 71f9f21..cbb8752 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -46,9 +46,7 @@
 static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx);
 static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
 static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
-#ifdef CONFIG_IEEE80211W
 static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
-#endif /* CONFIG_IEEE80211W */
 static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
 static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
 
@@ -301,10 +299,8 @@
 
 	os_free(sta->challenge);
 
-#ifdef CONFIG_IEEE80211W
 	os_free(sta->sa_query_trans_id);
 	eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_P2P
 	p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
@@ -330,6 +326,7 @@
 	os_free(sta->ht_capabilities);
 	os_free(sta->vht_capabilities);
 	os_free(sta->vht_operation);
+	os_free(sta->he_capab);
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
@@ -670,6 +667,7 @@
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
 {
 	struct sta_info *sta;
+	int i;
 
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
@@ -694,6 +692,15 @@
 		return NULL;
 	}
 
+	for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+		if (!hapd->iface->basic_rates)
+			break;
+		if (hapd->iface->basic_rates[i] < 0)
+			break;
+		sta->supported_rates[i] = hapd->iface->basic_rates[i] / 5;
+	}
+	sta->supported_rates_len = i;
+
 	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
 		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
 			   "for " MACSTR " (%d seconds - ap_max_inactivity)",
@@ -812,6 +819,8 @@
 			       ap_handle_timer, hapd, sta);
 	accounting_sta_stop(hapd, sta);
 	ieee802_1x_free_station(hapd, sta);
+	wpa_auth_sta_deinit(sta->wpa_sm);
+	sta->wpa_sm = NULL;
 
 	sta->disassoc_reason = reason;
 	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
@@ -1082,8 +1091,6 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
-
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	u32 tu;
@@ -1173,8 +1180,6 @@
 	sta->sa_query_count = 0;
 }
 
-#endif /* CONFIG_IEEE80211W */
-
 
 const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
 				  struct sta_info *sta)
@@ -1401,7 +1406,7 @@
 	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",
+	res = os_snprintf(buf, buflen, "%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]" : ""),
@@ -1420,6 +1425,7 @@
 			  (flags & WLAN_STA_GAS ? "[GAS]" : ""),
 			  (flags & WLAN_STA_HT ? "[HT]" : ""),
 			  (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+			  (flags & WLAN_STA_HE ? "[HE]" : ""),
 			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
 			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
 			   "[WNM_SLEEP_MODE]" : ""));
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index ece0c60..de806b4 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -37,6 +37,7 @@
 #define WLAN_STA_VENDOR_VHT BIT(21)
 #define WLAN_STA_PENDING_FILS_ERP BIT(22)
 #define WLAN_STA_MULTI_AP BIT(23)
+#define WLAN_STA_HE BIT(24)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -119,6 +120,7 @@
 	unsigned int agreed_to_steer:1;
 	unsigned int hs20_t_c_filtering:1;
 	unsigned int ft_over_ds:1;
+	unsigned int external_dh_updated:1;
 
 	u16 auth_alg;
 
@@ -166,8 +168,9 @@
 	struct ieee80211_vht_capabilities *vht_capabilities;
 	struct ieee80211_vht_operation *vht_operation;
 	u8 vht_opmode;
+	struct ieee80211_he_capabilities *he_capab;
+	size_t he_capab_len;
 
-#ifdef CONFIG_IEEE80211W
 	int sa_query_count; /* number of pending SA Query requests;
 			     * 0 = no SA Query in progress */
 	int sa_query_timed_out;
@@ -175,7 +178,6 @@
 				* sa_query_count octets of pending SA Query
 				* transaction identifiers */
 	struct os_reltime sa_query_start;
-#endif /* CONFIG_IEEE80211W */
 
 #if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
 #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
@@ -275,6 +277,10 @@
 	u8 last_tk[WPA_TK_MAX_LEN];
 	size_t last_tk_len;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_weight;
+	struct os_reltime backlogged_until;
+#endif /* CONFIG_AIRTIME_POLICY */
 };
 
 
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 8054c5d..9f52dee 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -20,6 +20,13 @@
 #include "ap_drv_ops.h"
 #include "wmm.h"
 
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
 
 static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
 {
@@ -39,6 +46,62 @@
 }
 
 
+static void
+wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf,
+			 struct hostapd_wmm_ac_params *wmm,
+			 const struct hostapd_wmm_rule *wmm_reg)
+{
+	int ac;
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin);
+		wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax);
+		wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs);
+		wmm[ac].txop_limit =
+			MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop);
+		wmm[ac].admission_control_mandatory =
+			wmm_conf[ac].admission_control_mandatory;
+	}
+}
+
+
+/*
+ * Calculate WMM regulatory limit if any.
+ */
+static void wmm_calc_regulatory_limit(struct hostapd_data *hapd,
+				      struct hostapd_wmm_ac_params *acp)
+{
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	int c;
+
+	os_memcpy(acp, hapd->iconf->wmm_ac_params,
+		  sizeof(hapd->iconf->wmm_ac_params));
+
+	for (c = 0; mode && c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+
+		if (chan->freq != hapd->iface->freq)
+			continue;
+
+		if (chan->wmm_rules_valid)
+			wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params,
+						 acp, chan->wmm_rules);
+		break;
+	}
+
+	/*
+	 * Check if we need to update set count. Since both were initialized to
+	 * zero we can compare the whole array in one shot.
+	 */
+	if (os_memcmp(acp, hapd->iface->prev_wmm,
+		      sizeof(hapd->iconf->wmm_ac_params)) != 0) {
+		os_memcpy(hapd->iface->prev_wmm, acp,
+			  sizeof(hapd->iconf->wmm_ac_params));
+		hapd->parameter_set_count++;
+	}
+}
+
+
 /*
  * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
  * Response frames.
@@ -48,10 +111,12 @@
 	u8 *pos = eid;
 	struct wmm_parameter_element *wmm =
 		(struct wmm_parameter_element *) (pos + 2);
+	struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM] = { 0 };
 	int e;
 
 	if (!hapd->conf->wmm_enabled)
 		return eid;
+	wmm_calc_regulatory_limit(hapd, wmmp);
 	eid[0] = WLAN_EID_VENDOR_SPECIFIC;
 	wmm->oui[0] = 0x00;
 	wmm->oui[1] = 0x50;
@@ -70,8 +135,7 @@
 	/* fill in a parameter set record for each AC */
 	for (e = 0; e < 4; e++) {
 		struct wmm_ac_parameter *ac = &wmm->ac[e];
-		struct hostapd_wmm_ac_params *acp =
-			&hapd->iconf->wmm_ac_params[e];
+		struct hostapd_wmm_ac_params *acp = &wmmp[e];
 
 		ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
 					      acp->admission_control_mandatory,
@@ -227,10 +291,11 @@
 
 static void wmm_addts_req(struct hostapd_data *hapd,
 			  const struct ieee80211_mgmt *mgmt,
-			  struct wmm_tspec_element *tspec, size_t len)
+			  const struct wmm_tspec_element *tspec, size_t len)
 {
 	const u8 *end = ((const u8 *) mgmt) + len;
 	int res;
+	struct wmm_tspec_element tspec_resp;
 
 	if ((const u8 *) (tspec + 1) > end) {
 		wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
@@ -242,10 +307,11 @@
 		   mgmt->u.action.u.wmm_action.dialog_token,
 		   MAC2STR(mgmt->sa));
 
-	res = wmm_process_tspec(tspec);
+	os_memcpy(&tspec_resp, tspec, sizeof(struct wmm_tspec_element));
+	res = wmm_process_tspec(&tspec_resp);
 	wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
 
-	wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
+	wmm_send_action(hapd, mgmt->sa, &tspec_resp, WMM_ACTION_CODE_ADDTS_RESP,
 			mgmt->u.action.u.wmm_action.dialog_token, res);
 }
 
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 27c69d3..e4dcfe9 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -150,7 +150,6 @@
 		pos += gtk_elem_len;
 		wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
 			   (int) gtk_elem_len);
-#ifdef CONFIG_IEEE80211W
 		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
 		if (res < 0)
 			goto fail;
@@ -158,7 +157,6 @@
 		pos += igtk_elem_len;
 		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
 			   (int) igtk_elem_len);
-#endif /* CONFIG_IEEE80211W */
 
 		WPA_PUT_LE16((u8 *)
 			     &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index e89a716..6611b0e 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -320,6 +320,19 @@
 }
 
 
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
+{
+	if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
+		wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
+			   MACSTR " (%d seconds)", MAC2STR(sm->addr),
+			   sm->wpa_auth->conf.wpa_ptk_rekey);
+		eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+		eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
+				       wpa_rekey_ptk, sm->wpa_auth, sm);
+	}
+}
+
+
 static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
 {
 	if (sm->pmksa == ctx)
@@ -695,6 +708,7 @@
 #endif /* CONFIG_IEEE80211R_AP */
 	os_free(sm->last_rx_eapol_key);
 	os_free(sm->wpa_ie);
+	os_free(sm->rsnxe);
 	wpa_group_put(sm->wpa_auth, sm->group);
 #ifdef CONFIG_DPP2
 	wpabuf_clear_free(sm->dpp_z);
@@ -921,6 +935,7 @@
 
 	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = TRUE;
 
 	return 0;
@@ -1394,6 +1409,8 @@
 #endif /* CONFIG_SHA256 */
 #endif /* CONFIG_SHA384 */
 
+	forced_memzero(data, sizeof(data));
+
 	return ret;
 }
 
@@ -1735,6 +1752,8 @@
 		sm->pmk_len = 0;
 		os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
 		sm->xxkey_len = 0;
+		os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+		sm->pmk_r1_len = 0;
 #endif /* CONFIG_IEEE80211R_AP */
 		break;
 	case WPA_REAUTH:
@@ -1776,6 +1795,7 @@
 
 		/* Using FT protocol, not WPA auth state machine */
 		sm->ft_completed = 1;
+		wpa_auth_set_ptk_rekey_timer(sm);
 		return 0;
 #else /* CONFIG_IEEE80211R_AP */
 		break;
@@ -1799,10 +1819,8 @@
 	sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R_AP */
 
-#ifdef CONFIG_IEEE80211W
 	if (sm->mgmt_frame_prot && event == WPA_AUTH)
 		remove_ptk = 0;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_FILS
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 	    (event == WPA_AUTH || event == WPA_ASSOC))
@@ -2030,7 +2048,7 @@
 		sm->Disconnect = TRUE;
 		return;
 	}
-	os_memset(msk, 0, sizeof(msk));
+	forced_memzero(msk, sizeof(msk));
 
 	sm->req_replay_counter_used = 0;
 	/* IEEE 802.11i does not set keyRun to FALSE, but not doing this
@@ -2130,6 +2148,28 @@
 			wpa_printf(MSG_DEBUG,
 				   "RSN: No KCK available to derive PMKID for message 1/4");
 			pmkid = NULL;
+#ifdef CONFIG_FILS
+		} else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+			if (sm->pmkid_set) {
+				wpa_hexdump(MSG_DEBUG,
+					    "RSN: Message 1/4 PMKID from FILS/ERP",
+					    sm->pmkid, PMKID_LEN);
+				os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+					  sm->pmkid, PMKID_LEN);
+			} else {
+				/* No PMKID available */
+				wpa_printf(MSG_DEBUG,
+					   "RSN: No FILS/ERP PMKID available for message 1/4");
+				pmkid = NULL;
+			}
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+		} else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+			   sm->ft_completed) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: No PMKID in message 1/4 when using FT protocol");
+			pmkid = NULL;
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
 		} else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
 			if (sm->pmkid_set) {
@@ -2158,6 +2198,8 @@
 				    &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
 		}
 	}
+	if (!pmkid)
+		pmkid_len = 0;
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
 		       sm->ANonce, pmkid, pmkid_len, 0, 0);
@@ -2172,8 +2214,20 @@
 	size_t z_len = 0;
 
 #ifdef CONFIG_IEEE80211R_AP
-	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
-		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		if (sm->ft_completed) {
+			u8 ptk_name[WPA_PMK_NAME_LEN];
+
+			return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+						 sm->SNonce, sm->ANonce,
+						 sm->addr, sm->wpa_auth->addr,
+						 sm->pmk_r1_name,
+						 ptk, ptk_name,
+						 sm->wpa_key_mgmt,
+						 sm->pairwise);
+		}
+		return wpa_auth_derive_ptk_ft(sm, ptk);
+	}
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_DPP2
@@ -2234,12 +2288,12 @@
 		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
 			    pmk_r0_name, WPA_PMK_NAME_LEN);
 		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
-		os_memset(fils_ft, 0, sizeof(fils_ft));
+		forced_memzero(fils_ft, sizeof(fils_ft));
 
 		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
 					     sm->addr, sm->pmk_r1_name,
 					     use_sha384);
-		os_memset(pmk_r0, 0, PMK_LEN_MAX);
+		forced_memzero(pmk_r0, PMK_LEN_MAX);
 		if (res < 0)
 			return -1;
 		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
@@ -2257,7 +2311,7 @@
 			       sm->wpa_key_mgmt, sm->fils_key_auth_sta,
 			       sm->fils_key_auth_ap,
 			       &sm->fils_key_auth_len);
-	os_memset(ick, 0, sizeof(ick));
+	forced_memzero(ick, sizeof(ick));
 
 	/* Store nonces for (Re)Association Request/Response frame processing */
 	os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
@@ -2559,7 +2613,7 @@
 	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
 		wpa_printf(MSG_DEBUG,
 			   "FILS: Not enough room for FILS elements");
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		return -1;
 	}
 
@@ -2569,7 +2623,7 @@
 	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
 			    wpabuf_head(plain), wpabuf_len(plain),
 			    5, aad, aad_len, pos) < 0) {
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		return -1;
 	}
 
@@ -2577,7 +2631,7 @@
 		    "FILS: Encrypted Association Response elements",
 		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
 	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	sm->fils_completed = 1;
 
@@ -2631,7 +2685,7 @@
 		 * of GTK in the BSS.
 		 */
 		if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
-			wpabuf_free(plain);
+			wpabuf_clear_free(plain);
 			return NULL;
 		}
 		gtk = dummy_gtk;
@@ -2658,13 +2712,13 @@
 		if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
 			wpa_printf(MSG_WARNING,
 				   "FILS: Failed to get channel info for OCI element");
-			wpabuf_free(plain);
+			wpabuf_clear_free(plain);
 			return NULL;
 		}
 
 		pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
 		if (ocv_insert_extended_oci(&ci, pos) < 0) {
-			wpabuf_free(plain);
+			wpabuf_clear_free(plain);
 			return NULL;
 		}
 	}
@@ -2727,7 +2781,7 @@
 
 	wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
 		   (unsigned int) wpabuf_len(plain));
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 	sm->fils_completed = 1;
 	return pos;
 }
@@ -2796,6 +2850,12 @@
 			pmk_len = sm->pmk_len;
 		}
 
+		if ((!pmk || !pmk_len) && sm->pmksa) {
+			wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
+			pmk = sm->pmksa->pmk;
+			pmk_len = sm->pmksa->pmk_len;
+		}
+
 		if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
 			break;
 
@@ -2878,6 +2938,22 @@
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
 		return;
 	}
+	if ((!sm->rsnxe && kde.rsnxe) ||
+	    (sm->rsnxe && !kde.rsnxe) ||
+	    (sm->rsnxe && kde.rsnxe &&
+	     (sm->rsnxe_len != kde.rsnxe_len ||
+	      os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
+		wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
+			    sm->rsnxe, sm->rsnxe_len);
+		wpa_hexdump(MSG_DEBUG, "RSNXE in EAPOL-Key msg 2/4",
+			    kde.rsnxe, kde.rsnxe_len);
+		/* MLME-DEAUTHENTICATE.request */
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		return;
+	}
 #ifdef CONFIG_OCV
 	if (wpa_auth_uses_ocv(sm)) {
 		struct wpa_channel_info ci;
@@ -2973,6 +3049,7 @@
 	sm->MICVerified = TRUE;
 
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = TRUE;
 }
 
@@ -2984,8 +3061,6 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
-
 static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 {
 	if (sm->mgmt_frame_prot) {
@@ -3032,21 +3107,6 @@
 	return pos;
 }
 
-#else /* CONFIG_IEEE80211W */
-
-static int ieee80211w_kde_len(struct wpa_state_machine *sm)
-{
-	return 0;
-}
-
-
-static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
-{
-	return pos;
-}
-
-#endif /* CONFIG_IEEE80211W */
-
 
 static int ocv_oci_len(struct wpa_state_machine *sm)
 {
@@ -3080,11 +3140,12 @@
 
 SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 {
-	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, dummy_gtk[32];
 	size_t gtk_len, kde_len;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
-	int wpa_ie_len, secure, keyidx, encr = 0;
+	int wpa_ie_len, secure, gtkidx, encr = 0;
+	u8 *wpa_ie_buf = NULL;
 
 	SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
 	sm->TimeoutEvt = FALSE;
@@ -3118,6 +3179,35 @@
 			wpa_ie = wpa_ie + wpa_ie[1] + 2;
 		wpa_ie_len = wpa_ie[1] + 2;
 	}
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->wpa_auth->conf.rsnxe_override_eapol_len) {
+		u8 *obuf = sm->wpa_auth->conf.rsnxe_override_eapol;
+		size_t olen = sm->wpa_auth->conf.rsnxe_override_eapol_len;
+		const u8 *rsnxe;
+
+		wpa_hexdump(MSG_DEBUG,
+			    "TESTING: wpa_ie before RSNXE EAPOL override",
+			    wpa_ie, wpa_ie_len);
+		wpa_ie_buf = os_malloc(wpa_ie_len + olen);
+		if (!wpa_ie_buf)
+			return;
+		os_memcpy(wpa_ie_buf, wpa_ie, wpa_ie_len);
+		wpa_ie = wpa_ie_buf;
+		rsnxe = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_RSNX);
+		if (rsnxe) {
+			u8 rsnxe_len = 2 + rsnxe[1];
+
+			os_memmove((void *) rsnxe, rsnxe + rsnxe_len,
+				   wpa_ie_len - (rsnxe - wpa_ie) - rsnxe_len);
+			wpa_ie_len -= rsnxe_len;
+		}
+		os_memcpy(wpa_ie + wpa_ie_len, obuf, olen);
+		wpa_ie_len += olen;
+		wpa_hexdump(MSG_DEBUG,
+			    "TESTING: wpa_ie after RSNXE EAPOL override",
+			    wpa_ie, wpa_ie_len);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 			"sending 3/4 msg of 4-Way Handshake");
 	if (sm->wpa == WPA_VERSION_WPA2) {
@@ -3132,10 +3222,10 @@
 			 * of GTK in the BSS.
 			 */
 			if (random_get_bytes(dummy_gtk, gtk_len) < 0)
-				return;
+				goto done;
 			gtk = dummy_gtk;
 		}
-		keyidx = gsm->GN;
+		gtkidx = gsm->GN;
 		_rsc = rsc;
 		encr = 1;
 	} else {
@@ -3143,7 +3233,6 @@
 		secure = 0;
 		gtk = NULL;
 		gtk_len = 0;
-		keyidx = 0;
 		_rsc = NULL;
 		if (sm->rx_eapol_key_secure) {
 			/*
@@ -3176,7 +3265,7 @@
 #endif /* CONFIG_P2P */
 	kde = os_malloc(kde_len);
 	if (kde == NULL)
-		return;
+		goto done;
 
 	pos = kde;
 	os_memcpy(pos, wpa_ie, wpa_ie_len);
@@ -3191,8 +3280,7 @@
 		if (res < 0) {
 			wpa_printf(MSG_ERROR, "FT: Failed to insert "
 				   "PMKR1Name into RSN IE in EAPOL-Key data");
-			os_free(kde);
-			return;
+			goto done;
 		}
 		pos -= wpa_ie_len;
 		pos += elen;
@@ -3200,16 +3288,14 @@
 #endif /* CONFIG_IEEE80211R_AP */
 	if (gtk) {
 		u8 hdr[2];
-		hdr[0] = keyidx & 0x03;
+		hdr[0] = gtkidx & 0x03;
 		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gtk_len);
 	}
 	pos = ieee80211w_kde_add(sm, pos);
-	if (ocv_oci_add(sm, &pos) < 0) {
-		os_free(kde);
-		return;
-	}
+	if (ocv_oci_add(sm, &pos) < 0)
+		goto done;
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -3235,8 +3321,7 @@
 		if (res < 0) {
 			wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
 				   "into EAPOL-Key Key Data");
-			os_free(kde);
-			return;
+			goto done;
 		}
 		pos += res;
 
@@ -3272,8 +3357,10 @@
 			WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
-		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
+done:
 	os_free(kde);
+	os_free(wpa_ie_buf);
 }
 
 
@@ -3293,12 +3380,7 @@
 		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 		sm->pairwise_set = TRUE;
 
-		if (sm->wpa_auth->conf.wpa_ptk_rekey) {
-			eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
-			eloop_register_timeout(sm->wpa_auth->conf.
-					       wpa_ptk_rekey, 0, wpa_rekey_ptk,
-					       sm->wpa_auth, sm);
-		}
+		wpa_auth_set_ptk_rekey_timer(sm);
 
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
 		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
@@ -3690,7 +3772,6 @@
 	wpa_hexdump_key(MSG_DEBUG, "GTK",
 			group->GTK[group->GN - 1], group->GTK_len);
 
-#ifdef CONFIG_IEEE80211W
 	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		size_t len;
 		len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
@@ -3703,7 +3784,6 @@
 		wpa_hexdump_key(MSG_DEBUG, "IGTK",
 				group->IGTK[group->GN_igtk - 4], len);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	return ret;
 }
@@ -3721,10 +3801,8 @@
 	os_memset(group->GTK, 0, sizeof(group->GTK));
 	group->GN = 1;
 	group->GM = 2;
-#ifdef CONFIG_IEEE80211W
 	group->GN_igtk = 4;
 	group->GM_igtk = 5;
-#endif /* CONFIG_IEEE80211W */
 	/* GTK[GN] = CalcGTK() */
 	wpa_gtk_update(wpa_auth, group);
 }
@@ -3813,7 +3891,6 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
 	struct wpa_group *gsm = sm->group;
@@ -3842,7 +3919,7 @@
 
 	return pos - start;
 }
-#endif /* CONFIG_IEEE80211W */
+
 #endif /* CONFIG_WNM_AP */
 
 
@@ -3859,11 +3936,9 @@
 	tmp = group->GM;
 	group->GM = group->GN;
 	group->GN = tmp;
-#ifdef CONFIG_IEEE80211W
 	tmp = group->GM_igtk;
 	group->GM_igtk = group->GN_igtk;
 	group->GN_igtk = tmp;
-#endif /* CONFIG_IEEE80211W */
 	/* "GKeyDoneStations = GNoStations" is done in more robust way by
 	 * counting the STAs that are marked with GUpdateStationKeys instead of
 	 * including all STAs that could be in not-yet-completed state. */
@@ -3892,7 +3967,6 @@
 			     group->GTK[group->GN - 1], group->GTK_len) < 0)
 		ret = -1;
 
-#ifdef CONFIG_IEEE80211W
 	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		enum wpa_alg alg;
 		size_t len;
@@ -3906,7 +3980,6 @@
 				     group->IGTK[group->GN_igtk - 4], len) < 0)
 			ret = -1;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	return ret;
 }
@@ -4044,11 +4117,9 @@
 		tmp = group->GM;
 		group->GM = group->GN;
 		group->GN = tmp;
-#ifdef CONFIG_IEEE80211W
 		tmp = group->GM_igtk;
 		group->GM_igtk = group->GN_igtk;
 		group->GN_igtk = tmp;
-#endif /* CONFIG_IEEE80211W */
 		wpa_gtk_update(wpa_auth, group);
 		wpa_group_config_group_keys(wpa_auth, group);
 	}
@@ -4194,8 +4265,12 @@
 
 	/* Private MIB */
 	ret = os_snprintf(buf + len, buflen - len,
+			  "wpa=%d\n"
+			  "AKMSuiteSelector=" RSN_SUITE "\n"
 			  "hostapdWPAPTKState=%d\n"
 			  "hostapdWPAPTKGroupState=%d\n",
+			  sm->wpa,
+			  RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
 			  sm->wpa_ptk_state,
 			  sm->wpa_ptk_group_state);
 	if (os_snprintf_error(buflen - len, ret))
@@ -4307,6 +4382,15 @@
 	    sm->wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
+#ifdef CONFIG_IEEE80211R_AP
+	if (pmk_len >= 2 * PMK_LEN && wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+	    !wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+		/* Cache MPMK/XXKey instead of initial part from MSK */
+		pmk = pmk + PMK_LEN;
+		pmk_len = PMK_LEN;
+	} else
+#endif /* CONFIG_IEEE80211R_AP */
 	if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
 		if (pmk_len > PMK_LEN_SUITE_B_192)
 			pmk_len = PMK_LEN_SUITE_B_192;
@@ -4314,6 +4398,7 @@
 		pmk_len = PMK_LEN;
 	}
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, pmk_len);
 	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
 				 sm->PTK.kck, sm->PTK.kck_len,
 				 sm->wpa_auth->addr, sm->addr, session_timeout,
@@ -4332,6 +4417,7 @@
 	if (wpa_auth == NULL)
 		return -1;
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from preauth", pmk, len);
 	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
 				 NULL, 0,
 				 wpa_auth->addr,
@@ -4349,6 +4435,7 @@
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
 	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
 				 NULL, 0,
 				 wpa_auth->addr, addr, 0, NULL,
@@ -4373,6 +4460,7 @@
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
 	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
 				 NULL, 0, wpa_auth->addr, addr, session_timeout,
 				 NULL, akmp))
@@ -4846,6 +4934,16 @@
 	*fils_kek_len = sm->PTK.kek_len;
 }
 
+
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+				 size_t pmk_len, const u8 *pmkid)
+{
+	os_memcpy(sm->PMK, pmk, pmk_len);
+	sm->pmk_len = pmk_len;
+	os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+	sm->pmkid_set = 1;
+}
+
 #endif /* CONFIG_FILS */
 
 
@@ -4896,13 +4994,11 @@
 		       void *ctx1, void *ctx2)
 {
 	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
-#ifdef CONFIG_IEEE80211W
 	u8 *opos;
-#endif /* CONFIG_IEEE80211W */
 	size_t gtk_len, kde_len;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
-	int wpa_ie_len, secure, keyidx, encr = 0;
+	int wpa_ie_len, secure, gtkidx, encr = 0;
 
 	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
 	   GTK[GN], IGTK, [FTIE], [TIE * 2])
@@ -4929,7 +5025,7 @@
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
 		gtk_len = gsm->GTK_len;
-		keyidx = gsm->GN;
+		gtkidx = gsm->GN;
 		_rsc = rsc;
 		encr = 1;
 	} else {
@@ -4937,7 +5033,6 @@
 		secure = 0;
 		gtk = NULL;
 		gtk_len = 0;
-		keyidx = 0;
 		_rsc = NULL;
 		if (sm->rx_eapol_key_secure) {
 			/*
@@ -4990,12 +5085,11 @@
 #endif /* CONFIG_IEEE80211R_AP */
 	if (gtk) {
 		u8 hdr[2];
-		hdr[0] = keyidx & 0x03;
+		hdr[0] = gtkidx & 0x03;
 		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gtk_len);
 	}
-#ifdef CONFIG_IEEE80211W
 	opos = pos;
 	pos = ieee80211w_kde_add(sm, pos);
 	if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
@@ -5003,7 +5097,6 @@
 		opos += 2 + RSN_SELECTOR_LEN + 2;
 		os_memset(opos, 0, 6); /* clear PN */
 	}
-#endif /* CONFIG_IEEE80211W */
 	if (ocv_oci_add(sm, &pos) < 0) {
 		os_free(kde);
 		return -1;
@@ -5060,7 +5153,7 @@
 			WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
-		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
 	os_free(kde);
 	return 0;
 }
@@ -5074,9 +5167,7 @@
 	struct wpa_group *gsm = sm->group;
 	const u8 *kde;
 	u8 *kde_buf = NULL, *pos, hdr[2];
-#ifdef CONFIG_IEEE80211W
 	u8 *opos;
-#endif /* CONFIG_IEEE80211W */
 	size_t kde_len;
 	u8 *gtk;
 
@@ -5099,7 +5190,6 @@
 		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gsm->GTK_len);
-#ifdef CONFIG_IEEE80211W
 		opos = pos;
 		pos = ieee80211w_kde_add(sm, pos);
 		if (pos - opos >=
@@ -5108,7 +5198,6 @@
 			opos += 2 + RSN_SELECTOR_LEN + 2;
 			os_memset(opos, 0, 6); /* clear PN */
 		}
-#endif /* CONFIG_IEEE80211W */
 		if (ocv_oci_add(sm, &pos) < 0) {
 			os_free(kde_buf);
 			return -1;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 484e1e5..933a4b8 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -187,11 +187,9 @@
 	int disable_pmksa_caching;
 	int okc;
 	int tx_status;
-#ifdef CONFIG_IEEE80211W
 	enum mfp_options ieee80211w;
 	int group_mgmt_cipher;
 	int sae_require_mfp;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	int ocv; /* Operating Channel Validation */
 #endif /* CONFIG_OCV */
@@ -221,6 +219,8 @@
 	double corrupt_gtk_rekey_mic_probability;
 	u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
 	size_t own_ie_override_len;
+	u8 rsnxe_override_eapol[MAX_OWN_IE_OVERRIDE];
+	size_t rsnxe_override_eapol_len;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_P2P
 	u8 ip_addr_go[4];
@@ -232,6 +232,7 @@
 	unsigned int fils_cache_id_set:1;
 	u8 fils_cache_id[FILS_CACHE_ID_LEN];
 #endif /* CONFIG_FILS */
+	int sae_pwe;
 };
 
 typedef enum {
@@ -320,6 +321,7 @@
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 			struct wpa_state_machine *sm, int freq,
 			const u8 *wpa_ie, size_t wpa_ie_len,
+			const u8 *rsnxe, size_t rsnxe_len,
 			const u8 *mdie, size_t mdie_len,
 			const u8 *owe_dh, size_t owe_dh_len);
 int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
@@ -470,9 +472,14 @@
 void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
 				   u8 *fils_anonce, u8 *fils_snonce,
 				   u8 *fils_kek, size_t *fils_kek_len);
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+				 size_t pmk_len, const u8 *pmkid);
 u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
 				   u8 *pos, size_t max_len,
 				   const u8 *req_ies, size_t req_ies_len);
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+				    u8 *pos, size_t max_len,
+				    const u8 *req_ies, size_t req_ies_len);
 void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
 
@@ -486,5 +493,6 @@
 			     void (*cb)(void *ctx1, void *ctx2),
 			     void *ctx1, void *ctx2);
 int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
 
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index fdb7eba..a599be2 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -25,6 +25,7 @@
 #include "wmm.h"
 #include "wpa_auth.h"
 #include "wpa_auth_i.h"
+#include "pmksa_cache_auth.h"
 
 
 #ifdef CONFIG_IEEE80211R_AP
@@ -2074,8 +2075,7 @@
 }
 
 
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
-			   struct wpa_ptk *ptk)
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
 {
 	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
 	size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
@@ -2095,8 +2095,16 @@
 	const u8 *identity, *radius_cui;
 	size_t identity_len, radius_cui_len;
 	int session_timeout;
+	const u8 *mpmk;
+	size_t mpmk_len;
 
-	if (sm->xxkey_len == 0) {
+	if (sm->xxkey_len > 0) {
+		mpmk = sm->xxkey;
+		mpmk_len = sm->xxkey_len;
+	} else if (sm->pmksa) {
+		mpmk = sm->pmksa->pmk;
+		mpmk_len = sm->pmksa->pmk_len;
+	} else {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
 			   "derivation");
 		return -1;
@@ -2113,7 +2121,7 @@
 					       &radius_cui);
 	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
 
-	if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+	if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
 			      r0kh, r0kh_len, sm->addr,
 			      pmk_r0, pmk_r0_name,
 			      wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
@@ -2218,12 +2226,12 @@
 		return NULL;
 	}
 
+	forced_memzero(keybuf, sizeof(keybuf));
 	*len = subelem_len;
 	return subelem;
 }
 
 
-#ifdef CONFIG_IEEE80211W
 static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
 {
 	u8 *subelem, *pos;
@@ -2270,7 +2278,6 @@
 	*len = subelem_len;
 	return subelem;
 }
-#endif /* CONFIG_IEEE80211W */
 
 
 static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
@@ -2411,6 +2418,8 @@
 	u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
 	u8 *fte_mic, *elem_count;
 	size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+	u8 rsnxe[10];
+	size_t rsnxe_len;
 	int res;
 	struct wpa_auth_config *conf;
 	struct wpa_ft_ies parse;
@@ -2478,7 +2487,6 @@
 		r0kh_id_len = sm->r0kh_id_len;
 		anonce = sm->ANonce;
 		snonce = sm->SNonce;
-#ifdef CONFIG_IEEE80211W
 		if (sm->mgmt_frame_prot) {
 			u8 *igtk;
 			size_t igtk_len;
@@ -2501,7 +2509,6 @@
 			subelem_len += igtk_len;
 			os_free(igtk);
 		}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 		if (wpa_auth_uses_ocv(sm)) {
 			struct wpa_channel_info ci;
@@ -2575,6 +2582,13 @@
 	if (ric_start == pos)
 		ric_start = NULL;
 
+	res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe, sizeof(rsnxe));
+	if (res < 0)
+		return NULL;
+	rsnxe_len = res;
+	if (auth_alg == WLAN_AUTH_FT && rsnxe_len)
+		*elem_count += 1;
+
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
 		kck = sm->PTK.kck2;
 		kck_len = sm->PTK.kck2_len;
@@ -2587,6 +2601,7 @@
 		       mdie, mdie_len, ftie, ftie_len,
 		       rsnie, rsnie_len,
 		       ric_start, ric_start ? pos - ric_start : 0,
+		       rsnxe_len ? rsnxe : NULL, rsnxe_len,
 		       fte_mic) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 		return NULL;
@@ -2984,6 +2999,8 @@
 	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
 	sm->pmk_r1_name_valid = 1;
 	os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+	os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
+	sm->pmk_r1_len = pmk_r1_len;
 
 	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
@@ -3089,8 +3106,9 @@
 	status = res;
 
 	wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
-		   " auth_transaction=%d status=%d",
-		   MAC2STR(sm->addr), auth_transaction + 1, status);
+		   " auth_transaction=%d status=%u (%s)",
+		   MAC2STR(sm->addr), auth_transaction + 1, status,
+		   status2str(status));
 	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
 	cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
 	   resp_ies, resp_ies_len);
@@ -3241,6 +3259,8 @@
 	count = 3;
 	if (parse.ric)
 		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+	if (parse.rsnxe)
+		count++;
 	if (fte_elem_count != count) {
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
@@ -3260,6 +3280,8 @@
 		       parse.ftie - 2, parse.ftie_len + 2,
 		       parse.rsn - 2, parse.rsn_len + 2,
 		       parse.ric, parse.ric_len,
+		       parse.rsnxe ? parse.rsnxe - 2 : NULL,
+		       parse.rsnxe ? parse.rsnxe_len + 2 : 0,
 		       mic) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -3278,6 +3300,9 @@
 			    parse.ftie - 2, parse.ftie_len + 2);
 		wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
 			    parse.rsn - 2, parse.rsn_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
+			    parse.rsnxe ? parse.rsnxe - 2 : NULL,
+			    parse.rsnxe ? parse.rsnxe_len + 2 : 0);
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
@@ -3448,8 +3473,9 @@
 	u8 *pos;
 
 	wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
-		   " CurrentAP=" MACSTR " status=%d",
-		   MAC2STR(sm->addr), MAC2STR(current_ap), status);
+		   " CurrentAP=" MACSTR " status=%u (%s)",
+		   MAC2STR(sm->addr), MAC2STR(current_ap), status,
+		   status2str(status));
 	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
 
 	/* RRB - Forward action frame response to the Current AP */
@@ -3555,7 +3581,7 @@
 			       pmk_r0->vlan, src_addr, type,
 			       packet, packet_len);
 
-	os_memset(pmk_r1, 0, sizeof(pmk_r1));
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
 
 	return ret;
 }
@@ -3881,10 +3907,7 @@
 
 	ret = 0;
 out:
-	if (plain) {
-		os_memset(plain, 0, plain_len);
-		os_free(plain);
-	}
+	bin_clear_free(plain, plain_len);
 
 	return ret;
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 45172c6..7fb0923 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -53,6 +53,10 @@
 	wconf->rsn_pairwise = conf->rsn_pairwise;
 	wconf->rsn_preauth = conf->rsn_preauth;
 	wconf->eapol_version = conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (wconf->eapol_version > 2)
+		wconf->eapol_version = 2;
+#endif /* CONFIG_MACSEC */
 	wconf->wmm_enabled = conf->wmm_enabled;
 	wconf->wmm_uapsd = conf->wmm_uapsd;
 	wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
@@ -60,11 +64,9 @@
 	wconf->ocv = conf->ocv;
 #endif /* CONFIG_OCV */
 	wconf->okc = conf->okc;
-#ifdef CONFIG_IEEE80211W
 	wconf->ieee80211w = conf->ieee80211w;
 	wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
 	wconf->sae_require_mfp = conf->sae_require_mfp;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R_AP
 	wconf->ssid_len = conf->ssid.ssid_len;
 	if (wconf->ssid_len > SSID_MAX_LEN)
@@ -103,9 +105,7 @@
 		wconf->rsn_pairwise = WPA_CIPHER_CCMP;
 		wconf->rsn_preauth = 0;
 		wconf->disable_pmksa_caching = 1;
-#ifdef CONFIG_IEEE80211W
 		wconf->ieee80211w = 1;
-#endif /* CONFIG_IEEE80211W */
 	}
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_TESTING_OPTIONS
@@ -118,6 +118,14 @@
 			  wpabuf_head(conf->own_ie_override),
 			  wconf->own_ie_override_len);
 	}
+	if (conf->rsnxe_override_eapol &&
+	    wpabuf_len(conf->rsnxe_override_eapol) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsnxe_override_eapol_len =
+			wpabuf_len(conf->rsnxe_override_eapol);
+		os_memcpy(wconf->rsnxe_override_eapol,
+			  wpabuf_head(conf->rsnxe_override_eapol),
+			  wconf->rsnxe_override_eapol_len);
+	}
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_P2P
 	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -130,6 +138,7 @@
 	os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
 		  FILS_CACHE_ID_LEN);
 #endif /* CONFIG_FILS */
+	wconf->sae_pwe = conf->sae_pwe;
 }
 
 
@@ -376,7 +385,6 @@
 				os_memcpy(sta->last_tk, key, key_len);
 			sta->last_tk_len = key_len;
 		}
-#ifdef CONFIG_IEEE80211W
 	} else if (alg == WPA_ALG_IGTK ||
 		   alg == WPA_ALG_BIP_GMAC_128 ||
 		   alg == WPA_ALG_BIP_GMAC_256 ||
@@ -386,7 +394,6 @@
 		if (key)
 			os_memcpy(hapd->last_igtk, key, key_len);
 		hapd->last_igtk_len = key_len;
-#endif /* CONFIG_IEEE80211W */
 	} else {
 		hapd->last_gtk_alg = alg;
 		hapd->last_gtk_key_idx = idx;
@@ -892,18 +899,28 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta;
+	int ret;
 
 	wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR
 		   " based on WPA authenticator callback",
 		   MAC2STR(sta_addr));
-	if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
+	ret = hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT);
+
+	/*
+	 * The expected return values from hostapd_add_sta_node() are
+	 * 0: successfully added STA entry
+	 * -EOPNOTSUPP: driver or driver wrapper does not support/need this
+	 *	operations
+	 * any other negative value: error in adding the STA entry */
+	if (ret < 0 && ret != -EOPNOTSUPP)
 		return NULL;
 
 	sta = ap_sta_add(hapd, sta_addr);
 	if (sta == NULL)
 		return NULL;
-	if (hapd->driver && hapd->driver->add_sta_node)
+	if (ret == 0)
 		sta->added_unassoc = 1;
+
 	sta->ft_over_ds = 1;
 	if (sta->wpa_sm) {
 		sta->auth_alg = WLAN_AUTH_FT;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 3dcf77a..a993f50 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -102,6 +102,8 @@
 
 	u8 *wpa_ie;
 	size_t wpa_ie_len;
+	u8 *rsnxe;
+	size_t rsnxe_len;
 
 	enum {
 		WPA_VERSION_NO_WPA = 0 /* WPA not used */,
@@ -119,6 +121,8 @@
 	u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
 				* first 384 bits of MSK */
 	size_t xxkey_len;
+	u8 pmk_r1[PMK_LEN_MAX];
+	unsigned int pmk_r1_len;
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
 					   * Request */
 	u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
@@ -188,10 +192,8 @@
 	Boolean changed;
 	Boolean first_sta_seen;
 	Boolean reject_4way_hs_for_entropy;
-#ifdef CONFIG_IEEE80211W
 	u8 IGTK[2][WPA_IGTK_MAX_LEN];
 	int GN_igtk, GM_igtk;
-#endif /* CONFIG_IEEE80211W */
 	/* Number of references except those in struct wpa_group->next */
 	unsigned int references;
 	unsigned int num_setup_iface;
@@ -267,6 +269,7 @@
 
 int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 		     const u8 *pmkid);
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len);
 void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		     logger_level level, const char *txt);
 void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
@@ -290,8 +293,7 @@
 		   const u8 *anonce, const u8 *snonce,
 		   u8 *buf, size_t len, const u8 *subelem,
 		   size_t subelem_len);
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
-			   struct wpa_ptk *ptk);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk);
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 8580a5a..2e6d059 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -183,7 +183,6 @@
 		num_suites++;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
 		pos += RSN_SELECTOR_LEN;
@@ -194,7 +193,6 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_SAE
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
@@ -286,13 +284,11 @@
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
 	}
-#ifdef CONFIG_IEEE80211W
 	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		capab |= WPA_CAPABILITY_MFPC;
 		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
 			capab |= WPA_CAPABILITY_MFPR;
 	}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	if (conf->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
@@ -314,7 +310,6 @@
 		pos += PMKID_LEN;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
 	    conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
 		if (2 + 4 > buf + len - pos)
@@ -347,7 +342,6 @@
 		}
 		pos += RSN_SELECTOR_LEN;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -378,6 +372,26 @@
 }
 
 
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+
+	if (conf->sae_pwe != 1 && conf->sae_pwe != 2)
+		return 0; /* no supported extended RSN capabilities */
+
+	if (len < 3)
+		return -1;
+
+	*pos++ = WLAN_EID_RSNX;
+	*pos++ = 1;
+	/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
+	 * used for now */
+	*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+
+	return pos - buf;
+}
+
+
 static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
 {
 	u8 *len;
@@ -411,13 +425,11 @@
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
 	}
-#ifdef CONFIG_IEEE80211W
 	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		capab |= WPA_CAPABILITY_MFPC;
 		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
 			capab |= WPA_CAPABILITY_MFPR;
 	}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	if (conf->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
@@ -464,6 +476,11 @@
 		if (res < 0)
 			return res;
 		pos += res;
+		res = wpa_write_rsnxe(&wpa_auth->conf, pos,
+				      buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
 	}
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
@@ -532,6 +549,7 @@
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 			struct wpa_state_machine *sm, int freq,
 			const u8 *wpa_ie, size_t wpa_ie_len,
+			const u8 *rsnxe, size_t rsnxe_len,
 			const u8 *mdie, size_t mdie_len,
 			const u8 *owe_dh, size_t owe_dh_len)
 {
@@ -607,12 +625,10 @@
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
 			selector = RSN_AUTH_KEY_MGMT_FT_PSK;
 #endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
 		else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
 			selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_SAE
 		else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
 			selector = RSN_AUTH_KEY_MGMT_SAE;
@@ -717,12 +733,10 @@
 	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
 #endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
 	else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_SAE
 	else if (key_mgmt & WPA_KEY_MGMT_SAE)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
@@ -758,7 +772,6 @@
 		return WPA_INVALID_PAIRWISE;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
 		if (!(data.capabilities & WPA_CAPABILITY_MFPC)) {
 			wpa_printf(MSG_DEBUG, "Management frame protection "
@@ -807,7 +820,6 @@
 			       "Management frame protection cannot use TKIP");
 		    return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -941,6 +953,21 @@
 	os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
 	sm->wpa_ie_len = wpa_ie_len;
 
+	if (rsnxe && rsnxe_len) {
+		if (!sm->rsnxe || sm->rsnxe_len < rsnxe_len) {
+			os_free(sm->rsnxe);
+			sm->rsnxe = os_malloc(rsnxe_len);
+			if (!sm->rsnxe)
+				return WPA_ALLOC_FAIL;
+		}
+		os_memcpy(sm->rsnxe, rsnxe, rsnxe_len);
+		sm->rsnxe_len = rsnxe_len;
+	} else {
+		os_free(sm->rsnxe);
+		sm->rsnxe = NULL;
+		sm->rsnxe_len = 0;
+	}
+
 	return WPA_IE_OK;
 }
 
@@ -975,153 +1002,6 @@
 #endif /* CONFIG_HS20 */
 
 
-/**
- * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
- * @pos: Pointer to the IE header
- * @end: Pointer to the end of the Key Data buffer
- * @ie: Pointer to parsed IE data
- * Returns: 0 on success, 1 if end mark is found, -1 on failure
- */
-static int wpa_parse_generic(const u8 *pos, const u8 *end,
-			     struct wpa_eapol_ie_parse *ie)
-{
-	if (pos[1] == 0)
-		return 1;
-
-	if (pos[1] >= 6 &&
-	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
-	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
-	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
-		ie->wpa_ie = pos;
-		ie->wpa_ie_len = pos[1] + 2;
-		return 0;
-	}
-
-	if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
-		ie->osen = pos;
-		ie->osen_len = pos[1] + 2;
-		return 0;
-	}
-
-	if (1 + RSN_SELECTOR_LEN < end - pos &&
-	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
-		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
-		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
-		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
-		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-
-#ifdef CONFIG_IEEE80211W
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
-		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-#endif /* CONFIG_IEEE80211W */
-
-#ifdef CONFIG_P2P
-	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
-		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
-			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
-		return 0;
-	}
-
-	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
-		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG,
-			    "WPA: IP Address Allocation in EAPOL-Key",
-			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
-		return 0;
-	}
-#endif /* CONFIG_P2P */
-
-#ifdef CONFIG_OCV
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
-		ie->oci = pos + 2 + RSN_SELECTOR_LEN;
-		ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
-		return 0;
-	}
-#endif /* CONFIG_OCV */
-
-	return 0;
-}
-
-
-/**
- * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs
- * @buf: Pointer to the Key Data buffer
- * @len: Key Data Length
- * @ie: Pointer to parsed IE data
- * Returns: 0 on success, -1 on failure
- */
-int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
-{
-	const u8 *pos, *end;
-	int ret = 0;
-
-	os_memset(ie, 0, sizeof(*ie));
-	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
-		if (pos[0] == 0xdd &&
-		    ((pos == buf + len - 1) || pos[1] == 0)) {
-			/* Ignore padding */
-			break;
-		}
-		if (2 + pos[1] > end - pos) {
-			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
-				   "underflow (ie=%d len=%d pos=%d)",
-				   pos[0], pos[1], (int) (pos - buf));
-			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
-					buf, len);
-			ret = -1;
-			break;
-		}
-		if (*pos == WLAN_EID_RSN) {
-			ie->rsn_ie = pos;
-			ie->rsn_ie_len = pos[1] + 2;
-#ifdef CONFIG_IEEE80211R_AP
-		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
-			ie->mdie = pos;
-			ie->mdie_len = pos[1] + 2;
-		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
-			ie->ftie = pos;
-			ie->ftie_len = pos[1] + 2;
-#endif /* CONFIG_IEEE80211R_AP */
-		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
-			ret = wpa_parse_generic(pos, end, ie);
-			if (ret < 0)
-				break;
-			if (ret > 0) {
-				ret = 0;
-				break;
-			}
-		} else {
-			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
-				    "Key Data IE", pos, 2 + pos[1]);
-		}
-	}
-
-	return ret;
-}
-
-
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
 {
 	return sm ? sm->mgmt_frame_prot : 0;
@@ -1176,3 +1056,23 @@
 	return pos + res;
 }
 #endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+				    u8 *pos, size_t max_len,
+				    const u8 *req_ies, size_t req_ies_len)
+{
+	int res;
+
+	if (!sm ||
+	    sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+				WPA_KEY_MGMT_FT_FILS_SHA384))
+		return pos;
+
+	res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL);
+	if (res < 0)
+		return pos;
+	return pos + res;
+}
+#endif /* CONFIG_FILS */
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index a38b206..dd44b9e 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -9,41 +9,6 @@
 #ifndef WPA_AUTH_IE_H
 #define WPA_AUTH_IE_H
 
-struct wpa_eapol_ie_parse {
-	const u8 *wpa_ie;
-	size_t wpa_ie_len;
-	const u8 *rsn_ie;
-	size_t rsn_ie_len;
-	const u8 *pmkid;
-	const u8 *gtk;
-	size_t gtk_len;
-	const u8 *mac_addr;
-	size_t mac_addr_len;
-#ifdef CONFIG_IEEE80211W
-	const u8 *igtk;
-	size_t igtk_len;
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R_AP
-	const u8 *mdie;
-	size_t mdie_len;
-	const u8 *ftie;
-	size_t ftie_len;
-#endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_P2P
-	const u8 *ip_addr_req;
-	const u8 *ip_addr_alloc;
-#endif /* CONFIG_P2P */
-#ifdef CONFIG_OCV
-	const u8 *oci;
-	size_t oci_len;
-#endif /* CONFIG_OCV */
-
-	const u8 *osen;
-	size_t osen_len;
-};
-
-int wpa_parse_kde_ies(const u8 *buf, size_t len,
-		      struct wpa_eapol_ie_parse *ie);
 u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
 		 const u8 *data2, size_t data2_len);
 int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth);
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
new file mode 100644
index 0000000..b6e4797
--- /dev/null
+++ b/src/ap/wpa_auth_kay.c
@@ -0,0 +1,523 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "pae/ieee802_1x_key.h"
+#include "pae/ieee802_1x_kay.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "wpa_auth_kay.h"
+#include "ieee802_1x.h"
+
+
+#define DEFAULT_KEY_LEN		16
+/* secure Connectivity Association Key Name (CKN) */
+#define DEFAULT_CKN_LEN		16
+
+
+static int hapd_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_init)
+		return -1;
+	return hapd->driver->macsec_init(hapd->drv_priv, params);
+}
+
+
+static int hapd_macsec_deinit(void *priv)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_deinit)
+		return -1;
+	return hapd->driver->macsec_deinit(hapd->drv_priv);
+}
+
+
+static int hapd_macsec_get_capability(void *priv, enum macsec_cap *cap)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_get_capability)
+		return -1;
+	return hapd->driver->macsec_get_capability(hapd->drv_priv, cap);
+}
+
+
+static int hapd_enable_protect_frames(void *priv, Boolean enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_protect_frames)
+		return -1;
+	return hapd->driver->enable_protect_frames(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_enable_encrypt(void *priv, Boolean enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_encrypt)
+		return -1;
+	return hapd->driver->enable_encrypt(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_set_replay_protect(void *priv, Boolean enabled, u32 window)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_replay_protect)
+		return -1;
+	return hapd->driver->set_replay_protect(hapd->drv_priv, enabled,
+						 window);
+}
+
+
+static int hapd_set_current_cipher_suite(void *priv, u64 cs)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_current_cipher_suite)
+		return -1;
+	return hapd->driver->set_current_cipher_suite(hapd->drv_priv, cs);
+}
+
+
+static int hapd_enable_controlled_port(void *priv, Boolean enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_controlled_port)
+		return -1;
+	return hapd->driver->enable_controlled_port(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->get_receive_lowest_pn)
+		return -1;
+	return hapd->driver->get_receive_lowest_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->get_transmit_next_pn)
+		return -1;
+	return hapd->driver->get_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_transmit_next_pn)
+		return -1;
+	return hapd->driver->set_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static unsigned int conf_offset_val(enum confidentiality_offset co)
+{
+	switch (co) {
+	case CONFIDENTIALITY_OFFSET_30:
+		return 30;
+		break;
+	case CONFIDENTIALITY_OFFSET_50:
+		return 50;
+	default:
+		return 0;
+	}
+}
+
+
+static int hapd_create_receive_sc(void *priv, struct receive_sc *sc,
+				  enum validate_frames vf,
+				  enum confidentiality_offset co)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_receive_sc)
+		return -1;
+	return hapd->driver->create_receive_sc(hapd->drv_priv, sc,
+					       conf_offset_val(co), vf);
+}
+
+
+static int hapd_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_receive_sc)
+		return -1;
+	return hapd->driver->delete_receive_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_receive_sa)
+		return -1;
+	return hapd->driver->create_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_receive_sa)
+		return -1;
+	return hapd->driver->delete_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_receive_sa)
+		return -1;
+	return hapd->driver->enable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->disable_receive_sa)
+		return -1;
+	return hapd->driver->disable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int
+hapd_create_transmit_sc(void *priv, struct transmit_sc *sc,
+			enum confidentiality_offset co)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_transmit_sc)
+		return -1;
+	return hapd->driver->create_transmit_sc(hapd->drv_priv, sc,
+						conf_offset_val(co));
+}
+
+
+static int hapd_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_transmit_sc)
+		return -1;
+	return hapd->driver->delete_transmit_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_transmit_sa)
+		return -1;
+	return hapd->driver->create_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_transmit_sa)
+		return -1;
+	return hapd->driver->delete_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_transmit_sa)
+		return -1;
+	return hapd->driver->enable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->disable_transmit_sa)
+		return -1;
+	return hapd->driver->disable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	struct ieee802_1x_kay_ctx *kay_ctx;
+	struct ieee802_1x_kay *res = NULL;
+	enum macsec_policy policy;
+
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
+
+	if (!hapd->conf || hapd->conf->macsec_policy == 0)
+		return 0;
+
+	if (hapd->conf->macsec_policy == 1) {
+		if (hapd->conf->macsec_integ_only == 1)
+			policy = SHOULD_SECURE;
+		else
+			policy = SHOULD_ENCRYPT;
+	} else {
+		policy = DO_NOT_SECURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: if_name=%s", __func__, hapd->conf->iface);
+	kay_ctx = os_zalloc(sizeof(*kay_ctx));
+	if (!kay_ctx)
+		return -1;
+
+	kay_ctx->ctx = hapd;
+
+	kay_ctx->macsec_init = hapd_macsec_init;
+	kay_ctx->macsec_deinit = hapd_macsec_deinit;
+	kay_ctx->macsec_get_capability = hapd_macsec_get_capability;
+	kay_ctx->enable_protect_frames = hapd_enable_protect_frames;
+	kay_ctx->enable_encrypt = hapd_enable_encrypt;
+	kay_ctx->set_replay_protect = hapd_set_replay_protect;
+	kay_ctx->set_current_cipher_suite = hapd_set_current_cipher_suite;
+	kay_ctx->enable_controlled_port = hapd_enable_controlled_port;
+	kay_ctx->get_receive_lowest_pn = hapd_get_receive_lowest_pn;
+	kay_ctx->get_transmit_next_pn = hapd_get_transmit_next_pn;
+	kay_ctx->set_transmit_next_pn = hapd_set_transmit_next_pn;
+	kay_ctx->create_receive_sc = hapd_create_receive_sc;
+	kay_ctx->delete_receive_sc = hapd_delete_receive_sc;
+	kay_ctx->create_receive_sa = hapd_create_receive_sa;
+	kay_ctx->delete_receive_sa = hapd_delete_receive_sa;
+	kay_ctx->enable_receive_sa = hapd_enable_receive_sa;
+	kay_ctx->disable_receive_sa = hapd_disable_receive_sa;
+	kay_ctx->create_transmit_sc = hapd_create_transmit_sc;
+	kay_ctx->delete_transmit_sc = hapd_delete_transmit_sc;
+	kay_ctx->create_transmit_sa = hapd_create_transmit_sa;
+	kay_ctx->delete_transmit_sa = hapd_delete_transmit_sa;
+	kay_ctx->enable_transmit_sa = hapd_enable_transmit_sa;
+	kay_ctx->disable_transmit_sa = hapd_disable_transmit_sa;
+
+	res = ieee802_1x_kay_init(kay_ctx, policy,
+				  hapd->conf->macsec_replay_protect,
+				  hapd->conf->macsec_replay_window,
+				  hapd->conf->macsec_port,
+				  hapd->conf->mka_priority, hapd->conf->iface,
+				  hapd->own_addr);
+	/* ieee802_1x_kay_init() frees kay_ctx on failure */
+	if (!res)
+		return -1;
+
+	hapd->kay = res;
+
+	return 0;
+}
+
+
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+	if (!hapd->kay)
+		return;
+
+	ieee802_1x_kay_deinit(hapd->kay);
+	hapd->kay = NULL;
+}
+
+
+static int ieee802_1x_auth_get_session_id(struct hostapd_data *hapd,
+					  struct sta_info *sta, u8 *sid,
+					  size_t *len)
+{
+	const u8 *session_id;
+	size_t id_len, need_len;
+
+	session_id = ieee802_1x_get_session_id(sta->eapol_sm, &id_len);
+	if (!session_id) {
+		wpa_printf(MSG_DEBUG,
+			   "MACsec: Failed to get SessionID from EAPOL state machines");
+		return -1;
+	}
+
+	need_len = 1 + 2 * 32 /* random size */;
+	if (need_len > id_len) {
+		wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
+		return -1;
+	}
+
+	os_memcpy(sid, session_id, need_len);
+	*len = need_len;
+
+	return 0;
+}
+
+
+static int ieee802_1x_auth_get_msk(struct hostapd_data *hapd,
+				   struct sta_info *sta, u8 *msk, size_t *len)
+{
+	const u8 *key;
+	size_t keylen;
+
+	if (!sta->eapol_sm)
+		return -1;
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "MACsec: Failed to get MSK from EAPOL state machines");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "MACsec: Successfully fetched key (len=%lu)",
+		   (unsigned long) keylen);
+	wpa_hexdump_key(MSG_DEBUG, "MSK: ", key, keylen);
+
+	if (keylen > *len)
+		keylen = *len;
+	os_memcpy(msk, key, keylen);
+	*len = keylen;
+
+	return 0;
+}
+
+
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+					   struct sta_info *sta)
+{
+	u8 *sid;
+	size_t sid_len = 128;
+	struct mka_key_name *ckn;
+	struct mka_key *cak;
+	struct mka_key *msk;
+	void *res = NULL;
+
+	if (!hapd->kay || hapd->kay->policy == DO_NOT_SECURE)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG,
+		   "IEEE 802.1X: External notification - Create MKA for "
+		   MACSTR, MAC2STR(sta->addr));
+
+	msk = os_zalloc(sizeof(*msk));
+	sid = os_zalloc(sid_len);
+	ckn = os_zalloc(sizeof(*ckn));
+	cak = os_zalloc(sizeof(*cak));
+	if (!msk || !sid || !ckn || !cak)
+		goto fail;
+
+	msk->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_auth_get_msk(hapd, sta, msk->key, &msk->len)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK");
+		goto fail;
+	}
+
+	if (ieee802_1x_auth_get_session_id(hapd, sta, sid, &sid_len))
+	{
+		wpa_printf(MSG_ERROR,
+			   "IEEE 802.1X: Could not get EAP Session Id");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "own_addr", hapd->own_addr, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "sta_addr", sta->addr, ETH_ALEN);
+
+	/* Derive CAK from MSK */
+	cak->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_cak_aes_cmac(msk->key, msk->len, hapd->own_addr,
+				    sta->addr, cak->key, cak->len)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CAK failed");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len);
+
+	/* Derive CKN from MSK */
+	ckn->len = DEFAULT_CKN_LEN;
+	if (ieee802_1x_ckn_aes_cmac(msk->key, msk->len, hapd->own_addr,
+				    sta->addr, sid, sid_len, ckn->name)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CKN failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len);
+
+	res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, EAP_EXCHANGE,
+					TRUE);
+
+fail:
+	bin_clear_free(msk, sizeof(*msk));
+	os_free(sid);
+	os_free(ckn);
+	bin_clear_free(cak, sizeof(*cak));
+
+	return res;
+}
+
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+	struct mka_key *cak;
+	struct mka_key_name *ckn;
+	void *res = NULL;
+
+	if ((hapd->conf->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET)
+		goto end;
+
+	ckn = os_zalloc(sizeof(*ckn));
+	if (!ckn)
+		goto end;
+
+	cak = os_zalloc(sizeof(*cak));
+	if (!cak)
+		goto free_ckn;
+
+	if (ieee802_1x_alloc_kay_sm_hapd(hapd, sta) < 0 || !hapd->kay)
+		goto free_cak;
+
+	if (hapd->kay->policy == DO_NOT_SECURE)
+		goto dealloc;
+
+	cak->len = hapd->conf->mka_cak_len;
+	os_memcpy(cak->key, hapd->conf->mka_cak, cak->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);
+	if (res)
+		goto free_cak;
+
+dealloc:
+	/* Failed to create MKA */
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
+free_cak:
+	os_free(cak);
+free_ckn:
+	os_free(ckn);
+end:
+	return res;
+}
diff --git a/src/ap/wpa_auth_kay.h b/src/ap/wpa_auth_kay.h
new file mode 100644
index 0000000..0dd7e41
--- /dev/null
+++ b/src/ap/wpa_auth_kay.h
@@ -0,0 +1,51 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_KAY_H
+#define WPA_AUTH_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+				 struct sta_info *sta);
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+					   struct sta_info *sta);
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd);
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+					    struct sta_info *sta);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+					       struct sta_info *sta)
+{
+	return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+				    struct sta_info *sta)
+{
+	return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+}
+
+static inline void *
+ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	return NULL;
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPA_AUTH_KAY_H */
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 6161cdb..175b9fc 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -358,12 +358,10 @@
 		    (cred->auth_type & WPS_AUTH_WPA2PSK) &&
 		    cred->key_len != 2 * PMK_LEN) {
 			bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
-#ifdef CONFIG_IEEE80211W
 			if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION)
 				bss->ieee80211w =
 					MGMT_FRAME_PROTECTION_OPTIONAL;
 			bss->sae_require_mfp = 1;
-#endif /* CONFIG_IEEE80211W */
 		}
 
 		if (cred->key_len >= 8 && cred->key_len < 64) {
@@ -533,9 +531,7 @@
 
 	if (wpa) {
 		char *prefix;
-#ifdef CONFIG_IEEE80211W
 		int sae = 0;
-#endif /* CONFIG_IEEE80211W */
 
 		fprintf(nconf, "wpa=%d\n", wpa);
 
@@ -553,13 +549,10 @@
 		    (cred->auth_type & WPS_AUTH_WPA2PSK) &&
 		    cred->key_len != 2 * PMK_LEN) {
 			fprintf(nconf, "%sSAE", prefix);
-#ifdef CONFIG_IEEE80211W
 			sae = 1;
-#endif /* CONFIG_IEEE80211W */
 		}
 		fprintf(nconf, "\n");
 
-#ifdef CONFIG_IEEE80211W
 		if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
 			fprintf(nconf, "ieee80211w=%d\n",
 				MGMT_FRAME_PROTECTION_OPTIONAL);
@@ -567,7 +560,6 @@
 		}
 		if (sae)
 			fprintf(nconf, "sae_require_mfp=1\n");
-#endif /* CONFIG_IEEE80211W */
 
 		fprintf(nconf, "wpa_pairwise=");
 		prefix = "";
@@ -1425,6 +1417,7 @@
 		data->count++;
 		wps_registrar_wps_cancel(hapd->wps->registrar);
 		ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL);
 	}
 
 	return 0;
diff --git a/src/common/Makefile b/src/common/Makefile
index e703630..ccb280e 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -9,7 +9,6 @@
 include ../lib.rules
 
 CFLAGS += -DCONFIG_IEEE80211R
-CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_SAE
 CFLAGS += -DCONFIG_SUITE
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 30c5247..7694c96 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/module_tests.h"
 #include "crypto/crypto.h"
+#include "crypto/dh_groups.h"
 #include "ieee802_11_common.h"
 #include "ieee802_11_defs.h"
 #include "gas.h"
@@ -258,6 +259,7 @@
 	/* IEEE P802.11-REVmd/D2.1, Annex J.10 */
 	const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 };
 	const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 };
+	const char *ssid = "byteme";
 	const char *pw = "mekmitasdigoat";
 	const char *pwid = "psk4internet";
 	const u8 local_rand[] = {
@@ -338,6 +340,72 @@
 	};
 	struct wpabuf *buf = NULL;
 	struct crypto_bignum *mask = NULL;
+	const u8 pwe_19_x[32] = {
+		0xc9, 0x30, 0x49, 0xb9, 0xe6, 0x40, 0x00, 0xf8,
+		0x48, 0x20, 0x16, 0x49, 0xe9, 0x99, 0xf2, 0xb5,
+		0xc2, 0x2d, 0xea, 0x69, 0xb5, 0x63, 0x2c, 0x9d,
+		0xf4, 0xd6, 0x33, 0xb8, 0xaa, 0x1f, 0x6c, 0x1e
+	};
+	const u8 pwe_19_y[32] = {
+		0x73, 0x63, 0x4e, 0x94, 0xb5, 0x3d, 0x82, 0xe7,
+		0x38, 0x3a, 0x8d, 0x25, 0x81, 0x99, 0xd9, 0xdc,
+		0x1a, 0x5e, 0xe8, 0x26, 0x9d, 0x06, 0x03, 0x82,
+		0xcc, 0xbf, 0x33, 0xe6, 0x14, 0xff, 0x59, 0xa0
+	};
+	const u8 pwe_15[384] = {
+		0x69, 0x68, 0x73, 0x65, 0x8f, 0x65, 0x31, 0x42,
+		0x9f, 0x97, 0x39, 0x6f, 0xb8, 0x5f, 0x89, 0xe1,
+		0xfc, 0xd2, 0xf6, 0x92, 0x19, 0xa9, 0x0e, 0x82,
+		0x2f, 0xf7, 0xf4, 0xbc, 0x0b, 0xd8, 0xa7, 0x9f,
+		0xf0, 0x80, 0x35, 0x31, 0x6f, 0xca, 0xe1, 0xa5,
+		0x39, 0x77, 0xdc, 0x11, 0x2b, 0x0b, 0xfe, 0x2e,
+		0x6f, 0x65, 0x6d, 0xc7, 0xd4, 0xa4, 0x5b, 0x08,
+		0x1f, 0xd9, 0xbb, 0xe2, 0x22, 0x85, 0x31, 0x81,
+		0x79, 0x70, 0xbe, 0xa1, 0x66, 0x58, 0x4a, 0x09,
+		0x3c, 0x57, 0x34, 0x3c, 0x9d, 0x57, 0x8f, 0x42,
+		0x58, 0xd0, 0x39, 0x81, 0xdb, 0x8f, 0x79, 0xa2,
+		0x1b, 0x01, 0xcd, 0x27, 0xc9, 0xae, 0xcf, 0xcb,
+		0x9c, 0xdb, 0x1f, 0x84, 0xb8, 0x88, 0x4e, 0x8f,
+		0x50, 0x66, 0xb4, 0x29, 0x83, 0x1e, 0xb9, 0x89,
+		0x0c, 0xa5, 0x47, 0x21, 0xba, 0x10, 0xd5, 0xaa,
+		0x1a, 0x80, 0xce, 0xf1, 0x4c, 0xad, 0x16, 0xda,
+		0x57, 0xb2, 0x41, 0x8a, 0xbe, 0x4b, 0x8c, 0xb0,
+		0xb2, 0xeb, 0xf7, 0xa8, 0x0e, 0x3e, 0xcf, 0x22,
+		0x8f, 0xd8, 0xb6, 0xdb, 0x79, 0x9c, 0x9b, 0x80,
+		0xaf, 0xd7, 0x14, 0xad, 0x51, 0x82, 0xf4, 0x64,
+		0xb6, 0x3f, 0x4c, 0x6c, 0xe5, 0x3f, 0xaa, 0x6f,
+		0xbf, 0x3d, 0xc2, 0x3f, 0x77, 0xfd, 0xcb, 0xe1,
+		0x9c, 0xe3, 0x1e, 0x8a, 0x0e, 0x97, 0xe2, 0x2b,
+		0xe2, 0xdd, 0x37, 0x39, 0x88, 0xc2, 0x8e, 0xbe,
+		0xfa, 0xac, 0x3d, 0x5b, 0x62, 0x2e, 0x1e, 0x74,
+		0xa0, 0x9a, 0xf8, 0xed, 0xfa, 0xe1, 0xce, 0x9c,
+		0xab, 0xbb, 0xdc, 0x36, 0xb1, 0x28, 0x46, 0x3c,
+		0x7e, 0xa8, 0xbd, 0xb9, 0x36, 0x4c, 0x26, 0x75,
+		0xe0, 0x17, 0x73, 0x1f, 0xe0, 0xfe, 0xf6, 0x49,
+		0xfa, 0xa0, 0x45, 0xf4, 0x44, 0x05, 0x20, 0x27,
+		0x25, 0xc2, 0x99, 0xde, 0x27, 0x8b, 0x70, 0xdc,
+		0x54, 0x60, 0x90, 0x02, 0x1e, 0x29, 0x97, 0x9a,
+		0xc4, 0xe7, 0xb6, 0xf5, 0x8b, 0xae, 0x7c, 0x34,
+		0xaa, 0xef, 0x9b, 0xc6, 0x30, 0xf2, 0x80, 0x8d,
+		0x80, 0x78, 0xc2, 0x55, 0x63, 0xa0, 0xa1, 0x38,
+		0x70, 0xfb, 0xf4, 0x74, 0x8d, 0xcd, 0x87, 0x90,
+		0xb4, 0x54, 0xc3, 0x75, 0xdf, 0x10, 0xc5, 0xb6,
+		0xb2, 0x08, 0x59, 0x61, 0xe6, 0x68, 0xa5, 0x82,
+		0xf8, 0x8f, 0x47, 0x30, 0x43, 0xb4, 0xdc, 0x31,
+		0xfc, 0xbc, 0x69, 0xe7, 0xb4, 0x94, 0xb0, 0x6a,
+		0x60, 0x59, 0x80, 0x2e, 0xd3, 0xa4, 0xe8, 0x97,
+		0xa2, 0xa3, 0xc9, 0x08, 0x4b, 0x27, 0x6c, 0xc1,
+		0x37, 0xe8, 0xfc, 0x5c, 0xe2, 0x54, 0x30, 0x3e,
+		0xf8, 0xfe, 0xa2, 0xfc, 0xbb, 0xbd, 0x88, 0x6c,
+		0x92, 0xa3, 0x2a, 0x40, 0x7a, 0x2c, 0x22, 0x38,
+		0x8c, 0x86, 0x86, 0xfe, 0xb9, 0xd4, 0x6b, 0xd6,
+		0x47, 0x88, 0xa7, 0xf6, 0x8e, 0x0f, 0x14, 0xad,
+		0x1e, 0xac, 0xcf, 0x33, 0x01, 0x99, 0xc1, 0x62
+	};
+	int pt_groups[] = { 19, 20, 21, 25, 26, 28, 29, 30, 15, 0 };
+	struct sae_pt *pt_info, *pt;
+	const u8 addr1b[ETH_ALEN] = { 0x00, 0x09, 0x5b, 0x66, 0xec, 0x1e };
+	const u8 addr2b[ETH_ALEN] = { 0x00, 0x0b, 0x6b, 0xd9, 0x02, 0x46 };
 
 	os_memset(&sae, 0, sizeof(sae));
 	buf = wpabuf_alloc(1000);
@@ -377,7 +445,7 @@
 	}
 
 	if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
-		    NULL) != 0 ||
+			     NULL, 0) != 0 ||
 	    sae_process_commit(&sae) < 0)
 		goto fail;
 
@@ -411,6 +479,62 @@
 	if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0)
 		goto fail;
 
+	pt_info = sae_derive_pt(pt_groups,
+				(const u8 *) ssid, os_strlen(ssid),
+				(const u8 *) pw, os_strlen(pw), pwid);
+	if (!pt_info)
+		goto fail;
+
+	for (pt = pt_info; pt; pt = pt->next) {
+		if (pt->group == 19) {
+			struct crypto_ec_point *pwe;
+			u8 bin[SAE_MAX_ECC_PRIME_LEN * 2];
+			size_t prime_len = sizeof(pwe_19_x);
+
+			pwe = sae_derive_pwe_from_pt_ecc(pt, addr1b, addr2b);
+			if (!pwe) {
+				sae_deinit_pt(pt);
+				goto fail;
+			}
+			if (crypto_ec_point_to_bin(pt->ec, pwe, bin,
+						   bin + prime_len) < 0 ||
+			    os_memcmp(pwe_19_x, bin, prime_len) != 0 ||
+			    os_memcmp(pwe_19_y, bin + prime_len,
+				      prime_len) != 0) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: PT/PWE test vector mismatch");
+				crypto_ec_point_deinit(pwe, 1);
+				sae_deinit_pt(pt);
+				goto fail;
+			}
+			crypto_ec_point_deinit(pwe, 1);
+		}
+
+		if (pt->group == 15) {
+			struct crypto_bignum *pwe;
+			u8 bin[SAE_MAX_PRIME_LEN];
+			size_t prime_len = sizeof(pwe_15);
+
+			pwe = sae_derive_pwe_from_pt_ffc(pt, addr1b, addr2b);
+			if (!pwe) {
+				sae_deinit_pt(pt);
+				goto fail;
+			}
+			if (crypto_bignum_to_bin(pwe, bin, sizeof(bin),
+						 prime_len) < 0 ||
+			    os_memcmp(pwe_15, bin, prime_len) != 0) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: PT/PWE test vector mismatch");
+				crypto_bignum_deinit(pwe, 1);
+				sae_deinit_pt(pt);
+				goto fail;
+			}
+			crypto_bignum_deinit(pwe, 1);
+		}
+	}
+
+	sae_deinit_pt(pt_info);
+
 	ret = 0;
 fail:
 	sae_clear_data(&sae);
diff --git a/src/common/defs.h b/src/common/defs.h
index 4faf1c8..e2fa4b2 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -416,6 +416,10 @@
 	CHAN_WIDTH_80,
 	CHAN_WIDTH_80P80,
 	CHAN_WIDTH_160,
+	CHAN_WIDTH_2160,
+	CHAN_WIDTH_4320,
+	CHAN_WIDTH_6480,
+	CHAN_WIDTH_8640,
 	CHAN_WIDTH_UNKNOWN
 };
 
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 49de476..7542c66 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -8,6 +8,7 @@
  */
 
 #include "utils/includes.h"
+#include <fcntl.h>
 #include <openssl/opensslv.h>
 #include <openssl/err.h>
 #include <openssl/asn1.h>
@@ -16,6 +17,8 @@
 #include "utils/common.h"
 #include "utils/base64.h"
 #include "utils/json.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
 #include "common/ieee802_11_common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
@@ -30,6 +33,8 @@
 #include "dpp.h"
 
 
+static const char * dpp_netrole_str(enum dpp_netrole netrole);
+
 #ifdef CONFIG_TESTING_OPTIONS
 enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
 u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
@@ -70,9 +75,62 @@
 #endif
 
 
+struct dpp_connection {
+	struct dl_list list;
+	struct dpp_controller *ctrl;
+	struct dpp_relay_controller *relay;
+	struct dpp_global *global;
+	struct dpp_authentication *auth;
+	int sock;
+	u8 mac_addr[ETH_ALEN];
+	unsigned int freq;
+	u8 msg_len[4];
+	size_t msg_len_octets;
+	struct wpabuf *msg;
+	struct wpabuf *msg_out;
+	size_t msg_out_pos;
+	unsigned int read_eloop:1;
+	unsigned int write_eloop:1;
+	unsigned int on_tcp_tx_complete_gas_done:1;
+	unsigned int on_tcp_tx_complete_remove:1;
+	unsigned int on_tcp_tx_complete_auth_ok:1;
+};
+
+/* Remote Controller */
+struct dpp_relay_controller {
+	struct dl_list list;
+	struct dpp_global *global;
+	u8 pkhash[SHA256_MAC_LEN];
+	struct hostapd_ip_addr ipaddr;
+	void *cb_ctx;
+	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
+		   size_t len);
+	void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
+			    int prot, struct wpabuf *buf);
+	struct dl_list conn; /* struct dpp_connection */
+};
+
+/* Local Controller */
+struct dpp_controller {
+	struct dpp_global *global;
+	u8 allowed_roles;
+	int qr_mutual;
+	int sock;
+	struct dl_list conn; /* struct dpp_connection */
+	char *configurator_params;
+};
+
 struct dpp_global {
+	void *msg_ctx;
 	struct dl_list bootstrap; /* struct dpp_bootstrap_info */
 	struct dl_list configurator; /* struct dpp_configurator */
+#ifdef CONFIG_DPP2
+	struct dl_list controllers; /* struct dpp_relay_controller */
+	struct dpp_controller *controller;
+	struct dl_list tcp_init; /* struct dpp_connection */
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+#endif /* CONFIG_DPP2 */
 };
 
 static const struct dpp_curve_params dpp_curves[] = {
@@ -554,6 +612,91 @@
 }
 
 
+static int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer,
+		    u8 *secret, size_t *secret_len)
+{
+	EVP_PKEY_CTX *ctx;
+	int ret = -1;
+
+	ERR_clear_error();
+	*secret_len = 0;
+
+	ctx = EVP_PKEY_CTX_new(own, NULL);
+	if (!ctx) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+
+	if (EVP_PKEY_derive_init(ctx) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: EVP_PKEY_derive_set_peet failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+		u8 buf[200];
+		int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
+
+		/* It looks like OpenSSL can return unexpectedly large buffer
+		 * need for shared secret from EVP_PKEY_derive(NULL) in some
+		 * cases. For example, group 19 has shown cases where secret_len
+		 * is set to 72 even though the actual length ends up being
+		 * updated to 32 when EVP_PKEY_derive() is called with a buffer
+		 * for the value. Work around this by trying to fetch the value
+		 * and continue if it is within supported range even when the
+		 * initial buffer need is claimed to be larger. */
+		wpa_printf(level,
+			   "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+			   (int) *secret_len);
+		if (*secret_len > 200)
+			goto fail;
+		if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
+			wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			goto fail;
+		}
+		if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+				   (int) *secret_len);
+			goto fail;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
+				buf, *secret_len);
+		os_memcpy(secret, buf, *secret_len);
+		forced_memzero(buf, sizeof(buf));
+		goto done;
+	}
+
+	if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+done:
+	ret = 0;
+
+fail:
+	EVP_PKEY_CTX_free(ctx);
+	return ret;
+}
+
+
 static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
 {
 	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
@@ -601,6 +744,34 @@
 }
 
 
+static const u8 * dpp_get_attr_next(const u8 *prev, const u8 *buf, size_t len,
+				    u16 req_id, u16 *ret_len)
+{
+	u16 id, alen;
+	const u8 *pos, *end = buf + len;
+
+	if (!prev)
+		pos = buf;
+	else
+		pos = prev + WPA_GET_LE16(prev - 2);
+	while (end - pos >= 4) {
+		id = WPA_GET_LE16(pos);
+		pos += 2;
+		alen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (alen > end - pos)
+			return NULL;
+		if (id == req_id) {
+			*ret_len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
 int dpp_check_attrs(const u8 *buf, size_t len)
 {
 	const u8 *pos, *end;
@@ -661,6 +832,8 @@
 		return "QRCODE";
 	case DPP_BOOTSTRAP_PKEX:
 		return "PKEX";
+	case DPP_BOOTSTRAP_NFC_URI:
+		return "NFC-URI";
 	}
 	return "??";
 }
@@ -689,17 +862,19 @@
 int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
 			    const char *chan_list)
 {
-	const char *pos = chan_list;
-	int opclass, channel, freq;
+	const char *pos = chan_list, *pos2;
+	int opclass = -1, channel, freq;
 
 	while (pos && *pos && *pos != ';') {
-		opclass = atoi(pos);
+		pos2 = pos;
+		while (*pos2 >= '0' && *pos2 <= '9')
+			pos2++;
+		if (*pos2 == '/') {
+			opclass = atoi(pos);
+			pos = pos2 + 1;
+		}
 		if (opclass <= 0)
 			goto fail;
-		pos = os_strchr(pos, '/');
-		if (!pos)
-			goto fail;
-		pos++;
 		channel = atoi(pos);
 		if (channel <= 0)
 			goto fail;
@@ -836,8 +1011,7 @@
 	if (!end)
 		return -1;
 
-	data = base64_decode((const unsigned char *) info, end - info,
-			     &data_len);
+	data = base64_decode(info, end - info, &data_len);
 	if (!data) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Invalid base64 encoding on URI public-key");
@@ -1011,17 +1185,6 @@
 }
 
 
-struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
-{
-	struct dpp_bootstrap_info *bi;
-
-	bi = dpp_parse_uri(uri);
-	if (bi)
-		bi->type = DPP_BOOTSTRAP_QR_CODE;
-	return bi;
-}
-
-
 static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
 {
 	EC_KEY *eckey;
@@ -1079,7 +1242,7 @@
 static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
 {
 	EVP_PKEY_CTX *kctx = NULL;
-	EC_KEY *ec_params;
+	EC_KEY *ec_params = NULL;
 	EVP_PKEY *params = NULL, *key = NULL;
 	int nid;
 
@@ -1110,19 +1273,18 @@
 	    EVP_PKEY_keygen_init(kctx) != 1 ||
 	    EVP_PKEY_keygen(kctx, &key) != 1) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+		key = NULL;
 		goto fail;
 	}
 
 	if (wpa_debug_show_keys)
 		dpp_debug_print_key("Own generated key", key);
 
+fail:
+	EC_KEY_free(ec_params);
 	EVP_PKEY_free(params);
 	EVP_PKEY_CTX_free(kctx);
 	return key;
-fail:
-	EVP_PKEY_CTX_free(kctx);
-	EVP_PKEY_free(params);
-	return NULL;
 }
 
 
@@ -1312,7 +1474,7 @@
 char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
 		  const u8 *privkey, size_t privkey_len)
 {
-	unsigned char *base64 = NULL;
+	char *base64 = NULL;
 	char *pos, *end;
 	size_t len;
 	struct wpabuf *der = NULL;
@@ -1358,7 +1520,7 @@
 	der = NULL;
 	if (!base64)
 		goto fail;
-	pos = (char *) base64;
+	pos = base64;
 	end = pos + len;
 	for (;;) {
 		pos = os_strchr(pos, '\n');
@@ -1366,7 +1528,7 @@
 			break;
 		os_memmove(pos, pos + 1, end - pos);
 	}
-	return (char *) base64;
+	return base64;
 fail:
 	os_free(base64);
 	wpabuf_free(der);
@@ -2085,7 +2247,6 @@
 {
 	struct dpp_authentication *auth;
 	size_t nonce_len;
-	EVP_PKEY_CTX *ctx = NULL;
 	size_t secret_len;
 	struct wpabuf *pi = NULL;
 	const u8 *r_pubkey_hash, *i_pubkey_hash;
@@ -2154,21 +2315,10 @@
 		goto fail;
 
 	/* ECDH: M = pI * BR */
-	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
-	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
+		     auth->Mx, &secret_len) < 0)
 		goto fail;
-	}
 	auth->secret_len = secret_len;
-	EVP_PKEY_CTX_free(ctx);
-	ctx = NULL;
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
 			auth->Mx, auth->secret_len);
@@ -2220,7 +2370,6 @@
 
 out:
 	wpabuf_free(pi);
-	EVP_PKEY_CTX_free(ctx);
 	return auth;
 fail:
 	dpp_auth_deinit(auth);
@@ -2247,7 +2396,7 @@
 	}
 	wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
 	json_len = os_strlen(json);
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: configRequest JSON", json, json_len);
 
 	/* { E-nonce, configAttrib }ke */
 	clear_len = 4 + nonce_len + 4 + json_len;
@@ -2383,6 +2532,66 @@
 }
 
 
+struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
+					  const char *name,
+					  enum dpp_netrole netrole,
+					  const char *mud_url, int *opclasses)
+{
+	size_t len, name_len;
+	const char *tech = "infra";
+	const char *dpp_name;
+	struct wpabuf *buf, *json;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) {
+		static const char *bogus_tech = "knfra";
+
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr");
+		tech = bogus_tech;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	dpp_name = name ? name : "Test";
+	name_len = os_strlen(dpp_name);
+
+	len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
+	if (mud_url && mud_url[0])
+		len += 10 + os_strlen(mud_url);
+	json = wpabuf_alloc(len);
+	if (!json)
+		return NULL;
+
+	json_start_object(json, NULL);
+	if (json_add_string_escape(json, "name", dpp_name, name_len) < 0) {
+		wpabuf_free(json);
+		return NULL;
+	}
+	json_value_sep(json);
+	json_add_string(json, "wi-fi_tech", tech);
+	json_value_sep(json);
+	json_add_string(json, "netRole", dpp_netrole_str(netrole));
+	if (mud_url && mud_url[0]) {
+		json_value_sep(json);
+		json_add_string(json, "mudurl", mud_url);
+	}
+	if (opclasses) {
+		int i;
+
+		json_value_sep(json);
+		json_start_array(json, "bandSupport");
+		for (i = 0; opclasses[i]; i++)
+			wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]);
+		json_end_array(json);
+	}
+	json_end_object(json);
+
+	buf = dpp_build_conf_req(auth, wpabuf_head(json));
+	wpabuf_free(json);
+
+	return buf;
+}
+
+
 static void dpp_auth_success(struct dpp_authentication *auth)
 {
 	wpa_printf(MSG_DEBUG,
@@ -2693,7 +2902,6 @@
 static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
 {
 	size_t nonce_len;
-	EVP_PKEY_CTX *ctx = NULL;
 	size_t secret_len;
 	struct wpabuf *msg, *pr = NULL;
 	u8 r_auth[4 + DPP_MAX_HASH_LEN];
@@ -2732,6 +2940,7 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
 
+	EVP_PKEY_free(auth->own_protocol_key);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_protocol_key_override_len) {
 		const struct dpp_curve_params *tmp_curve;
@@ -2755,20 +2964,9 @@
 		goto fail;
 
 	/* ECDH: N = pR * PI */
-	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
-	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
+		     auth->Nx, &secret_len) < 0)
 		goto fail;
-	}
-	EVP_PKEY_CTX_free(ctx);
-	ctx = NULL;
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
 			auth->Nx, auth->secret_len);
@@ -3064,22 +3262,9 @@
 	}
 	dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
 
-	ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
-	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+	if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
 		goto fail;
-	}
 	auth->secret_len = secret_len;
-	EVP_PKEY_CTX_free(ctx);
-	ctx = NULL;
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
 			auth->Mx, auth->secret_len);
@@ -3533,7 +3718,6 @@
 		 const u8 *attr_start, size_t attr_len)
 {
 	EVP_PKEY *pr;
-	EVP_PKEY_CTX *ctx = NULL;
 	size_t secret_len;
 	const u8 *addr[2];
 	size_t len[2];
@@ -3683,21 +3867,11 @@
 	}
 	dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
 
-	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
-	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
 		dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
 		goto fail;
 	}
-	EVP_PKEY_CTX_free(ctx);
-	ctx = NULL;
+	EVP_PKEY_free(auth->peer_protocol_key);
 	auth->peer_protocol_key = pr;
 	pr = NULL;
 
@@ -3868,7 +4042,6 @@
 	bin_clear_free(unwrapped, unwrapped_len);
 	bin_clear_free(unwrapped2, unwrapped2_len);
 	EVP_PKEY_free(pr);
-	EVP_PKEY_CTX_free(ctx);
 	return NULL;
 }
 
@@ -4206,8 +4379,8 @@
 }
 
 
-static int dpp_configuration_parse(struct dpp_authentication *auth,
-				   const char *cmd)
+static int dpp_configuration_parse_helper(struct dpp_authentication *auth,
+					  const char *cmd, int idx)
 {
 	const char *pos, *end;
 	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
@@ -4218,6 +4391,7 @@
 		conf_sta = dpp_configuration_alloc(pos + 10);
 		if (!conf_sta)
 			goto fail;
+		conf_sta->netrole = DPP_NETROLE_STA;
 		conf = conf_sta;
 	}
 
@@ -4226,6 +4400,7 @@
 		conf_ap = dpp_configuration_alloc(pos + 9);
 		if (!conf_ap)
 			goto fail;
+		conf_ap->netrole = DPP_NETROLE_AP;
 		conf = conf_ap;
 	}
 
@@ -4251,6 +4426,16 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 	}
 
+	pos = os_strstr(cmd, " ssid_charset=");
+	if (pos) {
+		if (conf_ap) {
+			wpa_printf(MSG_INFO,
+				   "DPP: ssid64 option (ssid_charset param) not allowed for AP enrollee");
+			goto fail;
+		}
+		conf->ssid_charset = atoi(pos + 14);
+	}
+
 	pos = os_strstr(cmd, " pass=");
 	if (pos) {
 		size_t pass_len;
@@ -4303,8 +4488,15 @@
 	if (!dpp_configuration_valid(conf))
 		goto fail;
 
-	auth->conf_sta = conf_sta;
-	auth->conf_ap = conf_ap;
+	if (idx == 0) {
+		auth->conf_sta = conf_sta;
+		auth->conf_ap = conf_ap;
+	} else if (idx == 1) {
+		auth->conf2_sta = conf_sta;
+		auth->conf2_ap = conf_ap;
+	} else {
+		goto fail;
+	}
 	return 0;
 
 fail:
@@ -4314,6 +4506,41 @@
 }
 
 
+static int dpp_configuration_parse(struct dpp_authentication *auth,
+				   const char *cmd)
+{
+	const char *pos;
+	char *tmp;
+	size_t len;
+	int res;
+
+	pos = os_strstr(cmd, " @CONF-OBJ-SEP@ ");
+	if (!pos)
+		return dpp_configuration_parse_helper(auth, cmd, 0);
+
+	len = pos - cmd;
+	tmp = os_malloc(len + 1);
+	if (!tmp)
+		goto fail;
+	os_memcpy(tmp, cmd, len);
+	tmp[len] = '\0';
+	res = dpp_configuration_parse_helper(auth, cmd, 0);
+	str_clear_free(tmp);
+	if (res)
+		goto fail;
+	res = dpp_configuration_parse_helper(auth, cmd + len, 1);
+	if (res)
+		goto fail;
+	return 0;
+fail:
+	dpp_configuration_free(auth->conf_sta);
+	dpp_configuration_free(auth->conf2_sta);
+	dpp_configuration_free(auth->conf_ap);
+	dpp_configuration_free(auth->conf2_ap);
+	return -1;
+}
+
+
 static struct dpp_configurator *
 dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id)
 {
@@ -4353,6 +4580,18 @@
 		}
 	}
 
+	pos = os_strstr(cmd, " conn_status=");
+	if (pos) {
+		pos += 13;
+		auth->send_conn_status = atoi(pos);
+	}
+
+	pos = os_strstr(cmd, " akm_use_selector=");
+	if (pos) {
+		pos += 18;
+		auth->akm_use_selector = atoi(pos);
+	}
+
 	if (dpp_configuration_parse(auth, cmd) < 0) {
 		wpa_msg(msg_ctx, MSG_INFO,
 			"DPP: Failed to set configurator parameters");
@@ -4364,18 +4603,26 @@
 
 void dpp_auth_deinit(struct dpp_authentication *auth)
 {
+	unsigned int i;
+
 	if (!auth)
 		return;
 	dpp_configuration_free(auth->conf_ap);
+	dpp_configuration_free(auth->conf2_ap);
 	dpp_configuration_free(auth->conf_sta);
+	dpp_configuration_free(auth->conf2_sta);
 	EVP_PKEY_free(auth->own_protocol_key);
 	EVP_PKEY_free(auth->peer_protocol_key);
 	wpabuf_free(auth->req_msg);
 	wpabuf_free(auth->resp_msg);
 	wpabuf_free(auth->conf_req);
-	os_free(auth->connector);
+	for (i = 0; i < auth->num_conf_obj; i++) {
+		struct dpp_config_obj *conf = &auth->conf_obj[i];
+
+		os_free(conf->connector);
+		wpabuf_free(conf->c_sign_key);
+	}
 	wpabuf_free(auth->net_access_key);
-	wpabuf_free(auth->c_sign_key);
 	dpp_bootstrap_info_free(auth->tmp_own_bi);
 #ifdef CONFIG_TESTING_OPTIONS
 	os_free(auth->config_obj_override);
@@ -4391,7 +4638,6 @@
 		     struct dpp_configuration *conf, size_t tailroom)
 {
 	struct wpabuf *buf;
-	char ssid[6 * sizeof(conf->ssid) + 1];
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->discovery_override)
@@ -4401,21 +4647,35 @@
 	buf = wpabuf_alloc(200 + tailroom);
 	if (!buf)
 		return NULL;
-	wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
+	json_start_object(buf, NULL);
+	json_add_string(buf, "wi-fi_tech", "infra");
+	json_value_sep(buf);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->discovery_override) {
 		wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
 			   auth->discovery_override);
+		wpabuf_put_str(buf, "\"discovery\":");
 		wpabuf_put_str(buf, auth->discovery_override);
-		wpabuf_put_u8(buf, ',');
+		json_value_sep(buf);
 		return buf;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	wpabuf_put_str(buf, "{\"ssid\":\"");
-	json_escape_string(ssid, sizeof(ssid),
-			   (const char *) conf->ssid, conf->ssid_len);
-	wpabuf_put_str(buf, ssid);
-	wpabuf_put_str(buf, "\"},");
+	json_start_object(buf, "discovery");
+	if (((!conf->ssid_charset || auth->peer_version < 2) &&
+	     json_add_string_escape(buf, "ssid", conf->ssid,
+				    conf->ssid_len) < 0) ||
+	    ((conf->ssid_charset && auth->peer_version >= 2) &&
+	     json_add_base64url(buf, "ssid64", conf->ssid,
+				conf->ssid_len) < 0)) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+	if (conf->ssid_charset > 0) {
+		json_value_sep(buf);
+		json_add_int(buf, "ssid_charset", conf->ssid_charset);
+	}
+	json_end_object(buf);
+	json_value_sep(buf);
 
 	return buf;
 }
@@ -4426,37 +4686,32 @@
 {
 	struct wpabuf *pub;
 	const u8 *pos;
-	char *x = NULL, *y = NULL;
 	int ret = -1;
 
 	pub = dpp_get_pubkey_point(key, 0);
 	if (!pub)
 		goto fail;
-	pos = wpabuf_head(pub);
-	x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
-	pos += curve->prime_len;
-	y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
-	if (!x || !y)
-		goto fail;
 
-	wpabuf_put_str(buf, "\"");
-	wpabuf_put_str(buf, name);
-	wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
-	wpabuf_put_str(buf, curve->jwk_crv);
-	wpabuf_put_str(buf, "\",\"x\":\"");
-	wpabuf_put_str(buf, x);
-	wpabuf_put_str(buf, "\",\"y\":\"");
-	wpabuf_put_str(buf, y);
+	json_start_object(buf, name);
+	json_add_string(buf, "kty", "EC");
+	json_value_sep(buf);
+	json_add_string(buf, "crv", curve->jwk_crv);
+	json_value_sep(buf);
+	pos = wpabuf_head(pub);
+	if (json_add_base64url(buf, "x", pos, curve->prime_len) < 0)
+		goto fail;
+	json_value_sep(buf);
+	pos += curve->prime_len;
+	if (json_add_base64url(buf, "y", pos, curve->prime_len) < 0)
+		goto fail;
 	if (kid) {
-		wpabuf_put_str(buf, "\",\"kid\":\"");
-		wpabuf_put_str(buf, kid);
+		json_value_sep(buf);
+		json_add_string(buf, "kid", kid);
 	}
-	wpabuf_put_str(buf, "\"}");
+	json_end_object(buf);
 	ret = 0;
 fail:
 	wpabuf_free(pub);
-	os_free(x);
-	os_free(y);
 	return ret;
 }
 
@@ -4465,36 +4720,43 @@
 					 struct dpp_configuration *conf)
 {
 	if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
-		char pass[63 * 6 + 1];
-
-		json_escape_string(pass, sizeof(pass), conf->passphrase,
-				   os_strlen(conf->passphrase));
-		wpabuf_put_str(buf, "\"pass\":\"");
-		wpabuf_put_str(buf, pass);
-		wpabuf_put_str(buf, "\"");
-		os_memset(pass, 0, sizeof(pass));
+		json_add_string_escape(buf, "pass", conf->passphrase,
+				       os_strlen(conf->passphrase));
 	} else if (conf->psk_set) {
 		char psk[2 * sizeof(conf->psk) + 1];
 
 		wpa_snprintf_hex(psk, sizeof(psk),
 				 conf->psk, sizeof(conf->psk));
-		wpabuf_put_str(buf, "\"psk_hex\":\"");
-		wpabuf_put_str(buf, psk);
-		wpabuf_put_str(buf, "\"");
-		os_memset(psk, 0, sizeof(psk));
+		json_add_string(buf, "psk_hex", psk);
+		forced_memzero(psk, sizeof(psk));
+	}
+}
+
+
+static const char * dpp_netrole_str(enum dpp_netrole netrole)
+{
+	switch (netrole) {
+	case DPP_NETROLE_STA:
+		return "sta";
+	case DPP_NETROLE_AP:
+		return "ap";
+	case DPP_NETROLE_CONFIGURATOR:
+		return "configurator";
+	default:
+		return "??";
 	}
 }
 
 
 static struct wpabuf *
-dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
+dpp_build_conf_obj_dpp(struct dpp_authentication *auth,
 		       struct dpp_configuration *conf)
 {
 	struct wpabuf *buf = NULL;
 	char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
 	size_t tailroom;
 	const struct dpp_curve_params *curve;
-	char jws_prot_hdr[100];
+	struct wpabuf *jws_prot_hdr;
 	size_t signed1_len, signed2_len, signed3_len;
 	struct wpabuf *dppcon = NULL;
 	unsigned char *signature = NULL;
@@ -4508,6 +4770,7 @@
 	size_t extra_len = 1000;
 	int incl_legacy;
 	enum dpp_akm akm;
+	const char *akm_str;
 
 	if (!auth->conf) {
 		wpa_printf(MSG_INFO,
@@ -4554,14 +4817,21 @@
 				   auth->groups_override);
 			wpabuf_put_str(dppcon, "\"groups\":");
 			wpabuf_put_str(dppcon, auth->groups_override);
-			wpabuf_put_u8(dppcon, ',');
+			json_value_sep(dppcon);
 		}
 		goto skip_groups;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",",
-		      conf->group_id ? conf->group_id : "*");
-	wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
+	json_start_object(dppcon, NULL);
+	json_start_array(dppcon, "groups");
+	json_start_object(dppcon, NULL);
+	json_add_string(dppcon, "groupId",
+			conf->group_id ? conf->group_id : "*");
+	json_value_sep(dppcon);
+	json_add_string(dppcon, "netRole", dpp_netrole_str(conf->netrole));
+	json_end_object(dppcon);
+	json_end_array(dppcon);
+	json_value_sep(dppcon);
 #ifdef CONFIG_TESTING_OPTIONS
 skip_groups:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -4572,30 +4842,40 @@
 	}
 	if (conf->netaccesskey_expiry) {
 		struct os_tm tm;
+		char expiry[30];
 
 		if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Failed to generate expiry string");
 			goto fail;
 		}
-		wpabuf_printf(dppcon,
-			      ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
-			      tm.year, tm.month, tm.day,
-			      tm.hour, tm.min, tm.sec);
+		os_snprintf(expiry, sizeof(expiry),
+			    "%04u-%02u-%02uT%02u:%02u:%02uZ",
+			    tm.year, tm.month, tm.day,
+			    tm.hour, tm.min, tm.sec);
+		json_value_sep(dppcon);
+		json_add_string(dppcon, "expiry", expiry);
 	}
-	wpabuf_put_u8(dppcon, '}');
+	json_end_object(dppcon);
 	wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
 		   (const char *) wpabuf_head(dppcon));
 
-	os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
-		    "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
-		    auth->conf->kid, curve->jws_alg);
-	signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
-					     os_strlen(jws_prot_hdr),
-					     &signed1_len, 0);
-	signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
-					     wpabuf_len(dppcon),
-					     &signed2_len, 0);
+	jws_prot_hdr = wpabuf_alloc(100);
+	if (!jws_prot_hdr)
+		goto fail;
+	json_start_object(jws_prot_hdr, NULL);
+	json_add_string(jws_prot_hdr, "typ", "dppCon");
+	json_value_sep(jws_prot_hdr);
+	json_add_string(jws_prot_hdr, "kid", auth->conf->kid);
+	json_value_sep(jws_prot_hdr);
+	json_add_string(jws_prot_hdr, "alg", curve->jws_alg);
+	json_end_object(jws_prot_hdr);
+	signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
+				    wpabuf_len(jws_prot_hdr),
+				    &signed1_len);
+	wpabuf_free(jws_prot_hdr);
+	signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
+				    &signed2_len);
 	if (!signed1 || !signed2)
 		goto fail;
 
@@ -4645,8 +4925,7 @@
 	signature_len = 2 * curve->prime_len;
 	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
 		    signature, signature_len);
-	signed3 = (char *) base64_url_encode(signature, signature_len,
-					     &signed3_len, 0);
+	signed3 = base64_url_encode(signature, signature_len, &signed3_len);
 	if (!signed3)
 		goto fail;
 
@@ -4660,10 +4939,16 @@
 	if (!buf)
 		goto fail;
 
-	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm));
+	if (auth->akm_use_selector && dpp_akm_ver2(akm))
+		akm_str = dpp_akm_selector_str(akm);
+	else
+		akm_str = dpp_akm_str(akm);
+	json_start_object(buf, "cred");
+	json_add_string(buf, "akm", akm_str);
+	json_value_sep(buf);
 	if (incl_legacy) {
 		dpp_build_legacy_cred_params(buf, conf);
-		wpabuf_put_str(buf, ",");
+		json_value_sep(buf);
 	}
 	wpabuf_put_str(buf, "\"signedConnector\":\"");
 	wpabuf_put_str(buf, signed1);
@@ -4671,14 +4956,16 @@
 	wpabuf_put_str(buf, signed2);
 	wpabuf_put_u8(buf, '.');
 	wpabuf_put_str(buf, signed3);
-	wpabuf_put_str(buf, "\",");
+	wpabuf_put_str(buf, "\"");
+	json_value_sep(buf);
 	if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
 			  curve) < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
 		goto fail;
 	}
 
-	wpabuf_put_str(buf, "}}");
+	json_end_object(buf);
+	json_end_object(buf);
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
 			      wpabuf_head(buf), wpabuf_len(buf));
@@ -4701,18 +4988,26 @@
 
 
 static struct wpabuf *
-dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
+dpp_build_conf_obj_legacy(struct dpp_authentication *auth,
 			  struct dpp_configuration *conf)
 {
 	struct wpabuf *buf;
+	const char *akm_str;
 
 	buf = dpp_build_conf_start(auth, conf, 1000);
 	if (!buf)
 		return NULL;
 
-	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
+	if (auth->akm_use_selector && dpp_akm_ver2(conf->akm))
+		akm_str = dpp_akm_selector_str(conf->akm);
+	else
+		akm_str = dpp_akm_str(conf->akm);
+	json_start_object(buf, "cred");
+	json_add_string(buf, "akm", akm_str);
+	json_value_sep(buf);
 	dpp_build_legacy_cred_params(buf, conf);
-	wpabuf_put_str(buf, "}}");
+	json_end_object(buf);
+	json_end_object(buf);
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
 			      wpabuf_head(buf), wpabuf_len(buf));
@@ -4722,37 +5017,51 @@
 
 
 static struct wpabuf *
-dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
+dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole,
+		   int idx)
 {
-	struct dpp_configuration *conf;
+	struct dpp_configuration *conf = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->config_obj_override) {
+		if (idx != 0)
+			return NULL;
 		wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
 		return wpabuf_alloc_copy(auth->config_obj_override,
 					 os_strlen(auth->config_obj_override));
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	conf = ap ? auth->conf_ap : auth->conf_sta;
+	if (idx == 0) {
+		if (netrole == DPP_NETROLE_STA)
+			conf = auth->conf_sta;
+		else if (netrole == DPP_NETROLE_AP)
+			conf = auth->conf_ap;
+	} else if (idx == 1) {
+		if (netrole == DPP_NETROLE_STA)
+			conf = auth->conf2_sta;
+		else if (netrole == DPP_NETROLE_AP)
+			conf = auth->conf2_ap;
+	}
 	if (!conf) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No configuration available for Enrollee(%s) - reject configuration request",
-			   ap ? "ap" : "sta");
+		if (idx == 0)
+			wpa_printf(MSG_DEBUG,
+				   "DPP: No configuration available for Enrollee(%s) - reject configuration request",
+				   dpp_netrole_str(netrole));
 		return NULL;
 	}
 
 	if (dpp_akm_dpp(conf->akm))
-		return dpp_build_conf_obj_dpp(auth, ap, conf);
-	return dpp_build_conf_obj_legacy(auth, ap, conf);
+		return dpp_build_conf_obj_dpp(auth, conf);
+	return dpp_build_conf_obj_legacy(auth, conf);
 }
 
 
 static struct wpabuf *
 dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
-		    u16 e_nonce_len, int ap)
+		    u16 e_nonce_len, enum dpp_netrole netrole)
 {
-	struct wpabuf *conf;
+	struct wpabuf *conf, *conf2 = NULL;
 	size_t clear_len, attr_len;
 	struct wpabuf *clear = NULL, *msg = NULL;
 	u8 *wrapped;
@@ -4760,18 +5069,24 @@
 	size_t len[1];
 	enum dpp_status_error status;
 
-	conf = dpp_build_conf_obj(auth, ap);
+	conf = dpp_build_conf_obj(auth, netrole, 0);
 	if (conf) {
 		wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
 				  wpabuf_head(conf), wpabuf_len(conf));
+		conf2 = dpp_build_conf_obj(auth, netrole, 1);
 	}
 	status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
 	auth->conf_resp_status = status;
 
-	/* { E-nonce, configurationObject}ke */
+	/* { E-nonce, configurationObject[, sendConnStatus]}ke */
 	clear_len = 4 + e_nonce_len;
 	if (conf)
 		clear_len += 4 + wpabuf_len(conf);
+	if (conf2)
+		clear_len += 4 + wpabuf_len(conf2);
+	if (auth->peer_version >= 2 && auth->send_conn_status &&
+	    netrole == DPP_NETROLE_STA)
+		clear_len += 4;
 	clear = wpabuf_alloc(clear_len);
 	attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -4819,6 +5134,21 @@
 		wpabuf_put_le16(clear, wpabuf_len(conf));
 		wpabuf_put_buf(clear, conf);
 	}
+	if (auth->peer_version >= 2 && conf2) {
+		wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
+		wpabuf_put_le16(clear, wpabuf_len(conf2));
+		wpabuf_put_buf(clear, conf2);
+	} else if (conf2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Second Config Object available, but peer does not support more than one");
+	}
+
+	if (auth->peer_version >= 2 && auth->send_conn_status &&
+	    netrole == DPP_NETROLE_STA) {
+		wpa_printf(MSG_DEBUG, "DPP: sendConnStatus");
+		wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS);
+		wpabuf_put_le16(clear, 0);
+	}
 
 #ifdef CONFIG_TESTING_OPTIONS
 skip_config_obj:
@@ -4867,6 +5197,7 @@
 			"DPP: Configuration Response attributes", msg);
 out:
 	wpabuf_free(conf);
+	wpabuf_free(conf2);
 	wpabuf_free(clear);
 
 	return msg;
@@ -4887,7 +5218,7 @@
 	size_t unwrapped_len = 0;
 	struct wpabuf *resp = NULL;
 	struct json_token *root = NULL, *token;
-	int ap;
+	enum dpp_netrole netrole;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
@@ -4985,9 +5316,11 @@
 	}
 	wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
 	if (os_strcmp(token->string, "sta") == 0) {
-		ap = 0;
+		netrole = DPP_NETROLE_STA;
 	} else if (os_strcmp(token->string, "ap") == 0) {
-		ap = 1;
+		netrole = DPP_NETROLE_AP;
+	} else if (os_strcmp(token->string, "configurator") == 0) {
+		netrole = DPP_NETROLE_CONFIGURATOR;
 	} else {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
 			   token->string);
@@ -4995,7 +5328,33 @@
 		goto fail;
 	}
 
-	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
+	token = json_get_member(root, "mudurl");
+	if (token && token->type == JSON_STRING)
+		wpa_printf(MSG_DEBUG, "DPP: mudurl = '%s'", token->string);
+
+	token = json_get_member(root, "bandSupport");
+	auth->band_list_size = 0;
+	if (token && token->type == JSON_ARRAY) {
+		memset(auth->band_list, 0, sizeof(auth->band_list));
+		wpa_printf(MSG_DEBUG, "DPP: bandSupport");
+		token = token->child;
+		while (token) {
+			if (token->type != JSON_NUMBER) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Invalid bandSupport array member type");
+			} else {
+				if (auth->band_list_size < DPP_MAX_CHANNELS) {
+					auth->band_list[auth->band_list_size++] = token->number;
+				}
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Supported global operating class: %d",
+					   token->number);
+			}
+			token = token->sibling;
+		}
+	}
+
+	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole);
 
 fail:
 	json_free(root);
@@ -5084,7 +5443,7 @@
 }
 
 
-static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
+static int dpp_parse_cred_legacy(struct dpp_config_obj *conf,
 				 struct json_token *cred)
 {
 	struct json_token *pass, *psk_hex;
@@ -5101,28 +5460,28 @@
 				      pass->string, len);
 		if (len < 8 || len > 63)
 			return -1;
-		os_strlcpy(auth->passphrase, pass->string,
-			   sizeof(auth->passphrase));
+		os_strlcpy(conf->passphrase, pass->string,
+			   sizeof(conf->passphrase));
 	} else if (psk_hex && psk_hex->type == JSON_STRING) {
-		if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) {
+		if (dpp_akm_sae(conf->akm) && !dpp_akm_psk(conf->akm)) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Unexpected psk_hex with akm=sae");
 			return -1;
 		}
 		if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
-		    hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
+		    hexstr2bin(psk_hex->string, conf->psk, PMK_LEN) < 0) {
 			wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
 			return -1;
 		}
 		wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
-				auth->psk, PMK_LEN);
-		auth->psk_set = 1;
+				conf->psk, PMK_LEN);
+		conf->psk_set = 1;
 	} else {
 		wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
 		return -1;
 	}
 
-	if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) {
+	if (dpp_akm_sae(conf->akm) && !conf->passphrase[0]) {
 		wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
 		return -1;
 	}
@@ -5199,6 +5558,7 @@
 
 	pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
 					  wpabuf_len(x));
+	EC_GROUP_free(group);
 	*key_curve = curve;
 
 fail:
@@ -5289,6 +5649,7 @@
 
 
 static int dpp_parse_connector(struct dpp_authentication *auth,
+			       struct dpp_config_obj *conf,
 			       const unsigned char *payload,
 			       u16 payload_len)
 {
@@ -5416,7 +5777,7 @@
 }
 
 
-static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
+static void dpp_copy_csign(struct dpp_config_obj *conf, EVP_PKEY *csign)
 {
 	unsigned char *der = NULL;
 	int der_len;
@@ -5424,13 +5785,14 @@
 	der_len = i2d_PUBKEY(csign, &der);
 	if (der_len <= 0)
 		return;
-	wpabuf_free(auth->c_sign_key);
-	auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
+	wpabuf_free(conf->c_sign_key);
+	conf->c_sign_key = wpabuf_alloc_copy(der, der_len);
 	OPENSSL_free(der);
 }
 
 
-static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
+static void dpp_copy_netaccesskey(struct dpp_authentication *auth,
+				  struct dpp_config_obj *conf)
 {
 	unsigned char *der = NULL;
 	int der_len;
@@ -5498,8 +5860,7 @@
 		ret = DPP_STATUS_INVALID_CONNECTOR;
 		goto fail;
 	}
-	prot_hdr = base64_url_decode((const unsigned char *) pos,
-				     end - pos, &prot_hdr_len);
+	prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
 	if (!prot_hdr) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode signedConnector JWS Protected Header");
@@ -5531,8 +5892,7 @@
 		goto fail;
 	}
 	signed_end = end - 1;
-	info->payload = base64_url_decode((const unsigned char *) pos,
-					  end - pos, &info->payload_len);
+	info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
 	if (!info->payload) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode signedConnector JWS Payload");
@@ -5543,8 +5903,7 @@
 			  "DPP: signedConnector - JWS Payload",
 			  info->payload, info->payload_len);
 	pos = end + 1;
-	signature = base64_url_decode((const unsigned char *) pos,
-				      os_strlen(pos), &signature_len);
+	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
 	if (!signature) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode signedConnector signature");
@@ -5624,6 +5983,7 @@
 
 
 static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
+			      struct dpp_config_obj *conf,
 			      struct json_token *cred)
 {
 	struct dpp_signed_connector_info info;
@@ -5635,10 +5995,10 @@
 
 	os_memset(&info, 0, sizeof(info));
 
-	if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) {
+	if (dpp_akm_psk(conf->akm) || dpp_akm_sae(conf->akm)) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Legacy credential included in Connector credential");
-		if (dpp_parse_cred_legacy(auth, cred) < 0)
+		if (dpp_parse_cred_legacy(conf, cred) < 0)
 			return -1;
 	}
 
@@ -5677,16 +6037,17 @@
 					 signed_connector) != DPP_STATUS_OK)
 		goto fail;
 
-	if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
+	if (dpp_parse_connector(auth, conf,
+				info.payload, info.payload_len) < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
 		goto fail;
 	}
 
-	os_free(auth->connector);
-	auth->connector = os_strdup(signed_connector);
+	os_free(conf->connector);
+	conf->connector = os_strdup(signed_connector);
 
-	dpp_copy_csign(auth, csign_pub);
-	dpp_copy_netaccesskey(auth);
+	dpp_copy_csign(conf, csign_pub);
+	dpp_copy_netaccesskey(auth, conf);
 
 	ret = 0;
 fail:
@@ -5717,8 +6078,32 @@
 }
 
 
+const char * dpp_akm_selector_str(enum dpp_akm akm)
+{
+	switch (akm) {
+	case DPP_AKM_DPP:
+		return "506F9A02";
+	case DPP_AKM_PSK:
+		return "000FAC02+000FAC06";
+	case DPP_AKM_SAE:
+		return "000FAC08";
+	case DPP_AKM_PSK_SAE:
+		return "000FAC02+000FAC06+000FAC08";
+	case DPP_AKM_SAE_DPP:
+		return "506F9A02+000FAC08";
+	case DPP_AKM_PSK_SAE_DPP:
+		return "506F9A02+000FAC08+000FAC02+000FAC06";
+	default:
+		return "??";
+	}
+}
+
+
 static enum dpp_akm dpp_akm_from_str(const char *akm)
 {
+	const char *pos;
+	int dpp = 0, psk = 0, sae = 0;
+
 	if (os_strcmp(akm, "psk") == 0)
 		return DPP_AKM_PSK;
 	if (os_strcmp(akm, "sae") == 0)
@@ -5731,6 +6116,38 @@
 		return DPP_AKM_SAE_DPP;
 	if (os_strcmp(akm, "dpp+psk+sae") == 0)
 		return DPP_AKM_PSK_SAE_DPP;
+
+	pos = akm;
+	while (*pos) {
+		if (os_strlen(pos) < 8)
+			break;
+		if (os_strncasecmp(pos, "506F9A02", 8) == 0)
+			dpp = 1;
+		else if (os_strncasecmp(pos, "000FAC02", 8) == 0)
+			psk = 1;
+		else if (os_strncasecmp(pos, "000FAC06", 8) == 0)
+			psk = 1;
+		else if (os_strncasecmp(pos, "000FAC08", 8) == 0)
+			sae = 1;
+		pos += 8;
+		if (*pos != '+')
+			break;
+		pos++;
+	}
+
+	if (dpp && psk && sae)
+		return DPP_AKM_PSK_SAE_DPP;
+	if (dpp && sae)
+		return DPP_AKM_SAE_DPP;
+	if (dpp)
+		return DPP_AKM_DPP;
+	if (psk && sae)
+		return DPP_AKM_PSK_SAE;
+	if (sae)
+		return DPP_AKM_SAE;
+	if (psk)
+		return DPP_AKM_PSK;
+
 	return DPP_AKM_UNKNOWN;
 }
 
@@ -5740,6 +6157,8 @@
 {
 	int ret = -1;
 	struct json_token *root, *token, *discovery, *cred;
+	struct dpp_config_obj *conf;
+	struct wpabuf *ssid64 = NULL;
 
 	root = json_parse((const char *) conf_obj, conf_obj_len);
 	if (!root)
@@ -5767,19 +6186,52 @@
 		goto fail;
 	}
 
-	token = json_get_member(discovery, "ssid");
-	if (!token || token->type != JSON_STRING) {
-		dpp_auth_fail(auth, "No discovery::ssid string value found");
+	ssid64 = json_get_member_base64url(discovery, "ssid64");
+	if (ssid64) {
+		wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid64",
+				  wpabuf_head(ssid64), wpabuf_len(ssid64));
+		if (wpabuf_len(ssid64) > SSID_MAX_LEN) {
+			dpp_auth_fail(auth, "Too long discovery::ssid64 value");
+			goto fail;
+		}
+	} else {
+		token = json_get_member(discovery, "ssid");
+		if (!token || token->type != JSON_STRING) {
+			dpp_auth_fail(auth,
+				      "No discovery::ssid string value found");
+			goto fail;
+		}
+		wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
+				  token->string, os_strlen(token->string));
+		if (os_strlen(token->string) > SSID_MAX_LEN) {
+			dpp_auth_fail(auth,
+				      "Too long discovery::ssid string value");
+			goto fail;
+		}
+	}
+
+	if (auth->num_conf_obj == DPP_MAX_CONF_OBJ) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No room for this many Config Objects - ignore this one");
+		ret = 0;
 		goto fail;
 	}
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
-			  token->string, os_strlen(token->string));
-	if (os_strlen(token->string) > SSID_MAX_LEN) {
-		dpp_auth_fail(auth, "Too long discovery::ssid string value");
-		goto fail;
+	conf = &auth->conf_obj[auth->num_conf_obj++];
+
+	if (ssid64) {
+		conf->ssid_len = wpabuf_len(ssid64);
+		os_memcpy(conf->ssid, wpabuf_head(ssid64), conf->ssid_len);
+	} else {
+		conf->ssid_len = os_strlen(token->string);
+		os_memcpy(conf->ssid, token->string, conf->ssid_len);
 	}
-	auth->ssid_len = os_strlen(token->string);
-	os_memcpy(auth->ssid, token->string, auth->ssid_len);
+
+	token = json_get_member(discovery, "ssid_charset");
+	if (token && token->type == JSON_NUMBER) {
+		conf->ssid_charset = token->number;
+		wpa_printf(MSG_DEBUG, "DPP: ssid_charset=%d",
+			   conf->ssid_charset);
+	}
 
 	cred = json_get_member(root, "cred");
 	if (!cred || cred->type != JSON_OBJECT) {
@@ -5792,13 +6244,13 @@
 		dpp_auth_fail(auth, "No cred::akm string value found");
 		goto fail;
 	}
-	auth->akm = dpp_akm_from_str(token->string);
+	conf->akm = dpp_akm_from_str(token->string);
 
-	if (dpp_akm_legacy(auth->akm)) {
-		if (dpp_parse_cred_legacy(auth, cred) < 0)
+	if (dpp_akm_legacy(conf->akm)) {
+		if (dpp_parse_cred_legacy(conf, cred) < 0)
 			goto fail;
-	} else if (dpp_akm_dpp(auth->akm)) {
-		if (dpp_parse_cred_dpp(auth, cred) < 0)
+	} else if (dpp_akm_dpp(conf->akm)) {
+		if (dpp_parse_cred_dpp(auth, conf, cred) < 0)
 			goto fail;
 	} else {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
@@ -5810,6 +6262,7 @@
 	wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
 	ret = 0;
 fail:
+	wpabuf_free(ssid64);
 	json_free(root);
 	return ret;
 }
@@ -5895,17 +6348,32 @@
 		goto fail;
 	}
 
-	conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
-				DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
+	conf_obj = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIG_OBJ,
+				&conf_obj_len);
 	if (!conf_obj) {
 		dpp_auth_fail(auth,
 			      "Missing required Configuration Object attribute");
 		goto fail;
 	}
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
-			  conf_obj, conf_obj_len);
-	if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
-		goto fail;
+	while (conf_obj) {
+		wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+				  conf_obj, conf_obj_len);
+		if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
+			goto fail;
+		conf_obj = dpp_get_attr_next(conf_obj, unwrapped, unwrapped_len,
+					     DPP_ATTR_CONFIG_OBJ,
+					     &conf_obj_len);
+	}
+
+#ifdef CONFIG_DPP2
+	status = dpp_get_attr(unwrapped, unwrapped_len,
+			      DPP_ATTR_SEND_CONN_STATUS, &status_len);
+	if (status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Configurator requested connection status result");
+		auth->conn_status_requested = 1;
+	}
+#endif /* CONFIG_DPP2 */
 
 	ret = 0;
 
@@ -5916,6 +6384,7 @@
 
 
 #ifdef CONFIG_DPP2
+
 enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
 					 const u8 *hdr,
 					 const u8 *attr_start, size_t attr_len)
@@ -5996,7 +6465,6 @@
 	bin_clear_free(unwrapped, unwrapped_len);
 	return ret;
 }
-#endif /* CONFIG_DPP2 */
 
 
 struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
@@ -6014,7 +6482,7 @@
 	clear = wpabuf_alloc(clear_len);
 	msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len);
 	if (!clear || !msg)
-		return NULL;
+		goto fail;
 
 	/* DPP Status */
 	dpp_build_attr_status(clear, status);
@@ -6055,6 +6523,219 @@
 }
 
 
+static int valid_channel_list(const char *val)
+{
+	while (*val) {
+		if (!((*val >= '0' && *val <= '9') ||
+		      *val == '/' || *val == ','))
+			return 0;
+		val++;
+	}
+
+	return 1;
+}
+
+
+enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth,
+						const u8 *hdr,
+						const u8 *attr_start,
+						size_t attr_len,
+						u8 *ssid, size_t *ssid_len,
+						char **channel_list)
+{
+	const u8 *wrapped_data, *status, *e_nonce;
+	u16 wrapped_data_len, status_len, e_nonce_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	enum dpp_status_error ret = 256;
+	struct json_token *root = NULL, *token;
+	struct wpabuf *ssid64;
+
+	*ssid_len = 0;
+	*channel_list = NULL;
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required Wrapped Data attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	addr[0] = hdr;
+	len[0] = DPP_HDR_LEN;
+	addr[1] = attr_start;
+	len[1] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    2, addr, len, unwrapped) < 0) {
+		dpp_auth_fail(auth, "AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_ENROLLEE_NONCE,
+			       &e_nonce_len);
+	if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid Enrollee Nonce attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+	if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Enrollee Nonce mismatch");
+		wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce",
+			    auth->e_nonce, e_nonce_len);
+		goto fail;
+	}
+
+	status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONN_STATUS,
+			      &status_len);
+	if (!status) {
+		dpp_auth_fail(auth,
+			      "Missing required DPP Connection Status attribute");
+		goto fail;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
+			  status, status_len);
+
+	root = json_parse((const char *) status, status_len);
+	if (!root) {
+		dpp_auth_fail(auth, "Could not parse connStatus");
+		goto fail;
+	}
+
+	ssid64 = json_get_member_base64url(root, "ssid64");
+	if (ssid64 && wpabuf_len(ssid64) <= SSID_MAX_LEN) {
+		*ssid_len = wpabuf_len(ssid64);
+		os_memcpy(ssid, wpabuf_head(ssid64), *ssid_len);
+	}
+	wpabuf_free(ssid64);
+
+	token = json_get_member(root, "channelList");
+	if (token && token->type == JSON_STRING &&
+	    valid_channel_list(token->string))
+		*channel_list = os_strdup(token->string);
+
+	token = json_get_member(root, "result");
+	if (!token || token->type != JSON_NUMBER) {
+		dpp_auth_fail(auth, "No connStatus - result");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: result %d", token->number);
+	ret = token->number;
+
+fail:
+	json_free(root);
+	bin_clear_free(unwrapped, unwrapped_len);
+	return ret;
+}
+
+
+struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
+					     enum dpp_status_error result,
+					     const u8 *ssid, size_t ssid_len,
+					     const char *channel_list)
+{
+	struct wpabuf *msg = NULL, *clear = NULL, *json;
+	size_t nonce_len, clear_len, attr_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 *wrapped;
+
+	json = wpabuf_alloc(1000);
+	if (!json)
+		return NULL;
+	json_start_object(json, NULL);
+	json_add_int(json, "result", result);
+	if (ssid) {
+		json_value_sep(json);
+		if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0)
+			goto fail;
+	}
+	if (channel_list) {
+		json_value_sep(json);
+		json_add_string(json, "channelList", channel_list);
+	}
+	json_end_object(json);
+	wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
+			  wpabuf_head(json), wpabuf_len(json));
+
+	nonce_len = auth->curve->nonce_len;
+	clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json);
+	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+	clear = wpabuf_alloc(clear_len);
+	msg = dpp_alloc_msg(DPP_PA_CONNECTION_STATUS_RESULT, attr_len);
+	if (!clear || !msg)
+		goto fail;
+
+	/* E-nonce */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(clear, nonce_len);
+	wpabuf_put_data(clear, auth->e_nonce, nonce_len);
+
+	/* DPP Connection Status */
+	wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
+	wpabuf_put_le16(clear, wpabuf_len(json));
+	wpabuf_put_buf(clear, json);
+
+	/* OUI, OUI type, Crypto Suite, DPP frame type */
+	addr[0] = wpabuf_head_u8(msg) + 2;
+	len[0] = 3 + 1 + 1 + 1;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+	/* Attributes before Wrapped Data (none) */
+	addr[1] = wpabuf_put(msg, 0);
+	len[1] = 0;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+	/* Wrapped Data */
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+	if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    wpabuf_head(clear), wpabuf_len(clear),
+			    2, addr, len, wrapped) < 0)
+		goto fail;
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Connection Status Result attributes",
+			msg);
+	wpabuf_free(json);
+	wpabuf_free(clear);
+	return msg;
+fail:
+	wpabuf_free(json);
+	wpabuf_free(clear);
+	wpabuf_free(msg);
+	return NULL;
+}
+
+#endif /* CONFIG_DPP2 */
+
+
 void dpp_configurator_free(struct dpp_configurator *conf)
 {
 	if (!conf)
@@ -6138,8 +6819,7 @@
 		goto fail;
 	}
 
-	conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
-					       NULL, 0);
+	conf->kid = base64_url_encode(kid_hash, sizeof(kid_hash), NULL);
 	if (!conf->kid)
 		goto fail;
 out:
@@ -6180,13 +6860,16 @@
 	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
 	if (!auth->own_protocol_key)
 		return -1;
-	dpp_copy_netaccesskey(auth);
+	dpp_copy_netaccesskey(auth, &auth->conf_obj[0]);
 	auth->peer_protocol_key = auth->own_protocol_key;
-	dpp_copy_csign(auth, auth->conf->csign);
+	dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign);
 
-	conf_obj = dpp_build_conf_obj(auth, ap);
-	if (!conf_obj)
+	conf_obj = dpp_build_conf_obj(auth, ap, 0);
+	if (!conf_obj) {
+		wpabuf_free(auth->conf_obj[0].c_sign_key);
+		auth->conf_obj[0].c_sign_key = NULL;
 		goto fail;
+	}
 	ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
 				 wpabuf_len(conf_obj));
 fail:
@@ -6367,7 +7050,6 @@
 	const char *pos, *end;
 	unsigned char *own_conn = NULL;
 	size_t own_conn_len;
-	EVP_PKEY_CTX *ctx = NULL;
 	size_t Nx_len;
 	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
 
@@ -6402,8 +7084,7 @@
 		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
 		goto fail;
 	}
-	own_conn = base64_url_decode((const unsigned char *) pos,
-				     end - pos, &own_conn_len);
+	own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
 	if (!own_conn) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode own signedConnector JWS Payload");
@@ -6481,18 +7162,8 @@
 	}
 
 	/* ECDH: N = nk * PK */
-	ctx = EVP_PKEY_CTX_new(own_key, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 ||
-	    Nx_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0)
 		goto fail;
-	}
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
 			Nx, Nx_len);
@@ -6515,7 +7186,6 @@
 	if (ret != DPP_STATUS_OK)
 		os_memset(intro, 0, sizeof(*intro));
 	os_memset(Nx, 0, sizeof(Nx));
-	EVP_PKEY_CTX_free(ctx);
 	os_free(own_conn);
 	os_free(signed_connector);
 	os_free(info.payload);
@@ -6535,6 +7205,7 @@
 	EC_GROUP *group;
 	size_t len = curve->prime_len;
 	const u8 *x, *y;
+	EVP_PKEY *res;
 
 	switch (curve->ike_group) {
 	case 19:
@@ -6568,14 +7239,16 @@
 	group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
 	if (!group)
 		return NULL;
-	return dpp_set_pubkey_point_group(group, x, y, len);
+	res = dpp_set_pubkey_point_group(group, x, y, len);
+	EC_GROUP_free(group);
+	return res;
 }
 
 
 static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
 				     const u8 *mac_init, const char *code,
 				     const char *identifier, BN_CTX *bnctx,
-				     const EC_GROUP **ret_group)
+				     EC_GROUP **ret_group)
 {
 	u8 hash[DPP_MAX_HASH_LEN];
 	const u8 *addr[3];
@@ -6644,8 +7317,10 @@
 	EC_KEY_free(Pi_ec);
 	EVP_PKEY_free(Pi);
 	BN_clear_free(hash_bn);
-	if (ret_group)
+	if (ret_group && Qi)
 		*ret_group = group2;
+	else
+		EC_GROUP_free(group2);
 	return Qi;
 fail:
 	EC_POINT_free(Qi);
@@ -6657,7 +7332,7 @@
 static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
 				     const u8 *mac_resp, const char *code,
 				     const char *identifier, BN_CTX *bnctx,
-				     const EC_GROUP **ret_group)
+				     EC_GROUP **ret_group)
 {
 	u8 hash[DPP_MAX_HASH_LEN];
 	const u8 *addr[3];
@@ -6726,8 +7401,10 @@
 	EC_KEY_free(Pr_ec);
 	EVP_PKEY_free(Pr);
 	BN_clear_free(hash_bn);
-	if (ret_group)
+	if (ret_group && Qr)
 		*ret_group = group2;
+	else
+		EC_GROUP_free(group2);
 	return Qr;
 fail:
 	EC_POINT_free(Qr);
@@ -6796,6 +7473,7 @@
 	BN_free(y);
 	EC_POINT_free(point);
 	BN_CTX_free(ctx);
+	EC_GROUP_free(group);
 
 	return ret;
 }
@@ -6807,7 +7485,7 @@
 	EC_KEY *X_ec = NULL;
 	const EC_POINT *X_point;
 	BN_CTX *bnctx = NULL;
-	const EC_GROUP *group;
+	EC_GROUP *group = NULL;
 	EC_POINT *Qi = NULL, *M = NULL;
 	struct wpabuf *M_buf = NULL;
 	BIGNUM *Mx = NULL, *My = NULL;
@@ -6929,6 +7607,7 @@
 	BN_clear_free(Mx);
 	BN_clear_free(My);
 	BN_CTX_free(bnctx);
+	EC_GROUP_free(group);
 	return msg;
 fail:
 	wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
@@ -7173,7 +7852,7 @@
 	struct dpp_pkex *pkex = NULL;
 	EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
 	BN_CTX *bnctx = NULL;
-	const EC_GROUP *group;
+	EC_GROUP *group = NULL;
 	BIGNUM *Mx = NULL, *My = NULL;
 	EC_KEY *Y_ec = NULL, *X_ec = NULL;;
 	const EC_POINT *Y_point;
@@ -7181,7 +7860,6 @@
 	u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
 	size_t Kx_len;
 	int res;
-	EVP_PKEY_CTX *ctx = NULL;
 
 	if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
 		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
@@ -7348,18 +8026,8 @@
 		goto fail;
 
 	/* K = y * X' */
-	ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
-	    Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
 		goto fail;
-	}
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
 			Kx, Kx_len);
@@ -7377,7 +8045,6 @@
 	pkex->exchange_done = 1;
 
 out:
-	EVP_PKEY_CTX_free(ctx);
 	BN_CTX_free(bnctx);
 	EC_POINT_free(Qi);
 	EC_POINT_free(Qr);
@@ -7390,6 +8057,7 @@
 	EC_POINT_free(X);
 	EC_KEY_free(X_ec);
 	EC_KEY_free(Y_ec);
+	EC_GROUP_free(group);
 	return pkex;
 fail:
 	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
@@ -7518,13 +8186,12 @@
 {
 	const u8 *attr_status, *attr_id, *attr_key, *attr_group;
 	u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
-	const EC_GROUP *group;
+	EC_GROUP *group = NULL;
 	BN_CTX *bnctx = NULL;
 	struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
 	const struct dpp_curve_params *curve = pkex->own_bi->curve;
 	EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
 	BIGNUM *Nx = NULL, *Ny = NULL;
-	EVP_PKEY_CTX *ctx = NULL;
 	EC_KEY *Y_ec = NULL;
 	size_t Jx_len, Kx_len;
 	u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
@@ -7636,18 +8303,8 @@
 	if (!pkex->y ||
 	    EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
 		goto fail;
-	ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
-	    Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
 		goto fail;
-	}
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
 			Jx, Jx_len);
@@ -7671,19 +8328,8 @@
 	wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
 
 	/* K = x * Y’ */
-	EVP_PKEY_CTX_free(ctx);
-	ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
-	    Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
 		goto fail;
-	}
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
 			Kx, Kx_len);
@@ -7713,8 +8359,8 @@
 	BN_free(Nx);
 	BN_free(Ny);
 	EC_KEY_free(Y_ec);
-	EVP_PKEY_CTX_free(ctx);
 	BN_CTX_free(bnctx);
+	EC_GROUP_free(group);
 	return msg;
 fail:
 	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
@@ -7840,7 +8486,6 @@
 					      const u8 *buf, size_t buflen)
 {
 	const struct dpp_curve_params *curve = pkex->own_bi->curve;
-	EVP_PKEY_CTX *ctx = NULL;
 	size_t Jx_len, Lx_len;
 	u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
 	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
@@ -7924,18 +8569,8 @@
 			    pkex->peer_bootstrap_key);
 
 	/* ECDH: J' = y * A' */
-	ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
-	    Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
 		goto fail;
-	}
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
 			Jx, Jx_len);
@@ -7971,19 +8606,8 @@
 	wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
 
 	/* ECDH: L = b * X' */
-	EVP_PKEY_CTX_free(ctx);
-	ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
-	    Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
 		goto fail;
-	}
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
 			Lx, Lx_len);
@@ -8009,7 +8633,6 @@
 		goto fail;
 
 out:
-	EVP_PKEY_CTX_free(ctx);
 	os_free(unwrapped);
 	wpabuf_free(A_pub);
 	wpabuf_free(B_pub);
@@ -8038,7 +8661,6 @@
 	u8 v[DPP_MAX_HASH_LEN];
 	size_t Lx_len;
 	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
-	EVP_PKEY_CTX *ctx = NULL;
 	struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -8109,18 +8731,8 @@
 			    pkex->peer_bootstrap_key);
 
 	/* ECDH: L' = x * B' */
-	ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
-	if (!ctx ||
-	    EVP_PKEY_derive_init(ctx) != 1 ||
-	    EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
-	    EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
-	    Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
-	    EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
-		wpa_printf(MSG_ERROR,
-			   "DPP: Failed to derive ECDH shared secret: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+	if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
 		goto fail;
-	}
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
 			Lx, Lx_len);
@@ -8160,7 +8772,6 @@
 	wpabuf_free(B_pub);
 	wpabuf_free(X_pub);
 	wpabuf_free(Y_pub);
-	EVP_PKEY_CTX_free(ctx);
 	os_free(unwrapped);
 	return ret;
 fail:
@@ -8207,8 +8818,7 @@
 
 	wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
 		   pos);
-	signature = base64_url_decode((const unsigned char *) pos,
-				      os_strlen(pos), &signature_len);
+	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
 	if (!signature || signature_len == 0)
 		goto fail;
 	wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
@@ -8216,8 +8826,7 @@
 	signature[signature_len - 1] ^= 0x01;
 	wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
 		    signature, signature_len);
-	signed3 = (char *) base64_url_encode(signature, signature_len,
-					     &signed3_len, 0);
+	signed3 = base64_url_encode(signature, signature_len, &signed3_len);
 	if (!signed3)
 		goto fail;
 	os_memcpy(pos, signed3, signed3_len);
@@ -8365,10 +8974,30 @@
 	if (!dpp)
 		return NULL;
 
-	bi = dpp_parse_qr_code(uri);
+	bi = dpp_parse_uri(uri);
 	if (!bi)
 		return NULL;
 
+	bi->type = DPP_BOOTSTRAP_QR_CODE;
+	bi->id = dpp_next_id(dpp);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	return bi;
+}
+
+
+struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
+					    const char *uri)
+{
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return NULL;
+
+	bi = dpp_parse_uri(uri);
+	if (!bi)
+		return NULL;
+
+	bi->type = DPP_BOOTSTRAP_NFC_URI;
 	bi->id = dpp_next_id(dpp);
 	dl_list_add(&dpp->bootstrap, &bi->list);
 	return bi;
@@ -8396,6 +9025,8 @@
 		bi->type = DPP_BOOTSTRAP_QR_CODE;
 	else if (os_strstr(cmd, "type=pkex"))
 		bi->type = DPP_BOOTSTRAP_PKEX;
+	else if (os_strstr(cmd, "type=nfc-uri"))
+		bi->type = DPP_BOOTSTRAP_NFC_URI;
 	else
 		goto fail;
 
@@ -8533,20 +9164,25 @@
 		       char *reply, int reply_size)
 {
 	struct dpp_bootstrap_info *bi;
+	char pkhash[2 * SHA256_MAC_LEN + 1];
 
 	bi = dpp_bootstrap_get_id(dpp, id);
 	if (!bi)
 		return -1;
+	wpa_snprintf_hex(pkhash, sizeof(pkhash), bi->pubkey_hash,
+			 SHA256_MAC_LEN);
 	return os_snprintf(reply, reply_size, "type=%s\n"
 			   "mac_addr=" MACSTR "\n"
 			   "info=%s\n"
 			   "num_freq=%u\n"
-			   "curve=%s\n",
+			   "curve=%s\n"
+			   "pkhash=%s\n",
 			   dpp_bootstrap_type_txt(bi->type),
 			   MAC2STR(bi->mac_addr),
 			   bi->info ? bi->info : "",
 			   bi->num_freq,
-			   bi->curve->name);
+			   bi->curve->name,
+			   pkhash);
 }
 
 
@@ -8689,16 +9325,94 @@
 }
 
 
-struct dpp_global * dpp_global_init(void)
+#ifdef CONFIG_DPP2
+
+static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+							   void *timeout_ctx);
+
+
+static void dpp_connection_free(struct dpp_connection *conn)
+{
+	if (conn->sock >= 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
+			   conn->sock);
+		eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
+		eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+		close(conn->sock);
+	}
+	eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
+			     conn, NULL);
+	wpabuf_free(conn->msg);
+	wpabuf_free(conn->msg_out);
+	dpp_auth_deinit(conn->auth);
+	os_free(conn);
+}
+
+
+static void dpp_connection_remove(struct dpp_connection *conn)
+{
+	dl_list_del(&conn->list);
+	dpp_connection_free(conn);
+}
+
+
+static void dpp_tcp_init_flush(struct dpp_global *dpp)
+{
+	struct dpp_connection *conn, *tmp;
+
+	dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
+			      list)
+		dpp_connection_remove(conn);
+}
+
+
+static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
+{
+	struct dpp_connection *conn, *tmp;
+
+	dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+			      list)
+		dpp_connection_remove(conn);
+	os_free(ctrl);
+}
+
+
+static void dpp_relay_flush_controllers(struct dpp_global *dpp)
+{
+	struct dpp_relay_controller *ctrl, *tmp;
+
+	if (!dpp)
+		return;
+
+	dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
+			      struct dpp_relay_controller, list) {
+		dl_list_del(&ctrl->list);
+		dpp_relay_controller_free(ctrl);
+	}
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+struct dpp_global * dpp_global_init(struct dpp_global_config *config)
 {
 	struct dpp_global *dpp;
 
 	dpp = os_zalloc(sizeof(*dpp));
 	if (!dpp)
 		return NULL;
+	dpp->msg_ctx = config->msg_ctx;
+#ifdef CONFIG_DPP2
+	dpp->cb_ctx = config->cb_ctx;
+	dpp->process_conf_obj = config->process_conf_obj;
+#endif /* CONFIG_DPP2 */
 
 	dl_list_init(&dpp->bootstrap);
 	dl_list_init(&dpp->configurator);
+#ifdef CONFIG_DPP2
+	dl_list_init(&dpp->controllers);
+	dl_list_init(&dpp->tcp_init);
+#endif /* CONFIG_DPP2 */
 
 	return dpp;
 }
@@ -8711,6 +9425,11 @@
 
 	dpp_bootstrap_del(dpp, 0);
 	dpp_configurator_del(dpp, 0);
+#ifdef CONFIG_DPP2
+	dpp_tcp_init_flush(dpp);
+	dpp_relay_flush_controllers(dpp);
+	dpp_controller_stop(dpp);
+#endif /* CONFIG_DPP2 */
 }
 
 
@@ -8719,3 +9438,1287 @@
 	dpp_global_clear(dpp);
 	os_free(dpp);
 }
+
+
+#ifdef CONFIG_DPP2
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+					int initiator);
+
+
+int dpp_relay_add_controller(struct dpp_global *dpp,
+			     struct dpp_relay_config *config)
+{
+	struct dpp_relay_controller *ctrl;
+
+	if (!dpp)
+		return -1;
+
+	ctrl = os_zalloc(sizeof(*ctrl));
+	if (!ctrl)
+		return -1;
+	dl_list_init(&ctrl->conn);
+	ctrl->global = dpp;
+	os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
+	os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
+	ctrl->cb_ctx = config->cb_ctx;
+	ctrl->tx = config->tx;
+	ctrl->gas_resp_tx = config->gas_resp_tx;
+	dl_list_add(&dpp->controllers, &ctrl->list);
+	return 0;
+}
+
+
+static struct dpp_relay_controller *
+dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
+{
+	struct dpp_relay_controller *ctrl;
+
+	if (!dpp)
+		return NULL;
+
+	dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+			 list) {
+		if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
+			return ctrl;
+	}
+
+	return NULL;
+}
+
+
+static void dpp_controller_gas_done(struct dpp_connection *conn)
+{
+	struct dpp_authentication *auth = conn->auth;
+
+	if (auth->peer_version >= 2 &&
+	    auth->conf_resp_status == DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+		auth->waiting_conf_result = 1;
+		return;
+	}
+
+	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	dpp_connection_remove(conn);
+}
+
+
+static int dpp_tcp_send(struct dpp_connection *conn)
+{
+	int res;
+
+	if (!conn->msg_out) {
+		eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+		conn->write_eloop = 0;
+		return -1;
+	}
+	res = send(conn->sock,
+		   wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
+		   wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
+			   strerror(errno));
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	conn->msg_out_pos += res;
+	if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: %u/%u bytes of message sent to Controller",
+			   (unsigned int) conn->msg_out_pos,
+			   (unsigned int) wpabuf_len(conn->msg_out));
+		if (!conn->write_eloop &&
+		    eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+					dpp_conn_tx_ready, conn, NULL) == 0)
+			conn->write_eloop = 1;
+		return 1;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
+	wpabuf_free(conn->msg_out);
+	conn->msg_out = NULL;
+	conn->msg_out_pos = 0;
+	eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+	conn->write_eloop = 0;
+	if (!conn->read_eloop &&
+	    eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+				dpp_controller_rx, conn, NULL) == 0)
+		conn->read_eloop = 1;
+	if (conn->on_tcp_tx_complete_remove) {
+		dpp_connection_remove(conn);
+	} else if (conn->ctrl && conn->on_tcp_tx_complete_gas_done &&
+		   conn->auth) {
+		dpp_controller_gas_done(conn);
+	} else if (conn->on_tcp_tx_complete_auth_ok) {
+		conn->on_tcp_tx_complete_auth_ok = 0;
+		dpp_controller_auth_success(conn, 1);
+	}
+
+	return 0;
+}
+
+
+static void dpp_controller_start_gas_client(struct dpp_connection *conn)
+{
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *buf;
+	int netrole_ap = 0; /* TODO: make this configurable */
+
+	buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration request data available");
+		return;
+	}
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = wpabuf_alloc(4 + wpabuf_len(buf) - 1);
+	if (!conn->msg_out) {
+		wpabuf_free(buf);
+		return;
+	}
+	wpabuf_put_be32(conn->msg_out, wpabuf_len(buf) - 1);
+	wpabuf_put_data(conn->msg_out, wpabuf_head_u8(buf) + 1,
+			wpabuf_len(buf) - 1);
+	wpabuf_free(buf);
+
+	if (dpp_tcp_send(conn) == 1) {
+		if (!conn->write_eloop) {
+			if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+						dpp_conn_tx_ready,
+						conn, NULL) < 0)
+				return;
+			conn->write_eloop = 1;
+		}
+	}
+}
+
+
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+					int initiator)
+{
+	struct dpp_authentication *auth = conn->auth;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(conn->global->msg_ctx, MSG_INFO,
+		DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at Authentication Confirm");
+		if (auth->configurator) {
+			/* Prevent GAS response */
+			auth->auth_success = 0;
+		}
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!auth->configurator)
+		dpp_controller_start_gas_client(conn);
+}
+
+
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
+	dpp_tcp_send(conn);
+}
+
+
+static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
+				  const struct hostapd_ip_addr *ipaddr,
+				  int port)
+{
+	struct sockaddr_in *dst;
+#ifdef CONFIG_IPV6
+	struct sockaddr_in6 *dst6;
+#endif /* CONFIG_IPV6 */
+
+	switch (ipaddr->af) {
+	case AF_INET:
+		dst = (struct sockaddr_in *) addr;
+		os_memset(dst, 0, sizeof(*dst));
+		dst->sin_family = AF_INET;
+		dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
+		dst->sin_port = htons(port);
+		*addrlen = sizeof(*dst);
+		break;
+#ifdef CONFIG_IPV6
+	case AF_INET6:
+		dst6 = (struct sockaddr_in6 *) addr;
+		os_memset(dst6, 0, sizeof(*dst6));
+		dst6->sin6_family = AF_INET6;
+		os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
+			  sizeof(struct in6_addr));
+		dst6->sin6_port = htons(port);
+		*addrlen = sizeof(*dst6);
+		break;
+#endif /* CONFIG_IPV6 */
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct dpp_connection *
+dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
+		   unsigned int freq)
+{
+	struct dpp_connection *conn;
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	char txt[100];
+
+	if (dl_list_len(&ctrl->conn) >= 15) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
+		return NULL;
+	}
+
+	if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
+				   &ctrl->ipaddr, DPP_TCP_PORT) < 0)
+		return NULL;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn)
+		return NULL;
+
+	conn->global = ctrl->global;
+	conn->relay = ctrl;
+	os_memcpy(conn->mac_addr, src, ETH_ALEN);
+	conn->freq = freq;
+
+	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (conn->sock < 0)
+		goto fail;
+	wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
+		   conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
+
+	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
+		if (errno != EINPROGRESS) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+				   strerror(errno));
+			goto fail;
+		}
+
+		/*
+		 * Continue connecting in the background; eloop will call us
+		 * once the connection is ready (or failed).
+		 */
+	}
+
+	if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+				dpp_conn_tx_ready, conn, NULL) < 0)
+		goto fail;
+	conn->write_eloop = 1;
+
+	/* TODO: eloop timeout to clear a connection if it does not complete
+	 * properly */
+
+	dl_list_add(&ctrl->conn, &conn->list);
+	return conn;
+fail:
+	dpp_connection_free(conn);
+	return NULL;
+}
+
+
+static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct wpabuf *msg;
+
+	msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
+	if (!msg)
+		return NULL;
+	wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
+	wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+	wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
+	wpabuf_put_data(msg, buf, len);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+	return msg;
+}
+
+
+static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
+			const u8 *buf, size_t len)
+{
+	u8 type = hdr[DPP_HDR_LEN - 1];
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Continue already established Relay/Controller connection for this session");
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+	if (!conn->msg_out) {
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	/* TODO: for proto ver 1, need to do remove connection based on GAS Resp
+	 * TX status */
+	if (type == DPP_PA_CONFIGURATION_RESULT)
+		conn->on_tcp_tx_complete_remove = 1;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
+			const u8 *buf, size_t len, unsigned int freq,
+			const u8 *i_bootstrap, const u8 *r_bootstrap)
+{
+	struct dpp_relay_controller *ctrl;
+	struct dpp_connection *conn;
+	u8 type = hdr[DPP_HDR_LEN - 1];
+
+	/* Check if there is an already started session for this peer and if so,
+	 * continue that session (send this over TCP) and return 0.
+	 */
+	if (type != DPP_PA_PEER_DISCOVERY_REQ &&
+	    type != DPP_PA_PEER_DISCOVERY_RESP) {
+		dl_list_for_each(ctrl, &dpp->controllers,
+				 struct dpp_relay_controller, list) {
+			dl_list_for_each(conn, &ctrl->conn,
+					 struct dpp_connection, list) {
+				if (os_memcmp(src, conn->mac_addr,
+					      ETH_ALEN) == 0)
+					return dpp_relay_tx(conn, hdr, buf, len);
+			}
+		}
+	}
+
+	if (!r_bootstrap)
+		return -1;
+
+	ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
+	if (!ctrl)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Authentication Request for a configured Controller");
+	conn = dpp_relay_new_conn(ctrl, src, freq);
+	if (!conn)
+		return -1;
+
+	conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+	if (!conn->msg_out) {
+		dpp_connection_remove(conn);
+		return -1;
+	}
+	/* Message will be sent in dpp_conn_tx_ready() */
+
+	return 0;
+}
+
+
+int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
+			 size_t data_len)
+{
+	struct dpp_relay_controller *ctrl;
+	struct dpp_connection *conn, *found = NULL;
+	struct wpabuf *msg;
+
+	/* Check if there is a successfully completed authentication for this
+	 * and if so, continue that session (send this over TCP) and return 0.
+	 */
+	dl_list_for_each(ctrl, &dpp->controllers,
+			 struct dpp_relay_controller, list) {
+		if (found)
+			break;
+		dl_list_for_each(conn, &ctrl->conn,
+				 struct dpp_connection, list) {
+			if (os_memcmp(src, conn->mac_addr,
+				      ETH_ALEN) == 0) {
+				found = conn;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+		return -1;
+
+	msg = wpabuf_alloc(4 + 1 + data_len);
+	if (!msg)
+		return -1;
+	wpabuf_put_be32(msg, 1 + data_len);
+	wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
+	wpabuf_put_data(msg, data, data_len);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = msg;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+static void dpp_controller_free(struct dpp_controller *ctrl)
+{
+	struct dpp_connection *conn, *tmp;
+
+	if (!ctrl)
+		return;
+
+	dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+			      list)
+		dpp_connection_remove(conn);
+
+	if (ctrl->sock >= 0) {
+		close(ctrl->sock);
+		eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
+	}
+	os_free(ctrl->configurator_params);
+	os_free(ctrl);
+}
+
+
+static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
+				      const u8 *hdr, const u8 *buf, size_t len)
+{
+	const u8 *r_bootstrap, *i_bootstrap;
+	u16 r_bootstrap_len, i_bootstrap_len;
+	struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+	if (!conn->ctrl)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
+				&own_bi, &peer_bi);
+	if (!own_bi) {
+		wpa_printf(MSG_INFO,
+			"No matching own bootstrapping key found - ignore message");
+		return -1;
+	}
+
+	if (conn->auth) {
+		wpa_printf(MSG_INFO,
+			   "Already in DPP authentication exchange - ignore new one");
+		return 0;
+	}
+
+	conn->auth = dpp_auth_req_rx(conn->ctrl->global->msg_ctx,
+				     conn->ctrl->allowed_roles,
+				     conn->ctrl->qr_mutual,
+				     peer_bi, own_bi, -1, hdr, buf, len);
+	if (!conn->auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return -1;
+	}
+
+	if (dpp_set_configurator(conn->ctrl->global, conn->ctrl->global->msg_ctx,
+				 conn->auth,
+				 conn->ctrl->configurator_params) < 0) {
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = wpabuf_alloc(4 + wpabuf_len(conn->auth->resp_msg) - 1);
+	if (!conn->msg_out)
+		return -1;
+	wpabuf_put_be32(conn->msg_out, wpabuf_len(conn->auth->resp_msg) - 1);
+	wpabuf_put_data(conn->msg_out, wpabuf_head_u8(conn->auth->resp_msg) + 1,
+			wpabuf_len(conn->auth->resp_msg) - 1);
+
+	if (dpp_tcp_send(conn) == 1) {
+		if (!conn->write_eloop) {
+			if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+						dpp_conn_tx_ready,
+						conn, NULL) < 0)
+				return -1;
+			conn->write_eloop = 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
+				       const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *msg;
+
+	if (!auth)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
+
+	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+	if (!msg) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Start wait for full response");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		dpp_connection_remove(conn);
+		return -1;
+	}
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
+	if (!conn->msg_out) {
+		wpabuf_free(msg);
+		return -1;
+	}
+	wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
+	wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
+			wpabuf_len(msg) - 1);
+	wpabuf_free(msg);
+
+	conn->on_tcp_tx_complete_auth_ok = 1;
+	if (dpp_tcp_send(conn) == 1) {
+		if (!conn->write_eloop) {
+			if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+						dpp_conn_tx_ready,
+						conn, NULL) < 0)
+				return -1;
+			conn->write_eloop = 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
+				       const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return -1;
+	}
+
+	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return -1;
+	}
+
+	dpp_controller_auth_success(conn, 0);
+	return 0;
+}
+
+
+static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+							   void *timeout_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+
+	if (!conn->auth->waiting_conf_result)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Timeout while waiting for Connection Status Result");
+	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+		DPP_EVENT_CONN_STATUS_RESULT "timeout");
+	dpp_connection_remove(conn);
+}
+
+
+static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
+					 const u8 *hdr, const u8 *buf,
+					 size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+	enum dpp_status_error status;
+
+	if (!conn->ctrl)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
+
+	if (!auth || !auth->waiting_conf_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for result - drop");
+		return -1;
+	}
+
+	status = dpp_conf_result_rx(auth, hdr, buf, len);
+	if (status == DPP_STATUS_OK && auth->send_conn_status) {
+		wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+		eloop_cancel_timeout(
+			dpp_controller_conn_status_result_wait_timeout,
+			conn, NULL);
+		eloop_register_timeout(
+			16, 0, dpp_controller_conn_status_result_wait_timeout,
+			conn, NULL);
+		return 0;
+	}
+	if (status == DPP_STATUS_OK)
+		wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+			DPP_EVENT_CONF_SENT);
+	else
+		wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+			DPP_EVENT_CONF_FAILED);
+	return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
+						const u8 *hdr, const u8 *buf,
+						size_t len)
+{
+	struct dpp_authentication *auth = conn->auth;
+	enum dpp_status_error status;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len = 0;
+	char *channel_list = NULL;
+
+	if (!conn->ctrl)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+	if (!auth || !auth->waiting_conn_status_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for connection status result - drop");
+		return -1;
+	}
+
+	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+					   ssid, &ssid_len, &channel_list);
+	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+		DPP_EVENT_CONN_STATUS_RESULT
+		"result=%d ssid=%s channel_list=%s",
+		status, wpa_ssid_txt(ssid, ssid_len),
+		channel_list ? channel_list : "N/A");
+	os_free(channel_list);
+	return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
+				    size_t len)
+{
+	const u8 *pos, *end;
+	u8 type;
+
+	wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
+	pos = msg;
+	end = msg + len;
+
+	if (end - pos < DPP_HDR_LEN ||
+	    WPA_GET_BE24(pos) != OUI_WFA ||
+	    pos[3] != DPP_OUI_TYPE) {
+		wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
+		return -1;
+	}
+
+	if (pos[4] != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
+			   pos[4]);
+		return -1;
+	}
+	type = pos[5];
+	wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
+	pos += DPP_HDR_LEN;
+
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
+		    pos, end - pos);
+	if (dpp_check_attrs(pos, end - pos) < 0)
+		return -1;
+
+	if (conn->relay) {
+		wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+		conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
+				conn->freq, msg, len);
+		return 0;
+	}
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
+	case DPP_PA_AUTHENTICATION_RESP:
+		return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
+	case DPP_PA_AUTHENTICATION_CONF:
+		return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
+	case DPP_PA_CONFIGURATION_RESULT:
+		return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
+	case DPP_PA_CONNECTION_STATUS_RESULT:
+		return dpp_controller_rx_conn_status_result(conn, msg, pos,
+							    end - pos);
+	default:
+		/* TODO: missing messages types */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported frame subtype %d", type);
+		return -1;
+	}
+}
+
+
+static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
+				     size_t len)
+{
+	const u8 *pos, *end, *next;
+	u8 dialog_token;
+	const u8 *adv_proto;
+	u16 slen;
+	struct wpabuf *resp, *buf;
+	struct dpp_authentication *auth = conn->auth;
+
+	if (len < 1 + 2)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Configuration Request over TCP");
+
+	if (!conn->ctrl || !auth || !auth->auth_success) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return -1;
+	}
+
+	pos = msg;
+	end = msg + len;
+
+	dialog_token = *pos++;
+	adv_proto = pos++;
+	slen = *pos++;
+	if (*adv_proto != WLAN_EID_ADV_PROTO ||
+	    slen > end - pos || slen < 2)
+		return -1;
+
+	next = pos + slen;
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+	    pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+		return -1;
+
+	pos = next;
+	/* Query Request */
+	if (end - pos < 2)
+		return -1;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (slen > end - pos)
+		return -1;
+
+	resp = dpp_conf_req_rx(auth, pos, slen);
+	if (!resp)
+		return -1;
+
+	buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
+	if (!buf) {
+		wpabuf_free(resp);
+		return -1;
+	}
+
+	wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
+
+	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
+	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+
+	dpp_write_adv_proto(buf);
+	dpp_write_gas_query(buf, resp);
+	wpabuf_free(resp);
+
+	/* Send Config Response over TCP; GAS fragmentation is taken care of by
+	 * the Relay */
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = buf;
+	conn->on_tcp_tx_complete_gas_done = 1;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
+{
+	struct dpp_authentication *auth = conn->auth;
+	int res;
+	struct wpabuf *msg, *encaps;
+	enum dpp_status_error status;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Configuration Response for local stack from TCP");
+
+	res = dpp_conf_resp_rx(auth, resp);
+	wpabuf_free(resp);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		return -1;
+	}
+
+	if (conn->global->process_conf_obj)
+		res = conn->global->process_conf_obj(conn->global->cb_ctx,
+						     auth);
+	else
+		res = 0;
+
+	if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
+		return -1;
+
+#ifdef CONFIG_DPP2
+	wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+	status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
+	msg = dpp_build_conf_result(auth, status);
+	if (!msg)
+		return -1;
+
+	encaps = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
+	if (!encaps) {
+		wpabuf_free(msg);
+		return -1;
+	}
+	wpabuf_put_be32(encaps, wpabuf_len(msg) - 1);
+	wpabuf_put_data(encaps, wpabuf_head_u8(msg) + 1, wpabuf_len(msg) - 1);
+	wpabuf_free(msg);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", encaps);
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = encaps;
+	conn->on_tcp_tx_complete_remove = 1;
+	dpp_tcp_send(conn);
+
+	/* This exchange will be terminated in the TX status handler */
+
+	return 0;
+#else /* CONFIG_DPP2 */
+	return -1;
+#endif /* CONFIG_DPP2 */
+}
+
+
+static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
+			   size_t len)
+{
+	struct wpabuf *buf;
+	u8 dialog_token;
+	const u8 *pos, *end, *next, *adv_proto;
+	u16 status, slen;
+
+	if (len < 5 + 2)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Configuration Response over TCP");
+
+	pos = msg;
+	end = msg + len;
+
+	dialog_token = *pos++;
+	status = WPA_GET_LE16(pos);
+	if (status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
+		return -1;
+	}
+	pos += 2;
+	pos += 2; /* ignore GAS Comeback Delay */
+
+	adv_proto = pos++;
+	slen = *pos++;
+	if (*adv_proto != WLAN_EID_ADV_PROTO ||
+	    slen > end - pos || slen < 2)
+		return -1;
+
+	next = pos + slen;
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+	    pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+		return -1;
+
+	pos = next;
+	/* Query Response */
+	if (end - pos < 2)
+		return -1;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (slen > end - pos)
+		return -1;
+
+	buf = wpabuf_alloc(slen);
+	if (!buf)
+		return -1;
+	wpabuf_put_data(buf, pos, slen);
+
+	if (!conn->relay && !conn->ctrl)
+		return dpp_tcp_rx_gas_resp(conn, buf);
+
+	if (!conn->relay) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		wpabuf_free(buf);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+	conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
+				 dialog_token, 0, buf);
+
+	return 0;
+}
+
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+	int res;
+	const u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
+		   sd);
+
+	if (conn->msg_len_octets < 4) {
+		u32 msglen;
+
+		res = recv(sd, &conn->msg_len[conn->msg_len_octets],
+			   4 - conn->msg_len_octets, 0);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
+				   strerror(errno));
+			dpp_connection_remove(conn);
+			return;
+		}
+		if (res == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: No more data available over TCP");
+			dpp_connection_remove(conn);
+			return;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Received %d/%d octet(s) of message length field",
+			   res, (int) (4 - conn->msg_len_octets));
+		conn->msg_len_octets += res;
+
+		if (conn->msg_len_octets < 4) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Need %d more octets of message length field",
+				   (int) (4 - conn->msg_len_octets));
+			return;
+		}
+
+		msglen = WPA_GET_BE32(conn->msg_len);
+		wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
+		if (msglen > 65535) {
+			wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
+			dpp_connection_remove(conn);
+			return;
+		}
+
+		wpabuf_free(conn->msg);
+		conn->msg = wpabuf_alloc(msglen);
+	}
+
+	if (!conn->msg) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No buffer available for receiving the message");
+		dpp_connection_remove(conn);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
+		   (unsigned int) wpabuf_tailroom(conn->msg));
+
+	res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
+		dpp_connection_remove(conn);
+		return;
+	}
+	if (res == 0) {
+		wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
+		dpp_connection_remove(conn);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
+	wpabuf_put(conn->msg, res);
+
+	if (wpabuf_tailroom(conn->msg) > 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Need %u more octets of message payload",
+			   (unsigned int) wpabuf_tailroom(conn->msg));
+		return;
+	}
+
+	conn->msg_len_octets = 0;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
+	if (wpabuf_len(conn->msg) < 1) {
+		dpp_connection_remove(conn);
+		return;
+	}
+
+	pos = wpabuf_head(conn->msg);
+	switch (*pos) {
+	case WLAN_PA_VENDOR_SPECIFIC:
+		if (dpp_controller_rx_action(conn, pos + 1,
+					     wpabuf_len(conn->msg) - 1) < 0)
+			dpp_connection_remove(conn);
+		break;
+	case WLAN_PA_GAS_INITIAL_REQ:
+		if (dpp_controller_rx_gas_req(conn, pos + 1,
+					      wpabuf_len(conn->msg) - 1) < 0)
+			dpp_connection_remove(conn);
+		break;
+	case WLAN_PA_GAS_INITIAL_RESP:
+		if (dpp_rx_gas_resp(conn, pos + 1,
+				    wpabuf_len(conn->msg) - 1) < 0)
+			dpp_connection_remove(conn);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
+			   *pos);
+		break;
+	}
+}
+
+
+static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct dpp_controller *ctrl = eloop_ctx;
+	struct sockaddr_in addr;
+	socklen_t addr_len = sizeof(addr);
+	int fd;
+	struct dpp_connection *conn;
+
+	wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
+
+	fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
+	if (fd < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to accept new connection: %s",
+			   strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn)
+		goto fail;
+
+	conn->global = ctrl->global;
+	conn->ctrl = ctrl;
+	conn->sock = fd;
+
+	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+				dpp_controller_rx, conn, NULL) < 0)
+		goto fail;
+	conn->read_eloop = 1;
+
+	/* TODO: eloop timeout to expire connections that do not complete in
+	 * reasonable time */
+	dl_list_add(&ctrl->conn, &conn->list);
+	return;
+
+fail:
+	close(fd);
+	os_free(conn);
+}
+
+
+int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
+		 const struct hostapd_ip_addr *addr, int port)
+{
+	struct dpp_connection *conn;
+	struct sockaddr_storage saddr;
+	socklen_t addrlen;
+	const u8 *hdr, *pos, *end;
+	char txt[100];
+
+	wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
+		   hostapd_ip_txt(addr, txt, sizeof(txt)), port);
+	if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
+				   addr, port) < 0) {
+		dpp_auth_deinit(auth);
+		return -1;
+	}
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn) {
+		dpp_auth_deinit(auth);
+		return -1;
+	}
+
+	conn->global = dpp;
+	conn->auth = auth;
+	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (conn->sock < 0)
+		goto fail;
+
+	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
+		if (errno != EINPROGRESS) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+				   strerror(errno));
+			goto fail;
+		}
+
+		/*
+		 * Continue connecting in the background; eloop will call us
+		 * once the connection is ready (or failed).
+		 */
+	}
+
+	if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+				dpp_conn_tx_ready, conn, NULL) < 0)
+		goto fail;
+	conn->write_eloop = 1;
+
+	hdr = wpabuf_head(auth->req_msg);
+	end = hdr + wpabuf_len(auth->req_msg);
+	hdr += 2; /* skip Category and Actiom */
+	pos = hdr + DPP_HDR_LEN;
+	conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
+	if (!conn->msg_out)
+		goto fail;
+	/* Message will be sent in dpp_conn_tx_ready() */
+
+	/* TODO: eloop timeout to clear a connection if it does not complete
+	 * properly */
+	dl_list_add(&dpp->tcp_init, &conn->list);
+	return 0;
+fail:
+	dpp_connection_free(conn);
+	return -1;
+}
+
+
+int dpp_controller_start(struct dpp_global *dpp,
+			 struct dpp_controller_config *config)
+{
+	struct dpp_controller *ctrl;
+	int on = 1;
+	struct sockaddr_in sin;
+	int port;
+
+	if (!dpp || dpp->controller)
+		return -1;
+
+	ctrl = os_zalloc(sizeof(*ctrl));
+	if (!ctrl)
+		return -1;
+	ctrl->global = dpp;
+	if (config->configurator_params)
+		ctrl->configurator_params =
+			os_strdup(config->configurator_params);
+	dl_list_init(&ctrl->conn);
+	/* TODO: configure these somehow */
+	ctrl->allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
+	ctrl->qr_mutual = 0;
+
+	ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
+	if (ctrl->sock < 0)
+		goto fail;
+
+	if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
+		       &on, sizeof(on)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: setsockopt(SO_REUSEADDR) failed: %s",
+			   strerror(errno));
+		/* try to continue anyway */
+	}
+
+	if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
+		wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	/* TODO: IPv6 */
+	os_memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = INADDR_ANY;
+	port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
+	sin.sin_port = htons(port);
+	if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Failed to bind Controller TCP port: %s",
+			   strerror(errno));
+		goto fail;
+	}
+	if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
+	    fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
+	    eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
+				dpp_controller_tcp_cb, ctrl, NULL))
+		goto fail;
+
+	dpp->controller = ctrl;
+	wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
+	return 0;
+fail:
+	dpp_controller_free(ctrl);
+	return -1;
+}
+
+
+void dpp_controller_stop(struct dpp_global *dpp)
+{
+	if (dpp) {
+		dpp_controller_free(dpp->controller);
+		dpp->controller = NULL;
+	}
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 5a6d8cc..cb788ae 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -18,9 +18,11 @@
 #include "crypto/sha256.h"
 
 struct crypto_ecdh;
+struct hostapd_ip_addr;
 struct dpp_global;
 
 #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
+#define DPP_TCP_PORT 7871
 
 enum dpp_public_action_frame_type {
 	DPP_PA_AUTHENTICATION_REQ = 0,
@@ -33,6 +35,7 @@
 	DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
 	DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
 	DPP_PA_CONFIGURATION_RESULT = 11,
+	DPP_PA_CONNECTION_STATUS_RESULT = 12,
 };
 
 enum dpp_attribute_id {
@@ -62,6 +65,8 @@
 	DPP_ATTR_CHANNEL = 0x1018,
 	DPP_ATTR_PROTOCOL_VERSION = 0x1019,
 	DPP_ATTR_ENVELOPED_DATA = 0x101A,
+	DPP_ATTR_SEND_CONN_STATUS = 0x101B,
+	DPP_ATTR_CONN_STATUS = 0x101C,
 };
 
 enum dpp_status_error {
@@ -75,6 +80,7 @@
 	DPP_STATUS_INVALID_CONNECTOR = 7,
 	DPP_STATUS_NO_MATCH = 8,
 	DPP_STATUS_CONFIG_REJECTED = 9,
+	DPP_STATUS_NO_AP = 10,
 };
 
 #define DPP_CAPAB_ENROLLEE BIT(0)
@@ -100,6 +106,7 @@
 enum dpp_bootstrap_type {
 	DPP_BOOTSTRAP_QR_CODE,
 	DPP_BOOTSTRAP_PKEX,
+	DPP_BOOTSTRAP_NFC_URI,
 };
 
 struct dpp_bootstrap_info {
@@ -155,10 +162,18 @@
 	DPP_AKM_PSK_SAE_DPP,
 };
 
+enum dpp_netrole {
+	DPP_NETROLE_STA,
+	DPP_NETROLE_AP,
+	DPP_NETROLE_CONFIGURATOR,
+};
+
 struct dpp_configuration {
 	u8 ssid[32];
 	size_t ssid_len;
+	int ssid_charset;
 	enum dpp_akm akm;
+	enum dpp_netrole netrole;
 
 	/* For DPP configuration (connector) */
 	os_time_t netaccesskey_expiry;
@@ -172,6 +187,9 @@
 	int psk_set;
 };
 
+#define DPP_MAX_CONF_OBJ 10
+#define DPP_MAX_CHANNELS 32
+
 struct dpp_authentication {
 	void *msg_ctx;
 	u8 peer_version;
@@ -220,28 +238,40 @@
 	int remove_on_tx_status;
 	int connect_on_tx_status;
 	int waiting_conf_result;
+	int waiting_conn_status_result;
 	int auth_success;
 	struct wpabuf *conf_req;
 	const struct wpabuf *conf_resp; /* owned by GAS server */
 	struct dpp_configuration *conf_ap;
+	struct dpp_configuration *conf2_ap;
 	struct dpp_configuration *conf_sta;
+	struct dpp_configuration *conf2_sta;
 	struct dpp_configurator *conf;
-	char *connector; /* received signedConnector */
-	u8 ssid[SSID_MAX_LEN];
-	u8 ssid_len;
-	char passphrase[64];
-	u8 psk[PMK_LEN];
-	int psk_set;
-	enum dpp_akm akm;
+	struct dpp_config_obj {
+		char *connector; /* received signedConnector */
+		u8 ssid[SSID_MAX_LEN];
+		u8 ssid_len;
+		int ssid_charset;
+		char passphrase[64];
+		u8 psk[PMK_LEN];
+		int psk_set;
+		enum dpp_akm akm;
+		struct wpabuf *c_sign_key;
+	} conf_obj[DPP_MAX_CONF_OBJ];
+	unsigned int num_conf_obj;
 	struct wpabuf *net_access_key;
 	os_time_t net_access_key_expiry;
-	struct wpabuf *c_sign_key;
+	int send_conn_status;
+	int conn_status_requested;
+	int akm_use_selector;
 #ifdef CONFIG_TESTING_OPTIONS
 	char *config_obj_override;
 	char *discovery_override;
 	char *groups_override;
 	unsigned int ignore_netaccesskey_mismatch:1;
 #endif /* CONFIG_TESTING_OPTIONS */
+	unsigned short band_list[DPP_MAX_CHANNELS];
+	int band_list_size;
 };
 
 struct dpp_configurator {
@@ -259,6 +289,22 @@
 	size_t pmk_len;
 };
 
+struct dpp_relay_config {
+	const struct hostapd_ip_addr *ipaddr;
+	const u8 *pkhash;
+
+	void *cb_ctx;
+	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
+		   size_t len);
+	void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token, int prot,
+			    struct wpabuf *buf);
+};
+
+struct dpp_controller_config {
+	const char *configurator_params;
+	int tcp_port;
+};
+
 #ifdef CONFIG_TESTING_OPTIONS
 enum dpp_test_behavior {
 	DPP_TEST_DISABLED = 0,
@@ -373,7 +419,6 @@
 			    const char *chan_list);
 int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
 int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
-struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
 char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
 		  const u8 *privkey, size_t privkey_len);
 struct hostapd_hw_modes;
@@ -395,6 +440,10 @@
 		 const u8 *attr_start, size_t attr_len);
 struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
 				   const char *json);
+struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
+					  const char *name,
+					  enum dpp_netrole netrole,
+					  const char *mud_url, int *opclasses);
 int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 		     const u8 *attr_start, size_t attr_len);
 int dpp_notify_new_qr_code(struct dpp_authentication *auth,
@@ -421,12 +470,23 @@
 					 const u8 *attr_start, size_t attr_len);
 struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
 				      enum dpp_status_error status);
+enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth,
+						const u8 *hdr,
+						const u8 *attr_start,
+						size_t attr_len,
+						u8 *ssid, size_t *ssid_len,
+						char **channel_list);
+struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
+					     enum dpp_status_error result,
+					     const u8 *ssid, size_t ssid_len,
+					     const char *channel_list);
 struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
 			      size_t len);
 const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
 int dpp_check_attrs(const u8 *buf, size_t len);
 int dpp_key_expired(const char *timestamp, os_time_t *expiry);
 const char * dpp_akm_str(enum dpp_akm akm);
+const char * dpp_akm_selector_str(enum dpp_akm akm);
 int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
 			     size_t buflen);
 void dpp_configurator_free(struct dpp_configurator *conf);
@@ -479,6 +539,8 @@
 
 struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
 					    const char *uri);
+struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
+					    const char *uri);
 int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd);
 struct dpp_bootstrap_info *
 dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id);
@@ -497,7 +559,26 @@
 int dpp_configurator_remove(struct dpp_global *dpp, const char *id);
 int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
 				char *buf, size_t buflen);
-struct dpp_global * dpp_global_init(void);
+int dpp_relay_add_controller(struct dpp_global *dpp,
+			     struct dpp_relay_config *config);
+int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
+			const u8 *buf, size_t len, unsigned int freq,
+			const u8 *i_bootstrap, const u8 *r_bootstrap);
+int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
+			 size_t data_len);
+int dpp_controller_start(struct dpp_global *dpp,
+			 struct dpp_controller_config *config);
+void dpp_controller_stop(struct dpp_global *dpp);
+int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
+		 const struct hostapd_ip_addr *addr, int port);
+
+struct dpp_global_config {
+	void *msg_ctx;
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+};
+
+struct dpp_global * dpp_global_init(struct dpp_global_config *config);
 void dpp_global_clear(struct dpp_global *dpp);
 void dpp_global_deinit(struct dpp_global *dpp);
 
diff --git a/src/common/dragonfly.c b/src/common/dragonfly.c
new file mode 100644
index 0000000..547be66
--- /dev/null
+++ b/src/common/dragonfly.c
@@ -0,0 +1,215 @@
+/*
+ * Shared Dragonfly functionality
+ * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/const_time.h"
+#include "crypto/crypto.h"
+#include "dragonfly.h"
+
+
+int dragonfly_suitable_group(int group, int ecc_only)
+{
+	/* Enforce REVmd rules on which SAE groups are suitable for production
+	 * purposes: FFC groups whose prime is >= 3072 bits and ECC groups
+	 * defined over a prime field whose prime is >= 256 bits. Furthermore,
+	 * ECC groups defined over a characteristic 2 finite field and ECC
+	 * groups with a co-factor greater than 1 are not suitable. Disable
+	 * groups that use Brainpool curves as well for now since they leak more
+	 * timing information due to the prime not being close to a power of
+	 * two. */
+	return group == 19 || group == 20 || group == 21 ||
+		(!ecc_only &&
+		 (group == 15 || group == 16 || group == 17 || group == 18));
+}
+
+
+unsigned int dragonfly_min_pwe_loop_iter(int group)
+{
+	if (group == 22 || group == 23 || group == 24) {
+		/* FFC groups for which pwd-value is likely to be >= p
+		 * frequently */
+		return 40;
+	}
+
+	if (group == 1 || group == 2 || group == 5 || group == 14 ||
+	    group == 15 || group == 16 || group == 17 || group == 18) {
+		/* FFC groups that have prime that is close to a power of two */
+		return 1;
+	}
+
+	/* Default to 40 (this covers most ECC groups) */
+	return 40;
+}
+
+
+int dragonfly_get_random_qr_qnr(const struct crypto_bignum *prime,
+				struct crypto_bignum **qr,
+				struct crypto_bignum **qnr)
+{
+	*qr = *qnr = NULL;
+
+	while (!(*qr) || !(*qnr)) {
+		struct crypto_bignum *tmp;
+		int res;
+
+		tmp = crypto_bignum_init();
+		if (!tmp || crypto_bignum_rand(tmp, prime) < 0) {
+			crypto_bignum_deinit(tmp, 0);
+			break;
+		}
+
+		res = crypto_bignum_legendre(tmp, prime);
+		if (res == 1 && !(*qr))
+			*qr = tmp;
+		else if (res == -1 && !(*qnr))
+			*qnr = tmp;
+		else
+			crypto_bignum_deinit(tmp, 0);
+	}
+
+	if (*qr && *qnr)
+		return 0;
+	crypto_bignum_deinit(*qr, 0);
+	crypto_bignum_deinit(*qnr, 0);
+	*qr = *qnr = NULL;
+	return -1;
+}
+
+
+static struct crypto_bignum *
+dragonfly_get_rand_1_to_p_1(const struct crypto_bignum *prime)
+{
+	struct crypto_bignum *tmp, *pm1, *one;
+
+	tmp = crypto_bignum_init();
+	pm1 = crypto_bignum_init();
+	one = crypto_bignum_init_set((const u8 *) "\x01", 1);
+	if (!tmp || !pm1 || !one ||
+	    crypto_bignum_sub(prime, one, pm1) < 0 ||
+	    crypto_bignum_rand(tmp, pm1) < 0 ||
+	    crypto_bignum_add(tmp, one, tmp) < 0) {
+		crypto_bignum_deinit(tmp, 0);
+		tmp = NULL;
+	}
+
+	crypto_bignum_deinit(pm1, 0);
+	crypto_bignum_deinit(one, 0);
+	return tmp;
+}
+
+
+int dragonfly_is_quadratic_residue_blind(struct crypto_ec *ec,
+					 const u8 *qr, const u8 *qnr,
+					 const struct crypto_bignum *val)
+{
+	struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
+	int check, res = -1;
+	u8 qr_or_qnr_bin[DRAGONFLY_MAX_ECC_PRIME_LEN];
+	const struct crypto_bignum *prime;
+	size_t prime_len;
+	unsigned int mask;
+
+	prime = crypto_ec_get_prime(ec);
+	prime_len = crypto_ec_prime_len(ec);
+
+	/*
+	 * Use a blinding technique to mask val while determining whether it is
+	 * a quadratic residue modulo p to avoid leaking timing information
+	 * while determining the Legendre symbol.
+	 *
+	 * v = val
+	 * r = a random number between 1 and p-1, inclusive
+	 * num = (v * r * r) modulo p
+	 */
+	r = dragonfly_get_rand_1_to_p_1(prime);
+	if (!r)
+		return -1;
+
+	num = crypto_bignum_init();
+	if (!num ||
+	    crypto_bignum_mulmod(val, r, prime, num) < 0 ||
+	    crypto_bignum_mulmod(num, r, prime, num) < 0)
+		goto fail;
+
+	/*
+	 * Need to minimize differences in handling different cases, so try to
+	 * avoid branches and timing differences.
+	 *
+	 * If r is odd:
+	 * num = (num * qr) module p
+	 * LGR(num, p) = 1 ==> quadratic residue
+	 * else:
+	 * num = (num * qnr) module p
+	 * LGR(num, p) = -1 ==> quadratic residue
+	 *
+	 * mask is set to !odd(r)
+	 */
+	mask = const_time_is_zero(crypto_bignum_is_odd(r));
+	const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
+	qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
+	if (!qr_or_qnr ||
+	    crypto_bignum_mulmod(num, qr_or_qnr, prime, num) < 0)
+		goto fail;
+	/* branchless version of check = odd(r) ? 1 : -1, */
+	check = const_time_select_int(mask, -1, 1);
+
+	/* Determine the Legendre symbol on the masked value */
+	res = crypto_bignum_legendre(num, prime);
+	if (res == -2) {
+		res = -1;
+		goto fail;
+	}
+	/* branchless version of res = res == check
+	 * (res is -1, 0, or 1; check is -1 or 1) */
+	mask = const_time_eq(res, check);
+	res = const_time_select_int(mask, 1, 0);
+fail:
+	crypto_bignum_deinit(num, 1);
+	crypto_bignum_deinit(r, 1);
+	crypto_bignum_deinit(qr_or_qnr, 1);
+	return res;
+}
+
+
+static int dragonfly_get_rand_2_to_r_1(struct crypto_bignum *val,
+				       const struct crypto_bignum *order)
+{
+	return crypto_bignum_rand(val, order) == 0 &&
+		!crypto_bignum_is_zero(val) &&
+		!crypto_bignum_is_one(val);
+}
+
+
+int dragonfly_generate_scalar(const struct crypto_bignum *order,
+			      struct crypto_bignum *_rand,
+			      struct crypto_bignum *_mask,
+			      struct crypto_bignum *scalar)
+{
+	int count;
+
+	/* Select two random values rand,mask such that 1 < rand,mask < r and
+	 * rand + mask mod r > 1. */
+	for (count = 0; count < 100; count++) {
+		if (dragonfly_get_rand_2_to_r_1(_rand, order) &&
+		    dragonfly_get_rand_2_to_r_1(_mask, order) &&
+		    crypto_bignum_add(_rand, _mask, scalar) == 0 &&
+		    crypto_bignum_mod(scalar, order, scalar) == 0 &&
+		    !crypto_bignum_is_zero(scalar) &&
+		    !crypto_bignum_is_one(scalar))
+			return 0;
+	}
+
+	/* This should not be reachable in practice if the random number
+	 * generation is working. */
+	wpa_printf(MSG_INFO,
+		   "dragonfly: Unable to get randomness for own scalar");
+	return -1;
+}
diff --git a/src/common/dragonfly.h b/src/common/dragonfly.h
new file mode 100644
index 0000000..ec3dd59
--- /dev/null
+++ b/src/common/dragonfly.h
@@ -0,0 +1,31 @@
+/*
+ * Shared Dragonfly functionality
+ * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRAGONFLY_H
+#define DRAGONFLY_H
+
+#define DRAGONFLY_MAX_ECC_PRIME_LEN 66
+
+struct crypto_bignum;
+struct crypto_ec;
+
+int dragonfly_suitable_group(int group, int ecc_only);
+unsigned int dragonfly_min_pwe_loop_iter(int group);
+int dragonfly_get_random_qr_qnr(const struct crypto_bignum *prime,
+				struct crypto_bignum **qr,
+				struct crypto_bignum **qnr);
+int dragonfly_is_quadratic_residue_blind(struct crypto_ec *ec,
+					 const u8 *qr, const u8 *qnr,
+					 const struct crypto_bignum *val);
+int dragonfly_generate_scalar(const struct crypto_bignum *order,
+			      struct crypto_bignum *_rand,
+			      struct crypto_bignum *_mask,
+			      struct crypto_bignum *scalar);
+
+#endif /* DRAGONFLY_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 49ed806..19593a5 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -40,23 +40,32 @@
 }
 
 
-struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
-						  int freq, int *chan)
+struct hostapd_channel_data *
+hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
+		    struct hostapd_hw_modes *hw_features, int num_hw_features)
 {
-	int i;
+	int i, j;
 
 	if (chan)
 		*chan = 0;
 
-	if (!mode)
+	if (!hw_features)
 		return NULL;
 
-	for (i = 0; i < mode->num_channels; i++) {
-		struct hostapd_channel_data *ch = &mode->channels[i];
-		if (ch->freq == freq) {
-			if (chan)
-				*chan = ch->chan;
-			return ch;
+	for (j = 0; j < num_hw_features; j++) {
+		struct hostapd_hw_modes *curr_mode = &hw_features[j];
+
+		if (curr_mode->mode != mode)
+			continue;
+		for (i = 0; i < curr_mode->num_channels; i++) {
+			struct hostapd_channel_data *ch =
+				&curr_mode->channels[i];
+
+			if (ch->freq == freq) {
+				if (chan)
+					*chan = ch->chan;
+				return ch;
+			}
 		}
 	}
 
@@ -74,29 +83,33 @@
 }
 
 
-int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
+int hw_get_chan(enum hostapd_hw_mode mode, int freq,
+		struct hostapd_hw_modes *hw_features, int num_hw_features)
 {
 	int chan;
 
-	hw_get_channel_freq(mode, freq, &chan);
+	hw_get_channel_freq(mode, freq, &chan, hw_features, num_hw_features);
 
 	return chan;
 }
 
 
-int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
-			      int sec_chan)
+int allowed_ht40_channel_pair(enum hostapd_hw_mode mode,
+			      struct hostapd_channel_data *p_chan,
+			      struct hostapd_channel_data *s_chan)
 {
 	int ok, first;
 	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
 			  149, 157, 165, 184, 192 };
 	size_t k;
-	struct hostapd_channel_data *p_chan, *s_chan;
-	const int ht40_plus = pri_chan < sec_chan;
+	int ht40_plus, pri_chan, sec_chan;
 
-	p_chan = hw_get_channel_chan(mode, pri_chan, NULL);
-	if (!p_chan)
+	if (!p_chan || !s_chan)
 		return 0;
+	pri_chan = p_chan->chan;
+	sec_chan = s_chan->chan;
+
+	ht40_plus = pri_chan < sec_chan;
 
 	if (pri_chan == sec_chan || !sec_chan) {
 		if (chan_pri_allowed(p_chan))
@@ -107,13 +120,9 @@
 		return 0;
 	}
 
-	s_chan = hw_get_channel_chan(mode, sec_chan, NULL);
-	if (!s_chan)
-		return 0;
-
 	wpa_printf(MSG_DEBUG,
-		   "HT40: control channel: %d  secondary channel: %d",
-		   pri_chan, sec_chan);
+		   "HT40: control channel: %d (%d MHz), secondary channel: %d (%d MHz)",
+		   pri_chan, p_chan->freq, sec_chan, s_chan->freq);
 
 	/* Verify that HT40 secondary channel is an allowed 20 MHz
 	 * channel */
@@ -131,7 +140,7 @@
 	 * 2.4 GHz rules allow all cases where the secondary channel fits into
 	 * the list of allowed channels (already checked above).
 	 */
-	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+	if (mode != HOSTAPD_MODE_IEEE80211A)
 		return 1;
 
 	first = pri_chan < sec_chan ? pri_chan : sec_chan;
@@ -176,22 +185,19 @@
 }
 
 
-int check_40mhz_5g(struct hostapd_hw_modes *mode,
-		   struct wpa_scan_results *scan_res, int pri_chan,
-		   int sec_chan)
+int check_40mhz_5g(struct wpa_scan_results *scan_res,
+		   struct hostapd_channel_data *pri_chan,
+		   struct hostapd_channel_data *sec_chan)
 {
-	int pri_freq, sec_freq, pri_bss, sec_bss;
+	int pri_bss, sec_bss;
 	int bss_pri_chan, bss_sec_chan;
 	size_t i;
 	int match;
 
-	if (!mode || !scan_res || !pri_chan || !sec_chan ||
-	    pri_chan == sec_chan)
+	if (!scan_res || !pri_chan || !sec_chan ||
+	    pri_chan->freq == sec_chan->freq)
 		return 0;
 
-	pri_freq = hw_get_freq(mode, pri_chan);
-	sec_freq = hw_get_freq(mode, sec_chan);
-
 	/*
 	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
 	 * channel, but not on selected PRI channel.
@@ -199,9 +205,9 @@
 	pri_bss = sec_bss = 0;
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *bss = scan_res->res[i];
-		if (bss->freq == pri_freq)
+		if (bss->freq == pri_chan->freq)
 			pri_bss++;
-		else if (bss->freq == sec_freq)
+		else if (bss->freq == sec_chan->freq)
 			sec_bss++;
 	}
 	if (sec_bss && !pri_bss) {
@@ -219,8 +225,8 @@
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *bss = scan_res->res[i];
 		get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
-		if (pri_chan == bss_pri_chan &&
-		    sec_chan == bss_sec_chan) {
+		if (pri_chan->chan == bss_pri_chan &&
+		    sec_chan->chan == bss_sec_chan) {
 			match = 1;
 			break;
 		}
@@ -229,8 +235,8 @@
 		for (i = 0; i < scan_res->num; i++) {
 			struct wpa_scan_res *bss = scan_res->res[i];
 			get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
-			if (pri_chan == bss_sec_chan &&
-			    sec_chan == bss_pri_chan) {
+			if (pri_chan->chan == bss_sec_chan &&
+			    sec_chan->chan == bss_pri_chan) {
 				wpa_printf(MSG_INFO, "Switch own primary and "
 					   "secondary channel due to BSS "
 					   "overlap with " MACSTR,
@@ -273,12 +279,87 @@
 }
 
 
+/*
+ * Returns:
+ * 0: no impact
+ * 1: overlapping BSS
+ * 2: overlapping BSS with 40 MHz intolerant advertisement
+ */
+int check_bss_coex_40mhz(struct wpa_scan_res *bss, int pri_freq, int sec_freq)
+{
+	int affected_start, affected_end;
+	struct ieee802_11_elems elems;
+	int pri_chan, sec_chan;
+	int pri = bss->freq;
+	int sec = pri;
+
+	if (pri_freq == sec_freq)
+		return 1;
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+
+	/* Check for overlapping 20 MHz BSS */
+	if (check_20mhz_bss(bss, pri_freq, affected_start, affected_end)) {
+		wpa_printf(MSG_DEBUG, "Overlapping 20 MHz BSS is found");
+		return 1;
+	}
+
+	get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+	if (sec_chan) {
+		if (sec_chan < pri_chan)
+			sec = pri - 20;
+		else
+			sec = pri + 20;
+	}
+
+	if ((pri < affected_start || pri > affected_end) &&
+	    (sec < affected_start || sec > affected_end))
+		return 0; /* not within affected channel range */
+
+	wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+		   " freq=%d pri=%d sec=%d",
+		   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+	if (sec_chan) {
+		if (pri_freq != pri || sec_freq != sec) {
+			wpa_printf(MSG_DEBUG,
+				   "40 MHz pri/sec mismatch with BSS "
+				   MACSTR
+				   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+				   MAC2STR(bss->bssid),
+				   pri, sec, pri_chan,
+				   sec > pri ? '+' : '-',
+				   pri_freq, sec_freq);
+			return 1;
+		}
+	}
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (elems.ht_capabilities) {
+		struct ieee80211_ht_capabilities *ht_cap =
+			(struct ieee80211_ht_capabilities *)
+			elems.ht_capabilities;
+
+		if (le_to_host16(ht_cap->ht_capabilities_info) &
+		    HT_CAP_INFO_40MHZ_INTOLERANT) {
+			wpa_printf(MSG_DEBUG,
+				   "40 MHz Intolerant is set on channel %d in BSS "
+				   MACSTR, pri, MAC2STR(bss->bssid));
+			return 2;
+		}
+	}
+
+	return 0;
+}
+
+
 int check_40mhz_2g4(struct hostapd_hw_modes *mode,
 		    struct wpa_scan_results *scan_res, int pri_chan,
 		    int sec_chan)
 {
 	int pri_freq, sec_freq;
-	int affected_start, affected_end;
 	size_t i;
 
 	if (!mode || !scan_res || !pri_chan || !sec_chan ||
@@ -288,70 +369,12 @@
 	pri_freq = hw_get_freq(mode, pri_chan);
 	sec_freq = hw_get_freq(mode, sec_chan);
 
-	affected_start = (pri_freq + sec_freq) / 2 - 25;
-	affected_end = (pri_freq + sec_freq) / 2 + 25;
 	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
-		   affected_start, affected_end);
+		   (pri_freq + sec_freq) / 2 - 25,
+		   (pri_freq + sec_freq) / 2 + 25);
 	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		int pri = bss->freq;
-		int sec = pri;
-		struct ieee802_11_elems elems;
-
-		/* Check for overlapping 20 MHz BSS */
-		if (check_20mhz_bss(bss, pri_freq, affected_start,
-				    affected_end)) {
-			wpa_printf(MSG_DEBUG,
-				   "Overlapping 20 MHz BSS is found");
+		if (check_bss_coex_40mhz(scan_res->res[i], pri_freq, sec_freq))
 			return 0;
-		}
-
-		get_pri_sec_chan(bss, &pri_chan, &sec_chan);
-
-		if (sec_chan) {
-			if (sec_chan < pri_chan)
-				sec = pri - 20;
-			else
-				sec = pri + 20;
-		}
-
-		if ((pri < affected_start || pri > affected_end) &&
-		    (sec < affected_start || sec > affected_end))
-			continue; /* not within affected channel range */
-
-		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
-			   " freq=%d pri=%d sec=%d",
-			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
-
-		if (sec_chan) {
-			if (pri_freq != pri || sec_freq != sec) {
-				wpa_printf(MSG_DEBUG,
-					   "40 MHz pri/sec mismatch with BSS "
-					   MACSTR
-					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
-					   MAC2STR(bss->bssid),
-					   pri, sec, pri_chan,
-					   sec > pri ? '+' : '-',
-					   pri_freq, sec_freq);
-				return 0;
-			}
-		}
-
-		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
-				       0);
-		if (elems.ht_capabilities) {
-			struct ieee80211_ht_capabilities *ht_cap =
-				(struct ieee80211_ht_capabilities *)
-				elems.ht_capabilities;
-
-			if (le_to_host16(ht_cap->ht_capabilities_info) &
-			    HT_CAP_INFO_40MHZ_INTOLERANT) {
-				wpa_printf(MSG_DEBUG,
-					   "40 MHz Intolerant is set on channel %d in BSS "
-					   MACSTR, pri, MAC2STR(bss->bssid));
-				return 0;
-			}
-		}
 	}
 
 	return 1;
@@ -360,31 +383,107 @@
 
 int hostapd_set_freq_params(struct hostapd_freq_params *data,
 			    enum hostapd_hw_mode mode,
-			    int freq, int channel, int ht_enabled,
-			    int vht_enabled, int sec_channel_offset,
-			    int vht_oper_chwidth, int center_segment0,
-			    int center_segment1, u32 vht_caps)
+			    int freq, int channel, int enable_edmg,
+			    u8 edmg_channel, int ht_enabled,
+			    int vht_enabled, int he_enabled,
+			    int sec_channel_offset,
+			    int oper_chwidth, int center_segment0,
+			    int center_segment1, u32 vht_caps,
+			    struct he_capabilities *he_cap)
 {
+	if (!he_cap)
+		he_enabled = 0;
 	os_memset(data, 0, sizeof(*data));
 	data->mode = mode;
 	data->freq = freq;
 	data->channel = channel;
 	data->ht_enabled = ht_enabled;
 	data->vht_enabled = vht_enabled;
+	data->he_enabled = he_enabled;
 	data->sec_channel_offset = sec_channel_offset;
 	data->center_freq1 = freq + sec_channel_offset * 10;
 	data->center_freq2 = 0;
 	data->bandwidth = sec_channel_offset ? 40 : 20;
 
-	if (data->vht_enabled) switch (vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
+	hostapd_encode_edmg_chan(enable_edmg, edmg_channel, channel,
+				 &data->edmg);
+
+	if (is_6ghz_freq(freq)) {
+		if (!data->he_enabled) {
+			wpa_printf(MSG_ERROR,
+				   "Can't set 6 GHz mode - HE isn't enabled");
+			return -1;
+		}
+
+		if (center_idx_to_bw_6ghz(channel) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid control channel for 6 GHz band");
+			return -1;
+		}
+
+		if (!center_segment0) {
+			if (center_segment1) {
+				wpa_printf(MSG_ERROR,
+					   "Segment 0 center frequency isn't set");
+				return -1;
+			}
+
+			data->center_freq1 = data->freq;
+			data->bandwidth = 20;
+		} else {
+			int freq1, freq2 = 0;
+			int bw = center_idx_to_bw_6ghz(center_segment0);
+
+			if (bw < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Invalid center frequency index for 6 GHz");
+				return -1;
+			}
+
+			freq1 = ieee80211_chan_to_freq(NULL, 131,
+						       center_segment0);
+			if (freq1 < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Invalid segment 0 center frequency for 6 GHz");
+				return -1;
+			}
+
+			if (center_segment1) {
+				if (center_idx_to_bw_6ghz(center_segment1) != 2 ||
+				    bw != 2) {
+					wpa_printf(MSG_ERROR,
+						   "6 GHz 80+80 MHz configuration doesn't use valid 80 MHz channels");
+					return -1;
+				}
+
+				freq2 = ieee80211_chan_to_freq(NULL, 131,
+							       center_segment1);
+				if (freq2 < 0) {
+					wpa_printf(MSG_ERROR,
+						   "Invalid segment 1 center frequency for UHB");
+					return -1;
+				}
+			}
+
+			data->bandwidth = (1 << (u8) bw) * 20;
+			data->center_freq1 = freq1;
+			data->center_freq2 = freq2;
+			data->ht_enabled = 0;
+			data->vht_enabled = 0;
+		}
+
+		return 0;
+	}
+
+	if (data->vht_enabled) switch (oper_chwidth) {
+	case CHANWIDTH_USE_HT:
 		if (center_segment1 ||
 		    (center_segment0 != 0 &&
 		     5000 + center_segment0 * 5 != data->center_freq1 &&
 		     2407 + center_segment0 * 5 != data->center_freq1))
 			return -1;
 		break;
-	case VHT_CHANWIDTH_80P80MHZ:
+	case CHANWIDTH_80P80MHZ:
 		if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
 			wpa_printf(MSG_ERROR,
 				   "80+80 channel width is not supported!");
@@ -395,11 +494,11 @@
 			return -1;
 		data->center_freq2 = 5000 + center_segment1 * 5;
 		/* fall through */
-	case VHT_CHANWIDTH_80MHZ:
+	case CHANWIDTH_80MHZ:
 		data->bandwidth = 80;
-		if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ &&
+		if ((oper_chwidth == CHANWIDTH_80MHZ &&
 		     center_segment1) ||
-		    (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ &&
+		    (oper_chwidth == CHANWIDTH_80P80MHZ &&
 		     !center_segment1) ||
 		    !sec_channel_offset)
 			return -1;
@@ -432,7 +531,7 @@
 				return -1;
 		}
 		break;
-	case VHT_CHANWIDTH_160MHZ:
+	case CHANWIDTH_160MHZ:
 		data->bandwidth = 160;
 		if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
 				  VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index eb1f1c5..e57a8d6 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -14,27 +14,34 @@
 
 struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
 						  int chan, int *freq);
-struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
-						  int freq, int *chan);
+struct hostapd_channel_data *
+hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
+		    struct hostapd_hw_modes *hw_features, int num_hw_features);
 
 int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
-int hw_get_chan(struct hostapd_hw_modes *mode, int freq);
+int hw_get_chan(enum hostapd_hw_mode mode, int freq,
+		struct hostapd_hw_modes *hw_features, int num_hw_features);
 
-int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
-			      int sec_chan);
+int allowed_ht40_channel_pair(enum hostapd_hw_mode mode,
+			      struct hostapd_channel_data *p_chan,
+			      struct hostapd_channel_data *s_chan);
 void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
-int check_40mhz_5g(struct hostapd_hw_modes *mode,
-		   struct wpa_scan_results *scan_res, int pri_chan,
-		   int sec_chan);
+int check_40mhz_5g(struct wpa_scan_results *scan_res,
+		   struct hostapd_channel_data *pri_chan,
+		   struct hostapd_channel_data *sec_chan);
+int check_bss_coex_40mhz(struct wpa_scan_res *bss, int pri_freq, int sec_freq);
 int check_40mhz_2g4(struct hostapd_hw_modes *mode,
 		    struct wpa_scan_results *scan_res, int pri_chan,
 		    int sec_chan);
 int hostapd_set_freq_params(struct hostapd_freq_params *data,
 			    enum hostapd_hw_mode mode,
-			    int freq, int channel, int ht_enabled,
-			    int vht_enabled, int sec_channel_offset,
-			    int vht_oper_chwidth, int center_segment0,
-			    int center_segment1, u32 vht_caps);
+			    int freq, int channel, int edmg, u8 edmg_channel,
+			    int ht_enabled,
+			    int vht_enabled, int he_enabled,
+			    int sec_channel_offset,
+			    int oper_chwidth, int center_segment0,
+			    int center_segment1, u32 vht_caps,
+			    struct he_capabilities *he_caps);
 void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
 		      int disabled);
 int ieee80211ac_cap_check(u32 hw, u32 conf);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index f886551..b330454 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -271,9 +271,17 @@
 		elems->password_id_len = elen;
 		break;
 	case WLAN_EID_EXT_HE_CAPABILITIES:
+		if (elen < HE_CAPABILITIES_IE_MIN_LEN)
+			break;
 		elems->he_capabilities = pos;
 		elems->he_capabilities_len = elen;
 		break;
+	case WLAN_EID_EXT_HE_OPERATION:
+		if (elen < HE_OPERATION_IE_MIN_LEN)
+			break;
+		elems->he_operation = pos;
+		elems->he_operation_len = elen;
+		break;
 	case WLAN_EID_EXT_OCV_OCI:
 		elems->oci = pos;
 		elems->oci_len = elen;
@@ -361,6 +369,10 @@
 			elems->rsn_ie = pos;
 			elems->rsn_ie_len = elen;
 			break;
+		case WLAN_EID_RSNX:
+			elems->rsnxe = pos;
+			elems->rsnxe_len = elen;
+			break;
 		case WLAN_EID_PWR_CAPABILITY:
 			if (elen < 2)
 				break;
@@ -704,7 +716,7 @@
 {
 	u8 op_class;
 
-	return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+	return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT,
 					     &op_class, channel);
 }
 
@@ -714,13 +726,14 @@
  * for HT40 and VHT. DFS channels are not covered.
  * @freq: Frequency (MHz) to convert
  * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
- * @vht: VHT channel width (VHT_CHANWIDTH_*)
+ * @chanwidth: VHT/EDMG channel width (CHANWIDTH_*)
  * @op_class: Buffer for returning operating class
  * @channel: Buffer for returning channel number
  * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
  */
 enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
-						   int sec_channel, int vht,
+						   int sec_channel,
+						   int chanwidth,
 						   u8 *op_class, u8 *channel)
 {
 	u8 vht_opclass;
@@ -734,7 +747,7 @@
 		if ((freq - 2407) % 5)
 			return NUM_HOSTAPD_MODES;
 
-		if (vht)
+		if (chanwidth)
 			return NUM_HOSTAPD_MODES;
 
 		/* 2.407 GHz, channels 1..13 */
@@ -751,7 +764,7 @@
 	}
 
 	if (freq == 2484) {
-		if (sec_channel || vht)
+		if (sec_channel || chanwidth)
 			return NUM_HOSTAPD_MODES;
 
 		*op_class = 82; /* channel 14 */
@@ -768,14 +781,14 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
-	switch (vht) {
-	case VHT_CHANWIDTH_80MHZ:
+	switch (chanwidth) {
+	case CHANWIDTH_80MHZ:
 		vht_opclass = 128;
 		break;
-	case VHT_CHANWIDTH_160MHZ:
+	case CHANWIDTH_160MHZ:
 		vht_opclass = 129;
 		break;
-	case VHT_CHANWIDTH_80P80MHZ:
+	case CHANWIDTH_80P80MHZ:
 		vht_opclass = 130;
 		break;
 	default:
@@ -869,13 +882,57 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
-	/* 56.16 GHz, channel 1..4 */
-	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
-		if (sec_channel || vht)
+	if (freq > 5940 && freq <= 7105) {
+		int bw;
+		u8 idx = (freq - 5940) / 5;
+
+		bw = center_idx_to_bw_6ghz(idx);
+		if (bw < 0)
 			return NUM_HOSTAPD_MODES;
 
-		*channel = (freq - 56160) / 2160;
-		*op_class = 180;
+		*channel = idx;
+		*op_class = 131 + bw;
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 56.16 GHz, channel 1..6 */
+	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
+		if (sec_channel)
+			return NUM_HOSTAPD_MODES;
+
+		switch (chanwidth) {
+		case CHANWIDTH_USE_HT:
+		case CHANWIDTH_2160MHZ:
+			*channel = (freq - 56160) / 2160;
+			*op_class = 180;
+			break;
+		case CHANWIDTH_4320MHZ:
+			/* EDMG channels 9 - 13 */
+			if (freq > 56160 + 2160 * 5)
+				return NUM_HOSTAPD_MODES;
+
+			*channel = (freq - 56160) / 2160 + 8;
+			*op_class = 181;
+			break;
+		case CHANWIDTH_6480MHZ:
+			/* EDMG channels 17 - 20 */
+			if (freq > 56160 + 2160 * 4)
+				return NUM_HOSTAPD_MODES;
+
+			*channel = (freq - 56160) / 2160 + 16;
+			*op_class = 182;
+			break;
+		case CHANWIDTH_8640MHZ:
+			/* EDMG channels 25 - 27 */
+			if (freq > 56160 + 2160 * 3)
+				return NUM_HOSTAPD_MODES;
+
+			*channel = (freq - 56160) / 2160 + 24;
+			*op_class = 183;
+			break;
+		default:
+			return NUM_HOSTAPD_MODES;
+		}
 
 		return HOSTAPD_MODE_IEEE80211AD;
 	}
@@ -887,27 +944,39 @@
 int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
 				  int sec_channel, u8 *op_class, u8 *channel)
 {
-	int vht = CHAN_WIDTH_UNKNOWN;
+	int cw = CHAN_WIDTH_UNKNOWN;
 
 	switch (chanwidth) {
 	case CHAN_WIDTH_UNKNOWN:
 	case CHAN_WIDTH_20_NOHT:
 	case CHAN_WIDTH_20:
 	case CHAN_WIDTH_40:
-		vht = VHT_CHANWIDTH_USE_HT;
+		cw = CHANWIDTH_USE_HT;
 		break;
 	case CHAN_WIDTH_80:
-		vht = VHT_CHANWIDTH_80MHZ;
+		cw = CHANWIDTH_80MHZ;
 		break;
 	case CHAN_WIDTH_80P80:
-		vht = VHT_CHANWIDTH_80P80MHZ;
+		cw = CHANWIDTH_80P80MHZ;
 		break;
 	case CHAN_WIDTH_160:
-		vht = VHT_CHANWIDTH_160MHZ;
+		cw = CHANWIDTH_160MHZ;
+		break;
+	case CHAN_WIDTH_2160:
+		cw = CHANWIDTH_2160MHZ;
+		break;
+	case CHAN_WIDTH_4320:
+		cw = CHANWIDTH_4320MHZ;
+		break;
+	case CHAN_WIDTH_6480:
+		cw = CHANWIDTH_6480MHZ;
+		break;
+	case CHAN_WIDTH_8640:
+		cw = CHANWIDTH_8640MHZ;
 		break;
 	}
 
-	if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class,
+	if (ieee80211_freq_to_channel_ext(freq, sec_channel, cw, op_class,
 					  channel) == NUM_HOSTAPD_MODES) {
 		wpa_printf(MSG_WARNING,
 			   "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
@@ -989,10 +1058,22 @@
 		if (chan < 149 || chan > 165)
 			return -1;
 		return 5000 + 5 * chan;
-	case 34: /* 60 GHz band, channels 1..3 */
-		if (chan < 1 || chan > 3)
+	case 34: /* 60 GHz band, channels 1..8 */
+		if (chan < 1 || chan > 8)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 37: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		if (chan < 9 || chan > 15)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 38: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		if (chan < 17 || chan > 22)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 39: /* 60 GHz band, EDMG CB4, channels 25..29 */
+		if (chan < 25 || chan > 29)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1027,10 +1108,22 @@
 		if (chan < 149 || chan > 169)
 			return -1;
 		return 5000 + 5 * chan;
-	case 18: /* 60 GHz band, channels 1..4 */
-		if (chan < 1 || chan > 4)
+	case 18: /* 60 GHz band, channels 1..6 */
+		if (chan < 1 || chan > 6)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 21: /* 60 GHz band, EDMG CB2, channels 9..11 */
+		if (chan < 9 || chan > 11)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 22: /* 60 GHz band, EDMG CB3, channels 17..18 */
+		if (chan < 17 || chan > 18)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 23: /* 60 GHz band, EDMG CB4, channels 25 */
+		if (chan != 25)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1071,10 +1164,22 @@
 		if (chan < 100 || chan > 140)
 			return -1;
 		return 5000 + 5 * chan;
-	case 59: /* 60 GHz band, channels 1..4 */
-		if (chan < 1 || chan > 3)
+	case 59: /* 60 GHz band, channels 1..6 */
+		if (chan < 1 || chan > 6)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 62: /* 60 GHz band, EDMG CB2, channels 9..11 */
+		if (chan < 9 || chan > 11)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 63: /* 60 GHz band, EDMG CB3, channels 17..18 */
+		if (chan < 17 || chan > 18)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 64: /* 60 GHz band, EDMG CB4, channel 25 */
+		if (chan != 25)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1159,10 +1264,30 @@
 		if (chan < 36 || chan > 128)
 			return -1;
 		return 5000 + 5 * chan;
-	case 180: /* 60 GHz band, channels 1..4 */
-		if (chan < 1 || chan > 4)
+	case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
+	case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
+	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
+	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
+	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
+		if (chan < 1 || chan > 233)
+			return -1;
+		return 5940 + chan * 5;
+	case 180: /* 60 GHz band, channels 1..8 */
+		if (chan < 1 || chan > 8)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		if (chan < 9 || chan > 15)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		if (chan < 17 || chan > 22)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
+		if (chan < 25 || chan > 29)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1317,6 +1442,186 @@
 }
 
 
+const char * reason2str(u16 reason)
+{
+#define R2S(r) case WLAN_REASON_ ## r: return #r;
+	switch (reason) {
+	R2S(UNSPECIFIED)
+	R2S(PREV_AUTH_NOT_VALID)
+	R2S(DEAUTH_LEAVING)
+	R2S(DISASSOC_DUE_TO_INACTIVITY)
+	R2S(DISASSOC_AP_BUSY)
+	R2S(CLASS2_FRAME_FROM_NONAUTH_STA)
+	R2S(CLASS3_FRAME_FROM_NONASSOC_STA)
+	R2S(DISASSOC_STA_HAS_LEFT)
+	R2S(STA_REQ_ASSOC_WITHOUT_AUTH)
+	R2S(PWR_CAPABILITY_NOT_VALID)
+	R2S(SUPPORTED_CHANNEL_NOT_VALID)
+	R2S(BSS_TRANSITION_DISASSOC)
+	R2S(INVALID_IE)
+	R2S(MICHAEL_MIC_FAILURE)
+	R2S(4WAY_HANDSHAKE_TIMEOUT)
+	R2S(GROUP_KEY_UPDATE_TIMEOUT)
+	R2S(IE_IN_4WAY_DIFFERS)
+	R2S(GROUP_CIPHER_NOT_VALID)
+	R2S(PAIRWISE_CIPHER_NOT_VALID)
+	R2S(AKMP_NOT_VALID)
+	R2S(UNSUPPORTED_RSN_IE_VERSION)
+	R2S(INVALID_RSN_IE_CAPAB)
+	R2S(IEEE_802_1X_AUTH_FAILED)
+	R2S(CIPHER_SUITE_REJECTED)
+	R2S(TDLS_TEARDOWN_UNREACHABLE)
+	R2S(TDLS_TEARDOWN_UNSPECIFIED)
+	R2S(SSP_REQUESTED_DISASSOC)
+	R2S(NO_SSP_ROAMING_AGREEMENT)
+	R2S(BAD_CIPHER_OR_AKM)
+	R2S(NOT_AUTHORIZED_THIS_LOCATION)
+	R2S(SERVICE_CHANGE_PRECLUDES_TS)
+	R2S(UNSPECIFIED_QOS_REASON)
+	R2S(NOT_ENOUGH_BANDWIDTH)
+	R2S(DISASSOC_LOW_ACK)
+	R2S(EXCEEDED_TXOP)
+	R2S(STA_LEAVING)
+	R2S(END_TS_BA_DLS)
+	R2S(UNKNOWN_TS_BA)
+	R2S(TIMEOUT)
+	R2S(PEERKEY_MISMATCH)
+	R2S(AUTHORIZED_ACCESS_LIMIT_REACHED)
+	R2S(EXTERNAL_SERVICE_REQUIREMENTS)
+	R2S(INVALID_FT_ACTION_FRAME_COUNT)
+	R2S(INVALID_PMKID)
+	R2S(INVALID_MDE)
+	R2S(INVALID_FTE)
+	R2S(MESH_PEERING_CANCELLED)
+	R2S(MESH_MAX_PEERS)
+	R2S(MESH_CONFIG_POLICY_VIOLATION)
+	R2S(MESH_CLOSE_RCVD)
+	R2S(MESH_MAX_RETRIES)
+	R2S(MESH_CONFIRM_TIMEOUT)
+	R2S(MESH_INVALID_GTK)
+	R2S(MESH_INCONSISTENT_PARAMS)
+	R2S(MESH_INVALID_SECURITY_CAP)
+	R2S(MESH_PATH_ERROR_NO_PROXY_INFO)
+	R2S(MESH_PATH_ERROR_NO_FORWARDING_INFO)
+	R2S(MESH_PATH_ERROR_DEST_UNREACHABLE)
+	R2S(MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS)
+	R2S(MESH_CHANNEL_SWITCH_REGULATORY_REQ)
+	R2S(MESH_CHANNEL_SWITCH_UNSPECIFIED)
+	}
+	return "UNKNOWN";
+#undef R2S
+}
+
+
+const char * status2str(u16 status)
+{
+#define S2S(s) case WLAN_STATUS_ ## s: return #s;
+	switch (status) {
+	S2S(SUCCESS)
+	S2S(UNSPECIFIED_FAILURE)
+	S2S(TDLS_WAKEUP_ALTERNATE)
+	S2S(TDLS_WAKEUP_REJECT)
+	S2S(SECURITY_DISABLED)
+	S2S(UNACCEPTABLE_LIFETIME)
+	S2S(NOT_IN_SAME_BSS)
+	S2S(CAPS_UNSUPPORTED)
+	S2S(REASSOC_NO_ASSOC)
+	S2S(ASSOC_DENIED_UNSPEC)
+	S2S(NOT_SUPPORTED_AUTH_ALG)
+	S2S(UNKNOWN_AUTH_TRANSACTION)
+	S2S(CHALLENGE_FAIL)
+	S2S(AUTH_TIMEOUT)
+	S2S(AP_UNABLE_TO_HANDLE_NEW_STA)
+	S2S(ASSOC_DENIED_RATES)
+	S2S(ASSOC_DENIED_NOSHORT)
+	S2S(SPEC_MGMT_REQUIRED)
+	S2S(PWR_CAPABILITY_NOT_VALID)
+	S2S(SUPPORTED_CHANNEL_NOT_VALID)
+	S2S(ASSOC_DENIED_NO_SHORT_SLOT_TIME)
+	S2S(ASSOC_DENIED_NO_HT)
+	S2S(R0KH_UNREACHABLE)
+	S2S(ASSOC_DENIED_NO_PCO)
+	S2S(ASSOC_REJECTED_TEMPORARILY)
+	S2S(ROBUST_MGMT_FRAME_POLICY_VIOLATION)
+	S2S(UNSPECIFIED_QOS_FAILURE)
+	S2S(DENIED_INSUFFICIENT_BANDWIDTH)
+	S2S(DENIED_POOR_CHANNEL_CONDITIONS)
+	S2S(DENIED_QOS_NOT_SUPPORTED)
+	S2S(REQUEST_DECLINED)
+	S2S(INVALID_PARAMETERS)
+	S2S(REJECTED_WITH_SUGGESTED_CHANGES)
+	S2S(INVALID_IE)
+	S2S(GROUP_CIPHER_NOT_VALID)
+	S2S(PAIRWISE_CIPHER_NOT_VALID)
+	S2S(AKMP_NOT_VALID)
+	S2S(UNSUPPORTED_RSN_IE_VERSION)
+	S2S(INVALID_RSN_IE_CAPAB)
+	S2S(CIPHER_REJECTED_PER_POLICY)
+	S2S(TS_NOT_CREATED)
+	S2S(DIRECT_LINK_NOT_ALLOWED)
+	S2S(DEST_STA_NOT_PRESENT)
+	S2S(DEST_STA_NOT_QOS_STA)
+	S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE)
+	S2S(INVALID_FT_ACTION_FRAME_COUNT)
+	S2S(INVALID_PMKID)
+	S2S(INVALID_MDIE)
+	S2S(INVALID_FTIE)
+	S2S(REQUESTED_TCLAS_NOT_SUPPORTED)
+	S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES)
+	S2S(TRY_ANOTHER_BSS)
+	S2S(GAS_ADV_PROTO_NOT_SUPPORTED)
+	S2S(NO_OUTSTANDING_GAS_REQ)
+	S2S(GAS_RESP_NOT_RECEIVED)
+	S2S(STA_TIMED_OUT_WAITING_FOR_GAS_RESP)
+	S2S(GAS_RESP_LARGER_THAN_LIMIT)
+	S2S(REQ_REFUSED_HOME)
+	S2S(ADV_SRV_UNREACHABLE)
+	S2S(REQ_REFUSED_SSPN)
+	S2S(REQ_REFUSED_UNAUTH_ACCESS)
+	S2S(INVALID_RSNIE)
+	S2S(U_APSD_COEX_NOT_SUPPORTED)
+	S2S(U_APSD_COEX_MODE_NOT_SUPPORTED)
+	S2S(BAD_INTERVAL_WITH_U_APSD_COEX)
+	S2S(ANTI_CLOGGING_TOKEN_REQ)
+	S2S(FINITE_CYCLIC_GROUP_NOT_SUPPORTED)
+	S2S(CANNOT_FIND_ALT_TBTT)
+	S2S(TRANSMISSION_FAILURE)
+	S2S(REQ_TCLAS_NOT_SUPPORTED)
+	S2S(TCLAS_RESOURCES_EXCHAUSTED)
+	S2S(REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
+	S2S(REJECT_WITH_SCHEDULE)
+	S2S(REJECT_NO_WAKEUP_SPECIFIED)
+	S2S(SUCCESS_POWER_SAVE_MODE)
+	S2S(PENDING_ADMITTING_FST_SESSION)
+	S2S(PERFORMING_FST_NOW)
+	S2S(PENDING_GAP_IN_BA_WINDOW)
+	S2S(REJECT_U_PID_SETTING)
+	S2S(REFUSED_EXTERNAL_REASON)
+	S2S(REFUSED_AP_OUT_OF_MEMORY)
+	S2S(REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED)
+	S2S(QUERY_RESP_OUTSTANDING)
+	S2S(REJECT_DSE_BAND)
+	S2S(TCLAS_PROCESSING_TERMINATED)
+	S2S(TS_SCHEDULE_CONFLICT)
+	S2S(DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL)
+	S2S(MCCAOP_RESERVATION_CONFLICT)
+	S2S(MAF_LIMIT_EXCEEDED)
+	S2S(MCCA_TRACK_LIMIT_EXCEEDED)
+	S2S(DENIED_DUE_TO_SPECTRUM_MANAGEMENT)
+	S2S(ASSOC_DENIED_NO_VHT)
+	S2S(ENABLEMENT_DENIED)
+	S2S(RESTRICTION_FROM_AUTHORIZED_GDB)
+	S2S(AUTHORIZATION_DEENABLED)
+	S2S(FILS_AUTHENTICATION_FAILURE)
+	S2S(UNKNOWN_AUTHENTICATION_SERVER)
+	S2S(UNKNOWN_PASSWORD_IDENTIFIER)
+	S2S(SAE_HASH_TO_ELEMENT)
+	}
+	return "UNKNOWN";
+#undef S2S
+}
+
+
 int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
 		       size_t ies_len)
 {
@@ -1409,7 +1714,17 @@
 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
+
+	/*
+	 * IEEE Std 802.11ad-2012 and P802.ay/D5.0 60 GHz operating classes.
+	 * Class 180 has the legacy channels 1-6. Classes 181-183 include
+	 * channels which implement channel bonding features.
+	 */
+	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 6, 1, BW2160, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211AD, 181, 9, 13, 1, BW4320, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
 	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
 };
 
@@ -1723,6 +2038,63 @@
 }
 
 
+int center_idx_to_bw_6ghz(u8 idx)
+{
+	/* channels: 1, 5, 9, 13... */
+	if ((idx & 0x3) == 0x1)
+		return 0; /* 20 MHz */
+	/* channels 3, 11, 19... */
+	if ((idx & 0x7) == 0x3)
+		return 1; /* 40 MHz */
+	/* channels 7, 23, 39.. */
+	if ((idx & 0xf) == 0x7)
+		return 2; /* 80 MHz */
+	/* channels 15, 47, 79...*/
+	if ((idx & 0x1f) == 0xf)
+		return 3; /* 160 MHz */
+
+	return -1;
+}
+
+
+int is_6ghz_freq(int freq)
+{
+	if (freq < 5940 || freq > 7105)
+		return 0;
+
+	if (center_idx_to_bw_6ghz((freq - 5940) / 5) < 0)
+		return 0;
+
+	return 1;
+}
+
+
+int is_6ghz_op_class(u8 op_class)
+{
+	return op_class >= 131 && op_class <= 135;
+}
+
+
+int is_6ghz_psc_frequency(int freq)
+{
+	int i;
+
+	if (!is_6ghz_freq(freq))
+		return 0;
+	if ((((freq - 5940) / 5) & 0x3) != 0x1)
+		return 0;
+
+	i = (freq - 5940 + 55) % 80;
+	if (i == 0)
+		i = (freq - 5940 + 55) / 80;
+
+	if (i >= 1 && i <= 15)
+		return 1;
+
+	return 0;
+}
+
+
 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
 				    size_t nei_rep_len)
 {
@@ -1831,3 +2203,463 @@
 		return 0;
 	return !!(ie[2 + capab / 8] & BIT(capab % 8));
 }
+
+
+void hostapd_encode_edmg_chan(int edmg_enable, u8 edmg_channel,
+			      int primary_channel,
+			      struct ieee80211_edmg_config *edmg)
+{
+	if (!edmg_enable) {
+		edmg->channels = 0;
+		edmg->bw_config = 0;
+		return;
+	}
+
+	/* Only EDMG CB1 and EDMG CB2 contiguous channels supported for now */
+	switch (edmg_channel) {
+	case EDMG_CHANNEL_9:
+		edmg->channels = EDMG_CHANNEL_9_SUBCHANNELS;
+		edmg->bw_config = EDMG_BW_CONFIG_5;
+		return;
+	case EDMG_CHANNEL_10:
+		edmg->channels = EDMG_CHANNEL_10_SUBCHANNELS;
+		edmg->bw_config = EDMG_BW_CONFIG_5;
+		return;
+	case EDMG_CHANNEL_11:
+		edmg->channels = EDMG_CHANNEL_11_SUBCHANNELS;
+		edmg->bw_config = EDMG_BW_CONFIG_5;
+		return;
+	case EDMG_CHANNEL_12:
+		edmg->channels = EDMG_CHANNEL_12_SUBCHANNELS;
+		edmg->bw_config = EDMG_BW_CONFIG_5;
+		return;
+	case EDMG_CHANNEL_13:
+		edmg->channels = EDMG_CHANNEL_13_SUBCHANNELS;
+		edmg->bw_config = EDMG_BW_CONFIG_5;
+		return;
+	default:
+		if (primary_channel > 0 && primary_channel < 7) {
+			edmg->channels = BIT(primary_channel - 1);
+			edmg->bw_config = EDMG_BW_CONFIG_4;
+		} else {
+			edmg->channels = 0;
+			edmg->bw_config = 0;
+		}
+		break;
+	}
+}
+
+
+/* Check if the requested EDMG configuration is a subset of the allowed
+ * EDMG configuration. */
+int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
+			    struct ieee80211_edmg_config requested)
+{
+	/*
+	 * The validation check if the requested EDMG configuration
+	 * is a subset of the allowed EDMG configuration:
+	 * 1. Check that the requested channels are part (set) of the allowed
+	 * channels.
+	 * 2. P802.11ay defines the values of bw_config between 4 and 15.
+	 * (bw config % 4) will give us 4 groups inside bw_config definition,
+	 * inside each group we can check the subset just by comparing the
+	 * bw_config value.
+	 * Between this 4 groups, there is no subset relation - as a result of
+	 * the P802.11ay definition.
+	 * bw_config defined by IEEE P802.11ay/D4.0, 9.4.2.251, Table 13.
+	 */
+	if (((requested.channels & allowed.channels) != requested.channels) ||
+	    ((requested.bw_config % 4) > (allowed.bw_config % 4)) ||
+	    requested.bw_config > allowed.bw_config)
+		return 0;
+
+	return 1;
+}
+
+
+int op_class_to_bandwidth(u8 op_class)
+{
+	switch (op_class) {
+	case 81:
+	case 82:
+		return 20;
+	case 83: /* channels 1..9; 40 MHz */
+	case 84: /* channels 5..13; 40 MHz */
+		return 40;
+	case 115: /* channels 36,40,44,48; indoor only */
+		return 20;
+	case 116: /* channels 36,44; 40 MHz; indoor only */
+	case 117: /* channels 40,48; 40 MHz; indoor only */
+		return 40;
+	case 118: /* channels 52,56,60,64; dfs */
+		return 20;
+	case 119: /* channels 52,60; 40 MHz; dfs */
+	case 120: /* channels 56,64; 40 MHz; dfs */
+		return 40;
+	case 121: /* channels 100-140 */
+		return 20;
+	case 122: /* channels 100-142; 40 MHz */
+	case 123: /* channels 104-136; 40 MHz */
+		return 40;
+	case 124: /* channels 149,153,157,161 */
+	case 125: /* channels 149,153,157,161,165,169 */
+		return 20;
+	case 126: /* channels 149,157; 40 MHz */
+	case 127: /* channels 153,161; 40 MHz */
+		return 40;
+	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+		return 80;
+	case 129: /* center freqs 50, 114; 160 MHz */
+		return 160;
+	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80+80 MHz */
+		return 80;
+	case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
+		return 20;
+	case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
+		return 40;
+	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
+		return 80;
+	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
+	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
+		return 160;
+	case 180: /* 60 GHz band, channels 1..8 */
+		return 2160;
+	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		return 4320;
+	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		return 6480;
+	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
+		return 8640;
+	}
+
+	return 20;
+}
+
+
+int op_class_to_ch_width(u8 op_class)
+{
+	switch (op_class) {
+	case 81:
+	case 82:
+		return CHANWIDTH_USE_HT;
+	case 83: /* channels 1..9; 40 MHz */
+	case 84: /* channels 5..13; 40 MHz */
+		return CHANWIDTH_USE_HT;
+	case 115: /* channels 36,40,44,48; indoor only */
+		return CHANWIDTH_USE_HT;
+	case 116: /* channels 36,44; 40 MHz; indoor only */
+	case 117: /* channels 40,48; 40 MHz; indoor only */
+		return CHANWIDTH_USE_HT;
+	case 118: /* channels 52,56,60,64; dfs */
+		return CHANWIDTH_USE_HT;
+	case 119: /* channels 52,60; 40 MHz; dfs */
+	case 120: /* channels 56,64; 40 MHz; dfs */
+		return CHANWIDTH_USE_HT;
+	case 121: /* channels 100-140 */
+		return CHANWIDTH_USE_HT;
+	case 122: /* channels 100-142; 40 MHz */
+	case 123: /* channels 104-136; 40 MHz */
+		return CHANWIDTH_USE_HT;
+	case 124: /* channels 149,153,157,161 */
+	case 125: /* channels 149,153,157,161,165,169 */
+		return CHANWIDTH_USE_HT;
+	case 126: /* channels 149,157; 40 MHz */
+	case 127: /* channels 153,161; 40 MHz */
+		return CHANWIDTH_USE_HT;
+	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+		return CHANWIDTH_80MHZ;
+	case 129: /* center freqs 50, 114; 160 MHz */
+		return CHANWIDTH_160MHZ;
+	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80+80 MHz */
+		return CHANWIDTH_80P80MHZ;
+	case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
+		return CHANWIDTH_USE_HT;
+	case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
+		return CHANWIDTH_USE_HT;
+	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
+		return CHANWIDTH_80MHZ;
+	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
+		return CHANWIDTH_160MHZ;
+	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
+		return CHANWIDTH_80P80MHZ;
+	case 180: /* 60 GHz band, channels 1..8 */
+		return CHANWIDTH_2160MHZ;
+	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		return CHANWIDTH_4320MHZ;
+	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		return CHANWIDTH_6480MHZ;
+	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
+		return CHANWIDTH_8640MHZ;
+	}
+	return CHANWIDTH_USE_HT;
+}
+
+
+/* Parse HT capabilities to get maximum number of supported spatial streams */
+static int parse_ht_mcs_set_for_max_nss(
+				struct ieee80211_ht_capabilities *htcaps,
+				u8 parse_for_rx)
+{
+	int max_nss_rx = 1;
+	if (htcaps == NULL)
+		return max_nss_rx;
+	int i;
+	for (i = 4; i >= 1; i--) {
+		if (htcaps->supported_mcs_set[i - 1] > 0) {
+			max_nss_rx = i;
+			break;
+		}
+	}
+	if (parse_for_rx)
+		return max_nss_rx;
+	u8 supported_tx_mcs_set = htcaps->supported_mcs_set[12];
+	u8 tx_mcs_set_defined = supported_tx_mcs_set & 0x1;
+	u8 tx_rx_mcs_set_not_equal = (supported_tx_mcs_set >> 1) & 0x1;
+	if (tx_mcs_set_defined && tx_rx_mcs_set_not_equal) {
+		int max_nss_tx_field_value = (supported_tx_mcs_set >> 2) & 0x3;
+		// The maximum number of Tx streams is 1 more than the field value.
+		return max_nss_tx_field_value + 1;
+	}
+	return max_nss_rx;
+}
+
+
+/* Parse MCS map to get maximum number of supported spatial streams */
+static int parse_mcs_map_for_max_nss (u16 mcs_map, int max_streams_allowed)
+{
+	int max_nss = 1;
+	int i;
+	for (i = max_streams_allowed; i >= 1; i--) {
+		int stream_map = (mcs_map >> ((i - 1) * 2)) & 0x3;
+		// 3 means unsupported
+		if (stream_map != 3) {
+			max_nss = i;
+			break;
+		}
+	}
+	return max_nss;
+}
+
+
+/* Parse capabilities IEs to get maximum number of supported spatial streams */
+int get_max_nss_capability(struct ieee802_11_elems *elems, int parse_for_rx)
+{
+	int max_nss = 1;
+	struct ieee80211_ht_capabilities *htcaps =
+		(struct ieee80211_ht_capabilities *) elems->ht_capabilities;
+	struct ieee80211_vht_capabilities *vhtcaps =
+		(struct ieee80211_vht_capabilities *) elems->vht_capabilities;
+	struct ieee80211_he_capabilities *hecaps =
+		(struct ieee80211_he_capabilities *) elems->he_capabilities;
+	if (htcaps) {
+		int max_nss_ht = parse_ht_mcs_set_for_max_nss(htcaps, parse_for_rx);
+		if (max_nss_ht > max_nss)
+			max_nss = max_nss_ht;
+	}
+	le16 mcs_map;
+	if (vhtcaps) {
+		mcs_map = (parse_for_rx) ? vhtcaps->vht_supported_mcs_set.rx_map :
+			vhtcaps->vht_supported_mcs_set.tx_map;
+		int max_nss_vht = parse_mcs_map_for_max_nss(
+			le_to_host16(mcs_map), VHT_RX_NSS_MAX_STREAMS);
+		if (max_nss_vht > max_nss)
+			max_nss = max_nss_vht;
+	}
+	if (hecaps) {
+		mcs_map = (parse_for_rx) ? hecaps->he_basic_supported_mcs_set.rx_map :
+			hecaps->he_basic_supported_mcs_set.tx_map;
+		int max_nss_he = parse_mcs_map_for_max_nss(
+			le_to_host16(mcs_map), HE_NSS_MAX_STREAMS);
+		if (max_nss_he > max_nss)
+			max_nss = max_nss_he;
+	}
+	return max_nss;
+}
+
+
+/* Parse VHT/HE capabilities IEs to get supported channel width */
+struct supported_chan_width get_supported_channel_width(
+				struct ieee802_11_elems *elems)
+{
+	struct supported_chan_width supported_width;
+	supported_width.is_160_supported = 0;
+	supported_width.is_80p80_supported = 0;
+	if (elems == NULL)
+		return supported_width;
+
+	struct ieee80211_vht_capabilities *vhtcaps =
+		(struct ieee80211_vht_capabilities *) elems->vht_capabilities;
+	struct ieee80211_he_capabilities *hecaps =
+		(struct ieee80211_he_capabilities *) elems->he_capabilities;
+
+	if (vhtcaps) {
+		le32 vht_capabilities_info =
+			le_to_host32(vhtcaps->vht_capabilities_info);
+		if (vht_capabilities_info & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)
+			supported_width.is_160_supported = 1;
+		if (vht_capabilities_info & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+			supported_width.is_80p80_supported = 1;
+	}
+	if (hecaps) {
+		u8 channel_width_set =
+        	hecaps->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+		if (channel_width_set & HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
+			supported_width.is_160_supported = 1;
+		if (channel_width_set & HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
+			supported_width.is_80p80_supported = 1;
+	}
+	wpa_printf(MSG_DEBUG, " IE indicate 160 supported: %u, 80+80 supported: %u",
+        supported_width.is_160_supported, supported_width.is_80p80_supported);
+	return supported_width;
+}
+
+/*
+ * Parse VHT operation info fields to get operation channel width
+ * note that VHT operation info fields could come from VHT operation IE
+ * or from HE operation IE
+ */
+static enum chan_width get_vht_operation_channel_width(
+				struct ieee80211_vht_operation_info *vht_oper_info)
+{
+	enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+	u8 seg0, seg1;
+	switch (vht_oper_info->vht_op_info_chwidth) {
+	case 1:
+		seg0 = vht_oper_info->vht_op_info_chan_center_freq_seg0_idx;
+		seg1 = vht_oper_info->vht_op_info_chan_center_freq_seg1_idx;
+		if (seg1 && abs(seg1 - seg0) == 8)
+			channel_width = CHAN_WIDTH_160;
+		else if (seg1)
+			channel_width = CHAN_WIDTH_80P80;
+		else
+			channel_width = CHAN_WIDTH_80;
+		break;
+	case 2:
+		channel_width = CHAN_WIDTH_160;
+		break;
+	case 3:
+		channel_width = CHAN_WIDTH_80P80;
+		break;
+	default:
+		break;
+	}
+	wpa_printf(MSG_DEBUG, " VHT operation CBW: %u", channel_width);
+	return channel_width;
+}
+
+/* Parse 6GHz operation info fields to get operation channel width */
+static enum chan_width get_6ghz_operation_channel_width(
+				struct ieee80211_6ghz_operation_info * six_ghz_oper_info)
+{
+	enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+	u8 seg0, seg1;
+	switch (six_ghz_oper_info->control & SIX_GHZ_CONTROL_CHANNEL_WIDTH_MASK) {
+	case 0:
+		channel_width = CHAN_WIDTH_20;
+		break;
+	case 1:
+		channel_width = CHAN_WIDTH_40;
+		break;
+	case 2:
+		channel_width = CHAN_WIDTH_80;
+		break;
+	case 3:
+		seg0 = six_ghz_oper_info->chan_center_freq_seg0_idx;
+		seg1 = six_ghz_oper_info->chan_center_freq_seg1_idx;
+		if (abs(seg1 - seg0) == 8)
+			channel_width = CHAN_WIDTH_160;
+		else
+			channel_width = CHAN_WIDTH_80P80;
+		break;
+	default:
+		break;
+	}
+	wpa_printf(MSG_DEBUG, " 6GHz operation CBW: %u", channel_width);
+	return channel_width;
+}
+
+
+/* Parse HE operation IE to get HE operation channel width */
+static enum chan_width get_he_operation_channel_width(
+				struct ieee80211_he_operation *he_oper,
+				int he_oper_len)
+{
+	enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+	u8 is_6ghz_info_present =
+		(he_oper->he_oper_params & HE_OPERATION_6GHZ_OPER_INFO) ? 1 : 0;
+	u8 is_vht_info_present =
+		(he_oper->he_oper_params & HE_OPERATION_VHT_OPER_INFO) ? 1 : 0;
+	u8 is_cohosted_bss_present =
+		(he_oper->he_oper_params & HE_OPERATION_COHOSTED_BSS) ? 1 : 0;
+	int expected_len = HE_OPERATION_IE_MIN_LEN
+		+ (is_6ghz_info_present ? HE_OPERATION_6GHZ_OPER_INFO_LEN : 0)
+		+ (is_vht_info_present ? HE_OPERATION_VHT_OPER_INFO_LEN : 0)
+		+ (is_cohosted_bss_present
+		? HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN : 0);
+	if (he_oper_len < expected_len)
+		return channel_width;
+
+	if (is_6ghz_info_present) {
+		struct ieee80211_6ghz_operation_info *six_ghz_oper_info =
+			(struct ieee80211_6ghz_operation_info *)
+			(he_oper + HE_OPERATION_IE_MIN_LEN
+			+ (is_vht_info_present ? HE_OPERATION_VHT_OPER_INFO_LEN : 0)
+			+ (is_cohosted_bss_present
+			? HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN : 0));
+		channel_width = get_6ghz_operation_channel_width(six_ghz_oper_info);
+	}
+	if (channel_width == CHAN_WIDTH_UNKNOWN && is_vht_info_present) {
+		struct ieee80211_vht_operation_info *vht_oper_info  =
+			(struct ieee80211_vht_operation_info *)
+			(he_oper + HE_OPERATION_IE_MIN_LEN);
+		channel_width = get_vht_operation_channel_width(vht_oper_info);
+	}
+	wpa_printf(MSG_DEBUG, " HE operation CBW: %u", channel_width);
+	return channel_width;
+}
+
+/* Parse HT/VHT/HE operation IEs to get operation channel width */
+enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems)
+{
+	enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+	if (elems == NULL)
+		return channel_width;
+
+	struct ieee80211_ht_operation *ht_oper =
+	    (struct ieee80211_ht_operation *) elems->ht_operation;
+	struct ieee80211_vht_operation_info *vht_oper_info =
+	    (struct ieee80211_vht_operation_info *) elems->vht_operation;
+	struct ieee80211_he_operation *he_oper =
+	    (struct ieee80211_he_operation *) elems->he_operation;
+	if (he_oper)
+		channel_width = get_he_operation_channel_width(
+			he_oper, elems->he_operation_len);
+
+	if (channel_width == CHAN_WIDTH_UNKNOWN && vht_oper_info)
+		channel_width = get_vht_operation_channel_width(vht_oper_info);
+
+	if (channel_width == CHAN_WIDTH_UNKNOWN && ht_oper) {
+		u8 sec_chan_offset =
+			ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+		channel_width = (sec_chan_offset == 0) ? CHAN_WIDTH_20 : CHAN_WIDTH_40;
+	}
+	wpa_printf(MSG_DEBUG, " overall operation CBW: %u", channel_width);
+	return channel_width;
+}
+
+/*
+ * Get STA operation channel width from AP's operation channel width and
+ *  STA's supported channel width
+ */
+enum chan_width get_sta_operation_chan_width(
+				enum chan_width ap_operation_chan_width,
+				struct supported_chan_width sta_supported_chan_width)
+{
+	if (ap_operation_chan_width == CHAN_WIDTH_160)
+		return (sta_supported_chan_width.is_160_supported)
+			? CHAN_WIDTH_160 : CHAN_WIDTH_80;
+	if (ap_operation_chan_width == CHAN_WIDTH_80P80)
+		return (sta_supported_chan_width.is_80p80_supported)
+			? CHAN_WIDTH_80P80 : CHAN_WIDTH_80;
+	return ap_operation_chan_width;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index d41bd39..2c66507 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -40,6 +40,7 @@
 	const u8 *ext_supp_rates;
 	const u8 *wpa_ie;
 	const u8 *rsn_ie;
+	const u8 *rsnxe;
 	const u8 *wmm; /* WMM Information or Parameter Element */
 	const u8 *wmm_tspec;
 	const u8 *wps_ie;
@@ -94,6 +95,7 @@
 	const u8 *oci;
 	const u8 *multi_ap;
 	const u8 *he_capabilities;
+	const u8 *he_operation;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -101,6 +103,7 @@
 	u8 ext_supp_rates_len;
 	u8 wpa_ie_len;
 	u8 rsn_ie_len;
+	u8 rsnxe_len;
 	u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
 	u8 wmm_tspec_len;
 	u8 wps_ie_len;
@@ -143,6 +146,7 @@
 	u8 oci_len;
 	u8 multi_ap_len;
 	u8 he_capabilities_len;
+	u8 he_operation_len;
 
 	struct mb_ies_info mb_ies;
 };
@@ -185,6 +189,8 @@
 struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
 
 const char * fc2str(u16 fc);
+const char * reason2str(u16 reason);
+const char * status2str(u16 status);
 
 struct oper_class_map {
 	enum hostapd_hw_mode mode;
@@ -192,7 +198,8 @@
 	u8 min_chan;
 	u8 max_chan;
 	u8 inc;
-	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
+	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80, BW4320,
+	       BW6480, BW8640} bw;
 	enum { P2P_SUPP, NO_P2P_SUPP } p2p;
 };
 
@@ -216,11 +223,17 @@
 
 const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
 int oper_class_bw_to_int(const struct oper_class_map *map);
+int center_idx_to_bw_6ghz(u8 idx);
+int is_6ghz_freq(int freq);
+int is_6ghz_op_class(u8 op_class);
+int is_6ghz_psc_frequency(int freq);
 
 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
 				    size_t nei_rep_len);
 
 int ieee802_11_ext_capab(const u8 *ie, unsigned int capab);
+int op_class_to_bandwidth(u8 op_class);
+int op_class_to_ch_width(u8 op_class);
 
 /* element iteration helpers */
 #define for_each_element(_elem, _data, _datalen)			\
@@ -269,4 +282,27 @@
 	return (const u8 *) element == (const u8 *) data + datalen;
 }
 
+struct ieee80211_edmg_config;
+
+void hostapd_encode_edmg_chan(int edmg_enable, u8 edmg_channel,
+			      int primary_channel,
+			      struct ieee80211_edmg_config *edmg);
+
+int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
+			    struct ieee80211_edmg_config requested);
+
+int get_max_nss_capability(struct ieee802_11_elems *elems, int parse_for_rx);
+
+struct supported_chan_width {
+	u8 is_160_supported;
+	u8 is_80p80_supported;
+};
+
+struct supported_chan_width get_supported_channel_width(struct ieee802_11_elems *elems);
+
+enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems);
+
+enum chan_width get_sta_operation_chan_width(enum chan_width ap_operation_chan_width,
+					     struct supported_chan_width sta_supported_width);
+
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index adaa893..fdc51dc 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -204,6 +204,7 @@
 #define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
 #define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
 #define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
+#define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
 
 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
 #define WLAN_REASON_UNSPECIFIED 1
@@ -446,6 +447,7 @@
 #define WLAN_EID_FILS_INDICATION 240
 #define WLAN_EID_DILS 241
 #define WLAN_EID_FRAGMENT 242
+#define WLAN_EID_RSNX 244
 #define WLAN_EID_EXTENSION 255
 
 /* Element ID Extension (EID 255) values */
@@ -468,7 +470,11 @@
 #define WLAN_EID_EXT_HE_CAPABILITIES 35
 #define WLAN_EID_EXT_HE_OPERATION 36
 #define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
+#define WLAN_EID_EXT_SPATIAL_REUSE 39
 #define WLAN_EID_EXT_OCV_OCI 54
+#define WLAN_EID_EXT_EDMG_CAPABILITIES 61
+#define WLAN_EID_EXT_EDMG_OPERATION 62
+#define WLAN_EID_EXT_REJECTED_GROUPS 92
 
 /* Extended Capabilities field */
 #define WLAN_EXT_CAPAB_20_40_COEX 0
@@ -551,6 +557,11 @@
 #define WLAN_EXT_CAPAB_SAE_PW_ID 81
 #define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82
 
+/* Extended RSN Capabilities */
+/* bits 0-3: Field length (n-1) */
+#define WLAN_RSNX_CAPAB_PROTECTED_TWT 4
+#define WLAN_RSNX_CAPAB_SAE_H2E 5
+
 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
 #define WLAN_ACTION_SPECTRUM_MGMT 0
 #define WLAN_ACTION_QOS 1
@@ -1091,6 +1102,12 @@
 	le16 vht_basic_mcs_set;
 } STRUCT_PACKED;
 
+struct ieee80211_vht_operation_info {
+	u8 vht_op_info_chwidth;
+	u8 vht_op_info_chan_center_freq_seg0_idx;
+	u8 vht_op_info_chan_center_freq_seg1_idx;
+} STRUCT_PACKED;
+
 struct ieee80211_ampe_ie {
 	u8 selected_pairwise_suite[4];
 	u8 local_nonce[32];
@@ -1216,6 +1233,7 @@
 
 #define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
+#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
 
 /* VHT Defines */
 #define VHT_CAP_MAX_MPDU_LENGTH_7991                ((u32) BIT(0))
@@ -1273,11 +1291,17 @@
 
 #define VHT_RX_NSS_MAX_STREAMS			    8
 
-/* VHT channel widths */
-#define VHT_CHANWIDTH_USE_HT	0
-#define VHT_CHANWIDTH_80MHZ	1
-#define VHT_CHANWIDTH_160MHZ	2
-#define VHT_CHANWIDTH_80P80MHZ	3
+/* VHT/EDMG channel widths */
+#define CHANWIDTH_USE_HT	0
+#define CHANWIDTH_80MHZ		1
+#define CHANWIDTH_160MHZ	2
+#define CHANWIDTH_80P80MHZ	3
+#define CHANWIDTH_2160MHZ	4
+#define CHANWIDTH_4320MHZ	5
+#define CHANWIDTH_6480MHZ	6
+#define CHANWIDTH_8640MHZ	7
+
+#define HE_NSS_MAX_STREAMS			    8
 
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
 				* 00:50:F2 */
@@ -2091,7 +2115,7 @@
 /*
  * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information
  * subfields.
- * Note: These definitions are not the same as other VHT_CHANWIDTH_*.
+ * Note: These definitions are not the same as other CHANWIDTH_*.
  */
 enum nr_chan_width {
 	NR_CHAN_WIDTH_20 = 0,
@@ -2104,21 +2128,59 @@
 struct ieee80211_he_capabilities {
 	u8 he_mac_capab_info[6];
 	u8 he_phy_capab_info[11];
-	u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */
-	/* PPE Thresholds (optional) */
+	struct {
+		le16 rx_map;
+		le16 tx_map;
+	} he_basic_supported_mcs_set;
+	/* Followed by 0, 4, or 8 octets of optional supported HE-MCS And NSS Set field
+	* and optional variable length PPE Thresholds field. */
+	u8 optional[33];
 } STRUCT_PACKED;
 
 struct ieee80211_he_operation {
-	u32 he_oper_params; /* HE Operation Parameters[3] and
-			     * BSS Color Information[1] */
-	u8 he_mcs_nss_set[2];
+	le32 he_oper_params; /* HE Operation Parameters[3] and
+			      * BSS Color Information[1] */
+	le16 he_mcs_nss_set;
 	u8 vht_op_info_chwidth;
 	u8 vht_op_info_chan_center_freq_seg0_idx;
 	u8 vht_op_info_chan_center_freq_seg1_idx;
 	/* Followed by conditional MaxBSSID Indicator subfield (u8) */
 } STRUCT_PACKED;
 
+/*
+ * IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
+ */
+struct ieee80211_spatial_reuse {
+	u8 sr_ctrl; /* SR Control */
+	/* Up to 19 octets of parameters:
+	 * Non-SRG OBSS PD Max Offset[0 or 1]
+	 * SRG OBSS PD Min Offset[0 or 1]
+	 * SRG OBSS PD Max Offset[0 or 1]
+	 * SRG BSS Color Bitmap[0 or 8]
+	 * SRG Partial BSSID Bitmap[0 or 8]
+	 */
+	u8 params[19];
+} STRUCT_PACKED;
+
+struct ieee80211_6ghz_operation_info {
+	u8 primary_chan;
+	u8 control;
+	u8 chan_center_freq_seg0_idx;
+	u8 chan_center_freq_seg1_idx;
+	u8 minimum_rate;
+} STRUCT_PACKED;
+
+#define HE_CAPABILITIES_IE_MIN_LEN 21
+
 /* HE Capabilities Information defines */
+#define HE_PHYCAP_CHANNEL_WIDTH_SET_IDX		0
+#define HE_PHYCAP_CHANNEL_WIDTH_MASK		((u8) (BIT(1) | BIT(2) | \
+						      BIT(3) | BIT(4)))
+#define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G         ((u8) BIT(1))
+#define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G	((u8) BIT(2))
+#define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G	((u8) BIT(3))
+#define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G	((u8) BIT(4))
+
 #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX	3
 #define HE_PHYCAP_SU_BEAMFORMER_CAPAB		((u8) BIT(7))
 #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX	4
@@ -2126,23 +2188,53 @@
 #define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX	4
 #define HE_PHYCAP_MU_BEAMFORMER_CAPAB		((u8) BIT(1))
 
+#define HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX	6
+#define HE_PHYCAP_PPE_THRESHOLD_PRESENT		((u8) BIT(7))
+
+/* HE PPE Threshold define */
+#define HE_PPE_THRES_RU_INDEX_BITMASK_MASK	0xf
+#define HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT	3
+#define HE_PPE_THRES_NSS_MASK			0x7
+
 /* HE Operation defines */
 /* HE Operation Parameters and BSS Color Information fields */
-#define HE_OPERATION_BSS_COLOR_MASK		((u32) (BIT(0) | BIT(1) | \
-							BIT(2) | BIT(3) | \
-							BIT(4) | BIT(5)))
-#define HE_OPERATION_PARTIAL_BSS_COLOR		((u32) BIT(6))
-#define HE_OPERATION_BSS_COLOR_DISABLED		((u32) BIT(7))
-#define HE_OPERATION_DFLT_PE_DURATION_MASK	((u32) (BIT(8) | BIT(9) | \
-							BIT(10)))
-#define HE_OPERATION_DFLT_PE_DURATION_OFFSET	8
-#define HE_OPERATION_TWT_REQUIRED		((u32) BIT(11))
-#define HE_OPERATION_RTS_THRESHOLD_MASK	((u32) (BIT(12) | BIT(13) | \
-						BIT(14) | BIT(15) | \
-						BIT(16) | BIT(17) | \
-						BIT(18) | BIT(19) | \
-						BIT(20) | BIT(21)))
-#define HE_OPERATION_RTS_THRESHOLD_OFFSET	12
+#define HE_OPERATION_DFLT_PE_DURATION_MASK	((u32) (BIT(0) | BIT(1) | \
+							BIT(2)))
+#define HE_OPERATION_DFLT_PE_DURATION_OFFSET	0
+#define HE_OPERATION_TWT_REQUIRED		((u32) BIT(3))
+#define HE_OPERATION_RTS_THRESHOLD_MASK	((u32) (BIT(4) | BIT(5) | \
+						BIT(6) | BIT(7) | \
+						BIT(8) | BIT(9) | \
+						BIT(10) | BIT(11) | \
+						BIT(12) | BIT(13)))
+#define HE_OPERATION_RTS_THRESHOLD_OFFSET	4
+#define HE_OPERATION_VHT_OPER_INFO		((u32) BIT(14))
+#define HE_OPERATION_COHOSTED_BSS		((u32) BIT(15))
+#define HE_OPERATION_ER_SU_DISABLE		((u32) BIT(16))
+#define HE_OPERATION_6GHZ_OPER_INFO		((u32) BIT(17))
+#define HE_OPERATION_BSS_COLOR_MASK		((u32) (BIT(24) | BIT(25) | \
+							BIT(26) | BIT(27) | \
+							BIT(28) | BIT(29)))
+#define HE_OPERATION_PARTIAL_BSS_COLOR		((u32) BIT(30))
+#define HE_OPERATION_BSS_COLOR_DISABLED		((u32) BIT(31))
+#define HE_OPERATION_BSS_COLOR_OFFSET		24
+
+/* HE operation fields length*/
+#define HE_OPERATION_IE_MIN_LEN 6
+#define HE_OPERATION_VHT_OPER_INFO_LEN 3
+#define HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN 1
+#define HE_OPERATION_6GHZ_OPER_INFO_LEN 5
+
+/* Spatial Reuse defines */
+#define SPATIAL_REUSE_SRP_DISALLOWED		BIT(0)
+#define SPATIAL_REUSE_NON_SRG_OBSS_PD_SR_DISALLOWED	BIT(1)
+#define SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT	BIT(2)
+#define SPATIAL_REUSE_SRG_INFORMATION_PRESENT	BIT(3)
+#define SPATIAL_REUSE_HESIGA_SR_VAL15_ALLOWED	BIT(4)
+
+/* 6GHz operation control field defines*/
+#define SIX_GHZ_CONTROL_CHANNEL_WIDTH_MASK 	((u8) BIT(0) | BIT(1))
+#define SIX_GHZ_CONTROL_DUPLICATE_BEACON 	BIT(2)
 
 struct ieee80211_he_mu_edca_parameter_set {
 	u8 he_qos_info;
@@ -2177,6 +2269,39 @@
 /* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */
 #define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7)))
 
+/* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
+#define EDMG_BSS_OPERATING_CHANNELS_OFFSET	6
+#define EDMG_OPERATING_CHANNEL_WIDTH_OFFSET	7
+
+/* IEEE P802.11ay/D4.0, 29.3.4 - Channelization */
+enum edmg_channel {
+	EDMG_CHANNEL_9	= 9,
+	EDMG_CHANNEL_10	= 10,
+	EDMG_CHANNEL_11	= 11,
+	EDMG_CHANNEL_12	= 12,
+	EDMG_CHANNEL_13	= 13,
+};
+
+/* Represent CB2 contiguous channels */
+#define EDMG_CHANNEL_9_SUBCHANNELS	(BIT(0) | BIT(1)) /* channels 1 and 2 */
+#define EDMG_CHANNEL_10_SUBCHANNELS	(BIT(1) | BIT(2)) /* channels 2 and 3 */
+#define EDMG_CHANNEL_11_SUBCHANNELS	(BIT(2) | BIT(3)) /* channels 3 and 4 */
+#define EDMG_CHANNEL_12_SUBCHANNELS	(BIT(3) | BIT(4)) /* channels 4 and 5 */
+#define EDMG_CHANNEL_13_SUBCHANNELS	(BIT(4) | BIT(5)) /* channels 5 and 6 */
+
+/**
+ * enum edmg_bw_config - Allowed channel bandwidth configurations
+ * @EDMG_BW_CONFIG_4: 2.16 GHz
+ * @EDMG_BW_CONFIG_5: 2.16 GHz and 4.32 GHz
+ *
+ * IEEE P802.11ay/D4.0, 9.4.2.251 (EDMG Operation element),
+ * Table 13 (Channel BW Configuration subfield definition)
+ */
+enum edmg_bw_config {
+	EDMG_BW_CONFIG_4	= 4,
+	EDMG_BW_CONFIG_5	= 5,
+};
+
 /* DPP Public Action frame identifiers - OUI_WFA */
 #define DPP_OUI_TYPE 0x1A
 
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index c34a3bc..ade7aa6 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,7 +1,7 @@
 /*
  * Qualcomm Atheros OUI and vendor specific assignments
  * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
- * Copyright (c) 2018, The Linux Foundation
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,8 @@
 #ifndef QCA_VENDOR_H
 #define QCA_VENDOR_H
 
+#include "utils/common.h"
+
 /*
  * This file is a registry of identifier assignments from the Qualcomm Atheros
  * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers
@@ -72,7 +74,7 @@
  *
  * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
  *	invoke the ACS function in device and pass selected channels to
- *	hostapd.
+ *	hostapd. Uses enum qca_wlan_vendor_attr_acs_offload attributes.
  *
  * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
  *	supported by the driver. enum qca_wlan_vendor_features defines
@@ -98,6 +100,9 @@
  *	which supports DFS offloading, to indicate a radar pattern has been
  *	detected. The channel is now unusable.
  *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO: Get information from the driver.
+ *	Attributes defined in enum qca_wlan_vendor_attr_get_wifi_info.
+ *
  * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap
  *	based on enum wifi_logger_supported_features. Attributes defined in
  *	enum qca_wlan_vendor_attr_get_logger_features.
@@ -167,6 +172,11 @@
  *	to notify the connected station's status. The attributes for this
  *	command are defined in enum qca_wlan_vendor_attr_link_properties.
  *
+ * @QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY: This command is used to configure
+ *	DFS policy and channel hint for ACS operation. This command uses the
+ *	attributes defined in enum qca_wlan_vendor_attr_acs_config and
+ *	enum qca_acs_dfs_mode.
+ *
  * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to
  *	start the P2P Listen offload function in device and pass the listen
  *	channel, period, interval, count, device types, and vendor specific
@@ -373,7 +383,9 @@
  * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan
  *	parameters are specified by enum qca_wlan_vendor_attr_spectral_scan.
  *	This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE)
- *	identifying the operation in success case.
+ *	identifying the operation in success case. In failure cases an
+ *	error code (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE)
+ *	describing the reason for the failure is returned.
  *
  * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses
  *	a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from
@@ -524,6 +536,89 @@
  *	parameters including Zigbee state and specific WLAN periods to enhance
  *	PTA master. All these parameters are delivered by the attributes
  *	defined in enum qca_mpta_helper_vendor_attr.
+ * @QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING: This sub command is used to
+ *	implement Beacon frame reporting feature.
+ *
+ *	Userspace can request the driver/firmware to periodically report
+ *	received Beacon frames whose BSSID is same as the current connected
+ *	BSS's MAC address.
+ *
+ *	In case the STA seamlessly (without sending disconnect indication to
+ *	userspace) roams to a different BSS, Beacon frame reporting will be
+ *	automatically enabled for the Beacon frames whose BSSID is same as the
+ *	MAC address of the new BSS. Beacon reporting will be stopped when the
+ *	STA is disconnected (when the disconnect indication is sent to
+ *	userspace) and need to be explicitly enabled by userspace for next
+ *	connection.
+ *
+ *	When a Beacon frame matching configured conditions is received, and if
+ *	userspace has requested to send asynchronous beacon reports, the
+ *	driver/firmware will encapsulate the details of the Beacon frame in an
+ *	event and send it to userspace along with updating the BSS information
+ *	in cfg80211 scan cache, otherwise driver will only update the cfg80211
+ *	scan cache with the information from the received Beacon frame but will
+ *	not send any active report to userspace.
+ *
+ *	The userspace can request the driver/firmware to stop reporting Beacon
+ *	frames. If the driver/firmware is not able to receive Beacon frames due
+ *	to other Wi-Fi operations such as off-channel activities, etc., the
+ *	driver/firmware will send a pause event to userspace and stop reporting
+ *	Beacon frames. Whether the beacon reporting will be automatically
+ *	resumed or not by the driver/firmware later will be reported to
+ *	userspace using the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES
+ *	flag. The beacon reporting shall be resumed for all the cases except
+ *	either when userspace sets
+ *	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME flag in the command
+ *	which triggered the current beacon reporting or during any disconnection
+ *	case as indicated by setting
+ *	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON to
+ *	QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED by the
+ *	driver.
+ *
+ *	After QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_PAUSE event is received
+ *	by userspace with QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES
+ *	flag not set, the next first
+ *	QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO event from the driver
+ *	shall be considered as un-pause event.
+ *
+ *	All the attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_beacon_reporting_params.
+ * @QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP: In practice, some APs have
+ *	interop issues with the DUT. This sub command is used to transfer the
+ *	AP info between the driver and user space. This works both as a command
+ *	and an event. As a command, it configures the stored list of APs from
+ *	user space to firmware; as an event, it indicates the AP info detected
+ *	by the firmware to user space for persistent storage. The attributes
+ *	defined in enum qca_vendor_attr_interop_issues_ap are used to deliver
+ *	the parameters.
+ * @QCA_NL80211_VENDOR_SUBCMD_OEM_DATA: This command/event is used to
+ *	send/receive OEM data binary blobs to/from application/service to/from
+ *	firmware. The attributes defined in enum
+ *	qca_wlan_vendor_attr_oem_data_params are used to deliver the
+ *	parameters.
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT: This command/event is used
+ *	to send/receive avoid frequency data using
+ *	enum qca_wlan_vendor_attr_avoid_frequency_ext.
+ *	This new command is alternative to existing command
+ *	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY since existing command/event
+ *	is using stream of bytes instead of structured data using vendor
+ *	attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE: This vendor subcommand is used to
+ *	add the STA node details in driver/firmware. Attributes for this event
+ *	are specified in enum qca_wlan_vendor_attr_add_sta_node_params.
+ * @QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE: This command is used to set BT
+ *	coex chain mode from application/service.
+ *	The attributes defined in enum qca_vendor_attr_btc_chain_mode are used
+ *	to deliver the parameters.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO: This vendor subcommand is used to
+ *	get information of a station from driver to userspace. This command can
+ *	be used in both STA and AP modes. For STA mode, it provides information
+ *	of the current association when in connected state or the last
+ *	association when in disconnected state. For AP mode, only information
+ *	of the currently connected stations is available. This command uses
+ *	attributes defined in enum qca_wlan_vendor_attr_get_sta_info.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -623,7 +718,8 @@
 	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109,
 	/* 110..114 - reserved for QCA */
 	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115,
-	/* 116..117 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY = 116,
+	/* 117 - reserved for QCA */
 	QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118,
 	QCA_NL80211_VENDOR_SUBCMD_TSF = 119,
 	QCA_NL80211_VENDOR_SUBCMD_WISA = 120,
@@ -692,6 +788,13 @@
 	QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177,
 	QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178,
 	QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179,
+	QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING = 180,
+	QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP = 181,
+	QCA_NL80211_VENDOR_SUBCMD_OEM_DATA = 182,
+	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT = 183,
+	QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE = 184,
+	QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE = 185,
+	QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO = 186,
 };
 
 enum qca_wlan_vendor_attr {
@@ -1028,31 +1131,162 @@
 	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1
 };
 
+/**
+ * enum qca_wlan_vendor_attr_acs_offload - Defines attributes to be used with
+ * vendor command/event QCA_NL80211_VENDOR_SUBCMD_DO_ACS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL: Required (u8).
+ * Used with event to notify the primary channel number selected in ACS
+ * operation.
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL is deprecated; use
+ * QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY instead.
+ * To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL
+ * is still used if either of the driver or user space application doesn't
+ * support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL: Required (u8).
+ * Used with event to notify the secondary channel number selected in ACS
+ * operation.
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL is deprecated; use
+ * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY instead.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL is still used if either of
+ * the driver or user space application doesn't support 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE: Required (u8).
+ * (a) Used with command to configure hw_mode from
+ * enum qca_wlan_vendor_acs_hw_mode for ACS operation.
+ * (b) Also used with event to notify the hw_mode of selected primary channel
+ * in ACS operation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED: Flag attribute.
+ * Used with command to configure ACS operation for HT mode.
+ * Disable (flag attribute not present) - HT disabled and
+ * Enable (flag attribute present) - HT enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED: Flag attribute.
+ * Used with command to configure ACS operation for HT40 mode.
+ * Disable (flag attribute not present) - HT40 disabled and
+ * Enable (flag attribute present) - HT40 enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED: Flag attribute.
+ * Used with command to configure ACS operation for VHT mode.
+ * Disable (flag attribute not present) - VHT disabled and
+ * Enable (flag attribute present) - VHT enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH: Optional (u16) with command and
+ * mandatory with event.
+ * If specified in command path, ACS operation is configured with the given
+ * channel width (in MHz).
+ * In event path, specifies the channel width of the primary channel selected.
+ *
+ * @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).
+ * 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.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL: Required (u8).
+ * Used with event to notify the VHT segment 0 center channel number selected in
+ * ACS operation.
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL is deprecated; use
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY instead.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL is still used if either of
+ * the driver or user space application doesn't support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL: Required (u8).
+ * Used with event to notify the VHT segment 1 center channel number selected in
+ * ACS operation.
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL is deprecated; use
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY instead.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL is still used if either of
+ * the driver or user space application doesn't support the 6 GHz band.
+ *
+ * @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).
+ * 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
+ * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST (considers only 2 GHz and 5 GHz channels in
+ * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY: Required (u32).
+ * Used with event to notify the primary channel center frequency (MHz) selected
+ * in ACS operation.
+ * Note: If the driver supports the 6 GHz band, the event sent from the driver
+ * includes this attribute along with QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY: Required (u32).
+ * Used with event to notify the secondary channel center frequency (MHz)
+ * selected in ACS operation.
+ * Note: If the driver supports the 6 GHz band, the event sent from the driver
+ * includes this attribute along with
+ * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY: Required (u32).
+ * Used with event to notify the VHT segment 0 center channel frequency (MHz)
+ * selected in ACS operation.
+ * Note: If the driver supports the 6 GHz band, the event sent from the driver
+ * includes this attribute along with
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY: Required (u32).
+ * Used with event to notify the VHT segment 1 center channel frequency (MHz)
+ * selected in ACS operation.
+ * Note: If the driver supports the 6 GHz band, the event sent from the driver
+ * includes this attribute along with
+ * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL.
+ */
 enum qca_wlan_vendor_attr_acs_offload {
 	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
-	QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
-	QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
-	QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
-	QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED,
-	QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED,
-	QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED,
-	QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
-	QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
-	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
-	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
-	QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+	QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL = 1,
+	QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL = 2,
+	QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE = 3,
+	QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED = 4,
+	QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED = 5,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED = 6,
+	QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH = 7,
+	QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST = 8,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL = 9,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL = 10,
+	QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST = 11,
+	QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY = 12,
+	QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY = 13,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY = 14,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY = 15,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ACS_MAX =
 	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1
 };
 
+/**
+ * enum qca_wlan_vendor_acs_hw_mode - Defines HW mode to be used with the
+ * vendor command/event QCA_NL80211_VENDOR_SUBCMD_DO_ACS.
+ *
+ * @QCA_ACS_MODE_IEEE80211B: 802.11b mode
+ * @QCA_ACS_MODE_IEEE80211G: 802.11g mode
+ * @QCA_ACS_MODE_IEEE80211A: 802.11a mode
+ * @QCA_ACS_MODE_IEEE80211AD: 802.11ad mode
+ * @QCA_ACS_MODE_IEEE80211ANY: all modes
+ * @QCA_ACS_MODE_IEEE80211AX: 802.11ax mode
+ */
 enum qca_wlan_vendor_acs_hw_mode {
 	QCA_ACS_MODE_IEEE80211B,
 	QCA_ACS_MODE_IEEE80211G,
 	QCA_ACS_MODE_IEEE80211A,
 	QCA_ACS_MODE_IEEE80211AD,
 	QCA_ACS_MODE_IEEE80211ANY,
+	QCA_ACS_MODE_IEEE80211AX,
 };
 
 /**
@@ -1084,6 +1318,8 @@
  * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
  *	managed regulatory.
  * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time).
+ * @QCA_WLAN_VENDOR_FEATURE_11AX: Device supports 802.11ax (HE)
+ * @QCA_WLAN_VENDOR_FEATURE_6GHZ_SUPPORT: Device supports 6 GHz band operation
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -1096,6 +1332,8 @@
 	QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON            = 6,
 	QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
 	QCA_WLAN_VENDOR_FEATURE_TWT 			= 8,
+	QCA_WLAN_VENDOR_FEATURE_11AX			= 9,
+	QCA_WLAN_VENDOR_FEATURE_6GHZ_SUPPORT		= 10,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -1788,6 +2026,30 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57,
 
+	/* Attribute to configure disconnect IEs to the driver.
+	 * This carries an array of unsigned 8-bit characters.
+	 *
+	 * If this is configured, driver shall fill the IEs in disassoc/deauth
+	 * frame.
+	 * These IEs are expected to be considered only for the next
+	 * immediate disconnection (disassoc/deauth frame) originated by
+	 * the DUT, irrespective of the entity (user space/driver/firmware)
+	 * triggering the disconnection.
+	 * The host drivers are not expected to use the IEs set through
+	 * this interface for further disconnections after the first immediate
+	 * disconnection initiated post the configuration.
+	 * If the IEs are also updated through cfg80211 interface (after the
+	 * enhancement to cfg80211_disconnect), host driver is expected to
+	 * take the union of IEs from both of these interfaces and send in
+	 * further disassoc/deauth frames.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES = 58,
+
+	/* 8-bit unsigned value for ELNA bypass.
+	 * 1-Enable, 0-Disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS = 59,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -1796,10 +2058,23 @@
 
 /**
  * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL: Optional (u8)
+ * Channel number on which Access Point should restart.
+ * Note: If both the driver and user space application supports the 6 GHz band,
+ * this attribute is deprecated and QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY
+ * should be used.
+ * To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL
+ * is still used if either of the driver or user space application doesn't
+ * support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY: Optional (u32)
+ * Channel center frequency (MHz) on which the access point should restart.
  */
 enum qca_wlan_vendor_attr_sap_config {
 	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_INVALID = 0,
-	/* 1 - reserved for QCA */
+	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL = 1,
+
 	/* List of frequencies on which AP is expected to operate.
 	 * This is irrespective of ACS configuration. This list is a priority
 	 * based one and is looked for before the AP is created to ensure the
@@ -1807,6 +2082,7 @@
 	 * the system.
 	 */
 	QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST = 2,
+	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY = 3,
 
 	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX =
@@ -1886,6 +2162,54 @@
 };
 
 /**
+ * enum qca_acs_dfs_mode - Defines different types of DFS channel
+ * configurations for ACS operation.
+ *
+ * @QCA_ACS_DFS_MODE_NONE: Refer to invalid DFS mode
+ * @QCA_ACS_DFS_MODE_ENABLE: Consider DFS channels in ACS operation
+ * @QCA_ACS_DFS_MODE_DISABLE: Do not consider DFS channels in ACS operation
+ * @QCA_ACS_DFS_MODE_DEPRIORITIZE: Deprioritize DFS channels in ACS operation
+ */
+enum qca_acs_dfs_mode {
+	QCA_ACS_DFS_MODE_NONE = 0,
+	QCA_ACS_DFS_MODE_ENABLE = 1,
+	QCA_ACS_DFS_MODE_DISABLE = 2,
+	QCA_ACS_DFS_MODE_DEPRIORITIZE = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_acs_config - Defines Configuration attributes
+ * used by the vendor command QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE: Required (u8)
+ * DFS mode for ACS operation from enum qca_acs_dfs_mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT: Required (u8)
+ * channel number hint for ACS operation, if valid channel is specified then
+ * ACS operation gives priority to this channel.
+ * Note: If both the driver and user space application supports the 6 GHz band,
+ * this attribute is deprecated and QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT
+ * should be used.
+ * To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT
+ * is still used if either of the driver or user space application doesn't
+ * support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT: Required (u32).
+ * Channel center frequency (MHz) hint for ACS operation, if a valid center
+ * frequency is specified, ACS operation gives priority to this channel.
+ */
+enum qca_wlan_vendor_attr_acs_config {
+	QCA_WLAN_VENDOR_ATTR_ACS_MODE_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE = 1,
+	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT = 2,
+	QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT = 3,
+
+	QCA_WLAN_VENDOR_ATTR_ACS_DFS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX =
+		QCA_WLAN_VENDOR_ATTR_ACS_DFS_AFTER_LAST - 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability
  */
 enum qca_wlan_vendor_attr_get_hw_capability {
@@ -3204,11 +3528,28 @@
 /**
  * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by
  * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION: In a request this attribute
+ *	should be set to any U8 value to indicate that the driver version
+ *	should be returned. When enabled in this manner, in a response this
+ *	attribute will contain a string representation of the driver version.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION: In a request this attribute
+ *	should be set to any U8 value to indicate that the firmware version
+ *	should be returned. When enabled in this manner, in a response this
+ *	attribute will contain a string representation of the firmware version.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX: In a request this attribute
+ *	should be set to any U32 value to indicate that the current radio
+ *	index should be returned. When enabled in this manner, in a response
+ *	this attribute will contain a U32 radio index value.
+ *
  */
 enum qca_wlan_vendor_attr_get_wifi_info {
 	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1,
 	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2,
+	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX = 3,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST,
@@ -3251,6 +3592,345 @@
 	QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_scan_freq_list_type: Frequency list types
+ *
+ * @QCA_PREFERRED_SCAN_FREQ_LIST: The driver shall use the scan frequency list
+ *	specified with attribute QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST as
+ *	a preferred frequency list for roaming.
+ *
+ * @QCA_SPECIFIC_SCAN_FREQ_LIST: The driver shall use the frequency list
+ *	specified with attribute QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST as
+ *	a specific frequency list for roaming.
+ */
+enum qca_scan_freq_list_type {
+	QCA_PREFERRED_SCAN_FREQ_LIST = 1,
+	QCA_SPECIFIC_SCAN_FREQ_LIST = 2,
+};
+
+/**
+ * enum qca_vendor_attr_scan_freq_list_scheme: Frequency list scheme
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST: Nested attribute of u32 values
+ *	List of frequencies in MHz to be considered for a roam scan.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE: Unsigned 32-bit value.
+ *	Type of frequency list scheme being configured/gotten as defined by the
+ *	enum qca_scan_freq_list_type.
+ */
+enum qca_vendor_attr_scan_freq_list_scheme {
+	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST = 1,
+	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE = 2,
+
+	/* keep last */
+	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST,
+	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_MAX =
+	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_vendor_roam_triggers: Bitmap of roaming triggers
+ *
+ * @QCA_ROAM_TRIGGER_REASON_PER: Set if the roam has to be triggered based on
+ *	a bad packet error rates (PER).
+ * @QCA_ROAM_TRIGGER_REASON_BEACON_MISS: Set if the roam has to be triggered
+ *	based on beacon misses from the connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_POOR_RSSI: Set if the roam has to be triggered
+ *	due to poor RSSI of the connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_BETTER_RSSI: Set if the roam has to be triggered
+ *	upon finding a BSSID with a better RSSI than the connected BSSID.
+ *	Here the RSSI of the current BSSID need not be poor.
+ * @QCA_ROAM_TRIGGER_REASON_PERIODIC: Set if the roam has to be triggered
+ *	by triggering a periodic scan to find a better AP to roam.
+ * @QCA_ROAM_TRIGGER_REASON_DENSE: Set if the roam has to be triggered
+ *	when the connected channel environment is too noisy/congested.
+ * @QCA_ROAM_TRIGGER_REASON_BTM: Set if the roam has to be triggered
+ *	when BTM Request frame is received from the connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_BSS_LOAD: Set if the roam has to be triggered
+ *	when the channel utilization is goes above the configured threshold.
+ *
+ * Set the corresponding roam trigger reason bit to consider it for roam
+ * trigger.
+ * Userspace can set multiple bits and send to the driver. The driver shall
+ * consider all of them to trigger/initiate a roam scan.
+ */
+enum qca_vendor_roam_triggers {
+	QCA_ROAM_TRIGGER_REASON_PER		= 1 << 0,
+	QCA_ROAM_TRIGGER_REASON_BEACON_MISS	= 1 << 1,
+	QCA_ROAM_TRIGGER_REASON_POOR_RSSI	= 1 << 2,
+	QCA_ROAM_TRIGGER_REASON_BETTER_RSSI	= 1 << 3,
+	QCA_ROAM_TRIGGER_REASON_PERIODIC	= 1 << 4,
+	QCA_ROAM_TRIGGER_REASON_DENSE		= 1 << 5,
+	QCA_ROAM_TRIGGER_REASON_BTM		= 1 << 6,
+	QCA_ROAM_TRIGGER_REASON_BSS_LOAD	= 1 << 7,
+};
+
+/**
+ * enum qca_vendor_attr_roam_candidate_selection_criteria:
+ *
+ * Each attribute carries a weightage in percentage (%).
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the RSSI selection
+ *	criteria among other parameters.
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the rate selection
+ *	criteria among other parameters.
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BW: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the band width selection
+ *	criteria among other parameters.
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BAND: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the band selection
+ *	criteria among other parameters.
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_NSS: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the NSS selection
+ *	criteria among other parameters.
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_CHAN_CONGESTION: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the channel congestion
+ *	selection criteria among other parameters.
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BEAMFORMING: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the beamforming selection
+ *	criteria among other parameters.
+ *
+ * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_OCE_WAN: Unsigned 8-bit value.
+ *	Represents the weightage to be given for the OCE selection
+ *	criteria among other parameters.
+ */
+enum qca_vendor_attr_roam_candidate_selection_criteria {
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI = 1,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE = 2,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BW = 3,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BAND = 4,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_NSS = 5,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_CHAN_CONGESTION = 6,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BEAMFORMING = 7,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_OCE_WAN = 8,
+
+	/* keep last */
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_AFTER_LAST,
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_MAX =
+	QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_vendor_attr_roam_control - Attributes to carry roam configuration
+ * 	The following attributes are used to set/get/clear the respective
+ *	configurations to/from the driver.
+ *	For the get, the attribute for the configuration to be queried shall
+ *	carry any of its acceptable values to the driver. In return, the driver
+ *	shall send the configured values within the same attribute to the user
+ *	space.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_ENABLE: Unsigned 8-bit value.
+ *	Signifies to enable/disable roam control in driver.
+ *	1-enable, 0-disable
+ *	Enable: Mandates the driver to do the further roams using the
+ *	configuration parameters set through
+ *	QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET.
+ *	Disable: Disables the driver/firmware roaming triggered through
+ *	QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET. Further roaming is
+ *	expected to continue with the default configurations.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_STATUS: Unsigned 8-bit value.
+ *	This is used along with QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET.
+ *	Roam control status is obtained through this attribute.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CLEAR_ALL: Flag attribute to indicate the
+ *	complete config set through QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET
+ *	is to be cleared in the driver.
+ *	This is used along with QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR
+ *	and shall be ignored if used with other sub commands.
+ *	If this attribute is specified along with subcmd
+ *	QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR, the driver shall ignore
+ *	all other attributes, if there are any.
+ *	If this attribute is not specified when the subcmd
+ *	QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR is sent, the driver shall
+ *	clear the data corresponding to the attributes specified.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME: Nested attribute to carry the
+ *	list of frequencies and its type, represented by
+ *	enum qca_vendor_attr_scan_freq_list_scheme.
+ *	Frequency list and its type are mandatory for this attribute to set
+ *	the frequencies.
+ *	Frequency type is mandatory for this attribute to get the frequencies
+ *	and the frequency list is obtained through
+ *	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST.
+ *	Frequency list type is mandatory for this attribute to clear the
+ *	frequencies.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD: Unsigned 32-bit value.
+ *	Carries the value of scan period in seconds to set.
+ *	The value of scan period is obtained with the same attribute for get.
+ *	Clears the scan period in the driver when specified with clear command.
+ *	Scan period is the idle time in seconds between each subsequent
+ *	channel scans.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD: Unsigned 32-bit value.
+ *	Carries the value of full scan period in seconds to set.
+ *	The value of full scan period is obtained with the same attribute for
+ *	get.
+ *	Clears the full scan period in the driver when specified with clear
+ *	command. Full scan period is the idle period in seconds between two
+ *	successive full channel roam scans.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_TRIGGERS: Unsigned 32-bit value.
+ *	Carries a bitmap of the roam triggers specified in
+ *	enum qca_vendor_roam_triggers.
+ *	The driver shall enable roaming by enabling corresponding roam triggers
+ *	based on the trigger bits sent with this attribute.
+ *	If this attribute is not configured, the driver shall proceed with
+ *	default behavior.
+ *	The bitmap configured is obtained with the same attribute for get.
+ *	Clears the bitmap configured in driver when specified with clear
+ *	command.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA: Nested attribute signifying the
+ *	weightage in percentage (%) to be given for each selection criteria.
+ *	Different roam candidate selection criteria are represented by
+ *	enum qca_vendor_attr_roam_candidate_selection_criteria.
+ *	The driver shall select the roam candidate based on corresponding
+ *	candidate selection scores sent.
+ *
+ *	An empty nested attribute is used to indicate that no specific
+ *	preference score/criteria is configured (i.e., to disable this mechanism
+ *	in the set case and to show that the mechanism is disabled in the get
+ *	case).
+ *
+ *	Userspace can send multiple attributes out of this enum to the driver.
+ *	Since this attribute represents the weight/percentage of preference for
+ *	the respective selection criteria, it is preferred to configure 100%
+ *	total weightage. The value in each attribute or cumulative weight of the
+ *	values in all the nested attributes should not exceed 100%. The driver
+ *	shall reject such configuration.
+ *
+ *	If the weights configured through this attribute are less than 100%,
+ *	the driver shall honor the weights (x%) passed for the corresponding
+ *	selection criteria and choose/distribute rest of the weight (100-x)%
+ *	for the other selection criteria, based on its internal logic.
+ *
+ *	The selection criteria configured is obtained with the same
+ *	attribute for get.
+ *
+ *	Clears the selection criteria configured in the driver when specified
+ *	with clear command.
+ */
+enum qca_vendor_attr_roam_control {
+	QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
+	QCA_ATTR_ROAM_CONTROL_STATUS = 2,
+	QCA_ATTR_ROAM_CONTROL_CLEAR_ALL = 3,
+	QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME= 4,
+	QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD = 5,
+	QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD = 6,
+	QCA_ATTR_ROAM_CONTROL_TRIGGERS = 7,
+	QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA = 8,
+
+	/* keep last */
+	QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
+	QCA_ATTR_ROAM_CONTROL_MAX =
+	QCA_ATTR_ROAM_CONTROL_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_roaming_config_params: Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD: Unsigned 32-bit value.
+ *	Represents the different roam sub commands referred by
+ *	enum qca_wlan_vendor_roaming_subcmd.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID: Unsigned 32-bit value.
+ *	Represents the Request ID for the specific set of commands.
+ *	This also helps to map specific set of commands to the respective
+ *	ID / client. e.g., helps to identify the user entity configuring the
+ *	Blacklist BSSID and accordingly clear the respective ones with the
+ *	matching ID.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS: Unsigned
+ *	32-bit value.Represents the number of whitelist SSIDs configured.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST: Nested attribute
+ *	to carry the list of Whitelist SSIDs.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID: SSID (binary attribute,
+ *	0..32 octets). Represents the white list SSID. Whitelist SSIDs
+ *	represent the list of SSIDs to which the firmware/driver can consider
+ *	to roam to.
+ *
+ * The following PARAM_A_BAND_XX attributes are applied to 5GHz BSSIDs when
+ * comparing with a 2.4GHz BSSID. They are not applied when comparing two
+ * 5GHz BSSIDs.The following attributes are set through the Roaming SUBCMD -
+ * QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD: Signed 32-bit
+ *	value, RSSI threshold above which 5GHz RSSI is favored.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD: Signed 32-bit
+ *	value, RSSI threshold below which 5GHz RSSI is penalized.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR: Unsigned 32-bit
+ *	value, factor by which 5GHz RSSI is boosted.
+ *	boost=(RSSI_measured-5GHz_boost_threshold)*5GHz_boost_factor
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR: Unsigned 32-bit
+ *	value, factor by which 5GHz RSSI is penalized.
+ *	penalty=(5GHz_penalty_threshold-RSSI_measured)*5GHz_penalty_factor
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST: Unsigned 32-bit
+ *	value, maximum boost that can be applied to a 5GHz RSSI.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS: Unsigned 32-bit
+ *	value, boost applied to current BSSID to ensure the currently
+ *	associated BSSID is favored so as to prevent ping-pong situations.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER: Signed 32-bit
+ *	value, RSSI below which "Alert" roam is enabled.
+ *	"Alert" mode roaming - firmware is "urgently" hunting for another BSSID
+ *	because the RSSI is low, or because many successive beacons have been
+ *	lost or other bad link conditions.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE: Unsigned 32-bit
+ *	value. 1-Enable, 0-Disable. Represents "Lazy" mode, where
+ *	firmware is hunting for a better BSSID or white listed SSID even though
+ *	the RSSI of the link is good. The parameters enabling the roaming are
+ *	configured through the PARAM_A_BAND_XX attrbutes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS: Nested attribute,
+ *	represents the BSSIDs preferred over others while evaluating them
+ *	for the roaming.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID: Unsigned
+ *	32-bit value. Represents the number of preferred BSSIDs set.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID: 6-byte MAC
+ *	address representing the BSSID to be preferred.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER: Signed
+ *	32-bit value, representing the modifier to be applied to the RSSI of
+ *	the BSSID for the purpose of comparing it with other roam candidate.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS: Nested attribute,
+ *	represents the BSSIDs to get blacklisted for roaming.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID: Unsigned
+ *	32-bit value, represents the number of blacklisted BSSIDs.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID: 6-byte MAC
+ *	address representing the Blacklisted BSSID.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT: Flag attribute,
+ *	indicates this BSSID blacklist as a hint to the driver. The driver can
+ *	select this BSSID in the worst case (when no other BSSIDs are better).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL: Nested attribute to
+ *	set/get/clear the roam control config as
+ *	defined @enum qca_vendor_attr_roam_control.
+ */
 enum qca_wlan_vendor_attr_roaming_config_params {
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0,
 
@@ -3287,6 +3967,8 @@
 	/* Flag attribute indicates this BSSID blacklist as a hint */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
 
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL = 22,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX =
@@ -3294,22 +3976,63 @@
 };
 
 /*
- * enum qca_wlan_vendor_attr_roam_subcmd: Attributes for data used by
- * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command.
+ * enum qca_wlan_vendor_roaming_subcmd: Referred by
+ * QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD.
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST: Sub command to
+ *	configure the white list SSIDs. These are configured through
+ *	the following attributes.
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS,
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST,
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS: Sub command to
+ *	configure the Roam params. These parameters are evaluated on the GScan
+ *	results. Refers the attributes PARAM_A_BAND_XX above to configure the
+ *	params.
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM: Sets the Lazy roam. Uses
+ *	the attribute QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE
+ *	to enable/disable Lazy roam.
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS: Sets the BSSID
+ *	preference. Contains the attribute
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS to set the BSSID
+ *	preference.
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the Blacklist
+ *	BSSIDs. Refers QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to
+ *	set the same.
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: Command to set the
+ *	roam control config to the driver with the attribute
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL.
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET: Command to obtain the
+ *	roam control config from driver with the attribute
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL.
+ *	For the get, the attribute for the configuration to be queried shall
+ *	carry any of its acceptable value to the driver. In return, the driver
+ *	shall send the configured values within the same attribute to the user
+ *	space.
+ *
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR: Command to clear the
+ *	roam control config in the driver with the attribute
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL.
+ *	The driver shall continue with its default roaming behavior when data
+ *	corresponding to an attribute is cleared.
  */
-enum qca_wlan_vendor_attr_roam_subcmd {
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_INVALID = 0,
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST = 1,
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2,
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM = 3,
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS = 4,
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PARAMS = 5,
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID = 6,
-
-	/* keep last */
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST,
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX =
-	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST - 1,
+enum qca_wlan_vendor_roaming_subcmd {
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_INVALID = 0,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST = 1,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM = 3,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS = 4,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PARAMS = 5,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID = 6,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET = 7,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET = 8,
+	QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR = 9,
 };
 
 enum qca_wlan_vendor_attr_gscan_config_params {
@@ -3677,8 +4400,8 @@
 
 	/* Unsigned 32-bit value; a GSCAN Capabilities attribute.
 	 * This is used to limit the maximum number of BSSIDs while sending
-	 * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with attributes
-	 * QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID and
+	 * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with subcmd
+	 * QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID and attribute
 	 * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID.
 	 */
 	QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46,
@@ -3780,6 +4503,44 @@
 	QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS,
 	/* Represents the reason that LTE co-exist in the current band. */
 	QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX,
+	/* Represents the reason that generic, uncategorized interference has
+	 * been found in the current channel.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_GENERIC_INTERFERENCE,
+	/* Represents the reason that excessive 802.11 interference has been
+	 * found in the current channel.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_80211_INTERFERENCE,
+	/* Represents the reason that generic Continuous Wave (CW) interference
+	 * has been found in the current channel.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_CW_INTERFERENCE,
+	/* Represents the reason that Microwave Oven (MWO) interference has been
+	 * found in the current channel.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_MWO_INTERFERENCE,
+	/* Represents the reason that generic Frequency-Hopping Spread Spectrum
+	 * (FHSS) interference has been found in the current channel. This may
+	 * include 802.11 waveforms.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_FHSS_INTERFERENCE,
+	/* Represents the reason that non-802.11 generic Frequency-Hopping
+	 * Spread Spectrum (FHSS) interference has been found in the current
+	 * channel.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_NON_80211_FHSS_INTERFERENCE,
+	/* Represents the reason that generic Wideband (WB) interference has
+	 * been found in the current channel. This may include 802.11 waveforms.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_WB_INTERFERENCE,
+	/* Represents the reason that non-802.11 generic Wideband (WB)
+	 * interference has been found in the current channel.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_NON_80211_WB_INTERFERENCE,
+	/* Represents the reason that Jammer interference has been found in the
+	 * current channel.
+	 */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_JAMMER_INTERFERENCE,
 };
 
 /**
@@ -3947,6 +4708,46 @@
 	 */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
 
+	/*
+	 * VHT segment 0 in MHz (u32) and the attribute is mandatory.
+	 * Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
+	 * along with
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0.
+	 *
+	 * If both the driver and user-space application supports the 6 GHz
+	 * band, QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0
+	 * is deprecated and
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
+	 * should be used.
+	 *
+	 * To maintain backward compatibility,
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
+	 * is still used if either of the driver or user space application
+	 * doesn't support the 6 GHz band.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 = 12,
+
+	/*
+	 * VHT segment 1 in MHz (u32) and the attribute is mandatory.
+	 * Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
+	 * along with
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1.
+	 *
+	 * If both the driver and user-space application supports the 6 GHz
+	 * band, QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1
+	 * is deprecated and
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
+	 * should be considered.
+	 *
+	 * To maintain backward compatibility,
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
+	 * is still used if either of the driver or user space application
+	 * doesn't support the 6 GHz band.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 = 13,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
@@ -4047,9 +4848,100 @@
 };
 
 /**
- * qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd
+ * enum qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd
  * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels
  * in priority order as decided after ACS operation in userspace.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON: Required (u8).
+ * One of reason code from enum qca_wlan_vendor_acs_select_reason.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST: Required
+ * Array of nested values for each channel with following attributes:
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST is deprecated and use
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST
+ * is still used if either of the driver or user space application doesn't
+ * support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY: Required (u8).
+ * Primary channel number
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY is deprecated and use
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY
+ * is still used if either of the driver or user space application doesn't
+ * support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY: Required (u8).
+ * Secondary channel number, required only for 160 and 80+80 MHz bandwidths.
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY is deprecated and use
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY
+ * is still used if either of the driver or user space application
+ * doesn't support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0: Required (u8).
+ * VHT seg0 channel number
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 is deprecated and use
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0
+ * is still used if either of the driver or user space application
+ * doesn't support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1: Required (u8).
+ * VHT seg1 channel number
+ * Note: If both the driver and user-space application supports the 6 GHz band,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 is deprecated and use
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1.
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1
+ * is still used if either of the driver or user space application
+ * doesn't support the 6 GHz band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH: Required (u8).
+ * Takes one of enum nl80211_chan_width values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST: Required
+ * Array of nested values for each channel with following attributes:
+ *	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY in MHz (u32),
+ *	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY in MHz (u32),
+ *	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 in MHz (u32),
+ *	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 in MHz (u32),
+ *	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
+ * Note: If user-space application has no support of the 6 GHz band, this
+ * attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY: Required (u32)
+ * Primary channel frequency in MHz
+ * Note: If user-space application has no support of the 6 GHz band, this
+ * attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY: Required (u32)
+ * Secondary channel frequency in MHz used for HT 40 MHz channels.
+ * Note: If user-space application has no support of the 6 GHz band, this
+ * attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0: Required (u32)
+ * VHT seg0 channel frequency in MHz
+ * Note: If user-space application has no support of the 6GHz band, this
+ * attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1: Required (u32)
+ * VHT seg1 channel frequency in MHz
+ * Note: If user-space application has no support of the 6 GHz band, this
+ * attribute is optional.
  */
 enum qca_wlan_vendor_attr_external_acs_channels {
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
@@ -4080,6 +4972,12 @@
 	/* Channel width (u8). Takes one of enum nl80211_chan_width values. */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8,
 
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST = 9,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY = 10,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY = 11,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX =
@@ -4481,6 +5379,44 @@
 	 * qca_wlan_vendor_attr_spectral_scan_request_type.
 	 */
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23,
+	/* This specifies the frequency span over which spectral
+	 * scan would be carried out. Its value depends on the
+	 * value of QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE and
+	 * the relation is as follows.
+	 * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL
+	 *    Not applicable. Spectral scan would happen in the
+	 *    operating span.
+	 * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE
+	 *    Center frequency (in MHz) of the span of interest or
+	 *    for convenience, center frequency (in MHz) of any channel
+	 *    in the span of interest. If agile spectral scan is initiated
+	 *    without setting a valid frequency it returns the error code
+	 *    (QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED).
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FREQUENCY = 24,
+	/* Spectral scan mode. u32 attribute.
+	 * It uses values defined in enum qca_wlan_vendor_spectral_scan_mode.
+	 * If this attribute is not present, it is assumed to be
+	 * normal mode (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL).
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE = 25,
+	/* Spectral scan error code. u32 attribute.
+	 * It uses values defined in enum
+	 * qca_wlan_vendor_spectral_scan_error_code.
+	 * This attribute is included only in failure scenarios.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE = 26,
+	/* 8-bit unsigned value to enable/disable debug of the
+	 * Spectral DMA ring.
+	 * 1-enable, 0-disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_RING_DEBUG = 27,
+	/* 8-bit unsigned value to enable/disable debug of the
+	 * Spectral DMA buffers.
+	 * 1-enable, 0-disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_BUFFER_DEBUG = 28,
 
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
@@ -4559,6 +5495,18 @@
 	 * u8 attribute.
 	 */
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10,
+	/* Flag attribute to indicate agile spectral scan capability
+	 * for 20/40/80 MHz modes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL = 11,
+	/* Flag attribute to indicate agile spectral scan capability
+	 * for 160 MHz mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL_160 = 12,
+	/* Flag attribute to indicate agile spectral scan capability
+	 * for 80+80 MHz mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL_80_80 = 13,
 
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX =
@@ -4575,6 +5523,13 @@
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1,
 	/* Flag attribute to indicate whether spectral scan is in progress*/
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2,
+	/* Spectral scan mode. u32 attribute.
+	 * It uses values defined in enum qca_wlan_vendor_spectral_scan_mode.
+	 * If this attribute is not present, normal mode
+	 * (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL is assumed to be
+	 * requested.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE = 3,
 
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX =
@@ -4600,6 +5555,43 @@
 };
 
 /**
+ * qca_wlan_vendor_spectral_scan_mode: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE in the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START and
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE in the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS. This represents the
+ * spectral scan modes.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL: Normal spectral scan:
+ * spectral scan in the current operating span.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE: Agile spectral scan:
+ * spectral scan in the configured agile span.
+ */
+enum qca_wlan_vendor_spectral_scan_mode {
+	QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL = 0,
+	QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE = 1,
+};
+
+/**
+ * qca_wlan_vendor_spectral_scan_error_code: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE in the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED: Changing the value
+ * of a parameter is not supported.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED: Requested spectral scan
+ * mode is not supported.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE: A parameter
+ * has invalid value.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED: A parameter
+ * is not initialized.
+ */
+enum qca_wlan_vendor_spectral_scan_error_code {
+	QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED = 0,
+	QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED = 1,
+	QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE = 2,
+	QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED = 3,
+};
+
+/**
  * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for
  * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd
  * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
@@ -6709,4 +7701,466 @@
 		QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1
 };
 
+/**
+ * enum qca_wlan_vendor_beacon_reporting_op_types - Defines different types of
+ * operations for which %QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING can be used.
+ * Will be used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START: Sent by userspace to the driver
+ * to request the driver to start reporting Beacon frames.
+ * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP: Sent by userspace to the driver to
+ * request the driver to stop reporting Beacon frames.
+ * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO: Sent by the driver to
+ * userspace to report received Beacon frames.
+ * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE: Sent by the driver to userspace
+ * to indicate that the driver is going to pause reporting Beacon frames.
+ */
+enum qca_wlan_vendor_beacon_reporting_op_types {
+	QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START = 0,
+	QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP = 1,
+	QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO = 2,
+	QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_beacon_reporting_pause_reasons - Defines different types
+ * of reasons for which the driver is pausing reporting Beacon frames. Will be
+ * used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON.
+ *
+ * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED: For unspecified
+ * reasons.
+ * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED: When the
+ * driver/firmware is starting a scan.
+ * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED: When the
+ * driver/firmware disconnects from the ESS and indicates the disconnection to
+ * userspace (non-seamless roaming case). This reason code will be used by the
+ * driver/firmware to indicate stopping of beacon report events. Userspace will
+ * need to start beacon reporting again (if desired) by sending vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING with
+ * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE set to
+ * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START after the next connection is
+ * completed.
+ */
+enum qca_wlan_vendor_beacon_reporting_pause_reasons {
+	QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED = 0,
+	QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED = 1,
+	QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED = 2,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_beacon_reporting_params - List of attributes used
+ * in vendor sub-command QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING.
+ */
+enum qca_wlan_vendor_attr_beacon_reporting_params {
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_INVALID = 0,
+	/* Specifies the type of operation that the vendor command/event is
+	 * intended for. Possible values for this attribute are defined in
+	 * enum qca_wlan_vendor_beacon_reporting_op_types. u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE = 1,
+	/* Optionally set by userspace to request the driver to report Beacon
+	 * frames using asynchronous vendor events when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute.
+	 * If this flag is not set, the driver will only update Beacon frames in
+	 * cfg80211 scan cache but not send any vendor events.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_ACTIVE_REPORTING = 2,
+	/* Optionally used by userspace to request the driver/firmware to report
+	 * Beacon frames periodically when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START.
+	 * u32 attribute, indicates the period of Beacon frames to be reported
+	 * and in the units of beacon interval.
+	 * If this attribute is missing in the command, then the default value
+	 * of 1 will be assumed by driver, i.e., to report every Beacon frame.
+	 * Zero is an invalid value.
+	 * If a valid value is received for this attribute, the driver will
+	 * update the cfg80211 scan cache periodically as per the value received
+	 * in this attribute in addition to updating the cfg80211 scan cache
+	 * when there is significant change in Beacon frame IEs.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PERIOD = 3,
+	/* Used by the driver to encapsulate the SSID when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
+	 * u8 array with a maximum size of 32.
+	 *
+	 * When generating beacon report from non-MBSSID Beacon frame, the SSID
+	 * will be taken from the SSID element of the received Beacon frame.
+	 *
+	 * When generating beacon report from Multiple BSSID Beacon frame and if
+	 * the BSSID of the current connected BSS matches the BSSID of the
+	 * transmitting BSS, the SSID will be taken from the SSID element of the
+	 * received Beacon frame.
+	 *
+	 * When generating beacon report from Multiple BSSID Beacon frame and if
+	 * the BSSID of the current connected BSS matches the BSSID of one of
+	 * the* nontransmitting BSSs, the SSID will be taken from the SSID field
+	 * included in the nontransmitted BSS profile whose derived BSSID is
+	 * same as the BSSID of the current connected BSS. When there is no
+	 * nontransmitted BSS profile whose derived BSSID is same as the BSSID
+	 * of current connected* BSS, this attribute will not be present.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_SSID = 4,
+	/* Used by the driver to encapsulate the BSSID of the AP to which STA is
+	 * currently connected to when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array with a
+	 * fixed size of 6 bytes.
+	 *
+	 * When generating beacon report from a Multiple BSSID beacon and the
+	 * current connected BSSID matches one of the nontransmitted BSSIDs in a
+	 * Multiple BSSID set, this BSSID will be that particular nontransmitted
+	 * BSSID and not the transmitted BSSID (i.e., the transmitting address
+	 * of the Beacon frame).
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BSSID = 5,
+	/* Used by the driver to encapsulate the frequency in MHz on which
+	 * the Beacon frame was received when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is
+	 * set to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
+	 * u32 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_FREQ = 6,
+	/* Used by the driver to encapsulate the Beacon interval
+	 * when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
+	 * u16 attribute. The value will be copied from the Beacon frame and the
+	 * units are TUs.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BI = 7,
+	/* Used by the driver to encapsulate the Timestamp field from the Beacon
+	 * frame when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set
+	 * to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
+	 * u64 attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_TSF = 8,
+	/* Used by the driver to encapsulate the CLOCK_BOOTTIME when this
+	 * Beacon frame is received in the driver when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u64 attribute, in
+	 * the units of nanoseconds. This value is expected to have accuracy of
+	 * about 10 ms.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BOOTTIME_WHEN_RECEIVED = 9,
+	/* Used by the driver to encapsulate the IEs of the Beacon frame from
+	 * which this event is generated when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_IES = 10,
+	/* Used by the driver to specify the reason for the driver/firmware to
+	 * pause sending beacons to userspace when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. Possible values are
+	 * defined in enum qca_wlan_vendor_beacon_reporting_pause_reasons, u32
+	 * attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON = 11,
+	/* Used by the driver to specify whether the driver will automatically
+	 * resume reporting beacon events to userspace later (for example after
+	 * the ongoing off-channel activity is completed etc.) when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. NLA_FLAG attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES = 12,
+	/* Optionally set by userspace to request the driver not to resume
+	 * beacon reporting after a pause is completed, when the
+	 * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute.
+	 * If this flag is set, the driver will not resume beacon reporting
+	 * after any pause in beacon reporting is completed. Userspace has to
+	 * send QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command again in order
+	 * to initiate beacon reporting again. If this flag is set in the recent
+	 * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command, then in the
+	 * subsequent QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE event (if any)
+	 * the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES shall not be
+	 * set by the driver. Setting this flag until and unless there is a
+	 * specific need is not recommended as there is a chance of some beacons
+	 * received after pause command and next start command being not
+	 * reported.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME = 13,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST,
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_MAX =
+		QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST - 1
+};
+
+/**
+ * enum qca_vendor_interop_issues_ap_type - Interop issue types
+ * This enum defines the valid set of values of interop issue types. These
+ * values are used by attribute %QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE.
+ *
+ * @QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS: The AP has power save interop issue
+ * when the STA's Qpower feature is enabled.
+ */
+enum qca_vendor_interop_issues_ap_type {
+	QCA_VENDOR_INTEROP_ISSUES_AP_INVALID = 0,
+	QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS = 1,
+};
+
+/**
+ * enum qca_vendor_attr_interop_issues_ap - attribute for AP with interop issues
+ * Values are used by %QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID: Invalid value
+ * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE: Interop issue type
+ * 32-bit unsigned value. The values defined in enum
+ * qca_vendor_interop_issues_ap_type are used.
+ * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST: APs' BSSID container
+ * array of nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID attributes.
+ * It is present and mandatory for the command but is not used for the event
+ * since only a single BSSID is reported in an event.
+ * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID: AP's BSSID 6-byte MAC address.
+ * It is used within the nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST
+ * attribute in command case and without such encapsulation in the event case.
+ * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST: last value
+ * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX: max value
+ */
+enum qca_vendor_attr_interop_issues_ap {
+	QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID,
+	QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE,
+	QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST,
+	QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX =
+		QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_vendor_oem_device_type - Represents the target device in firmware.
+ * It is used by QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO.
+ *
+ * @QCA_VENDOR_OEM_DEVICE_VIRTUAL: The command is intended for
+ * a virtual device.
+ *
+ * @QCA_VENDOR_OEM_DEVICE_PHYSICAL: The command is intended for
+ * a physical device.
+ */
+enum qca_vendor_oem_device_type {
+	QCA_VENDOR_OEM_DEVICE_VIRTUAL = 0,
+	QCA_VENDOR_OEM_DEVICE_PHYSICAL = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_oem_data_params - Used by the vendor command/event
+ * QCA_NL80211_VENDOR_SUBCMD_OEM_DATA.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: The binary blob for the vendor
+ * command/event QCA_NL80211_VENDOR_SUBCMD_OEM_DATA are carried through this
+ * attribute.
+ * NLA_BINARY attribute, the max size is 1024 bytes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO: The binary blob will be routed
+ * based on this field. This optional attribute is included to specify whether
+ * the device type is a virtual device or a physical device for the
+ * command/event. This attribute can be omitted for a virtual device (default)
+ * command/event.
+ * This u8 attribute is used to carry information for the device type using
+ * values defined by enum qca_vendor_oem_device_type.
+ */
+enum qca_wlan_vendor_attr_oem_data_params {
+	QCA_WLAN_VENDOR_ATTR_OEM_DATA_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA = 1,
+	QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_MAX =
+		QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_avoid_frequency_ext - Defines attributes to be
+ * used with vendor command/event QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE: Required
+ * Nested attribute containing multiple ranges with following attributes:
+ *	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START and
+ *	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START: Required (u32)
+ * Starting center frequency in MHz.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END: Required (u32)
+ * Ending center frequency in MHz.
+ */
+enum qca_wlan_vendor_attr_avoid_frequency_ext {
+	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE = 1,
+	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START = 2,
+	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END = 3,
+
+	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX =
+		QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST - 1
+};
+
+/*
+ * enum qca_wlan_vendor_attr_add_sta_node_params - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE.
+ */
+enum qca_wlan_vendor_attr_add_sta_node_params {
+	QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_INVALID = 0,
+	/* 6 byte MAC address of STA */
+	QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_MAC_ADDR = 1,
+	/* Authentication algorithm used by the station of size u16;
+	 * defined in enum nl80211_auth_type.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_MAX =
+		QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_btc_chain_mode - Specifies BT coex chain mode.
+ * This enum defines the valid set of values of BT coex chain mode.
+ * These values are used by attribute %QCA_VENDOR_ATTR_BTC_CHAIN_MODE of
+ * %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE.
+ *
+ * @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4G are shared.
+ * @QCA_BTC_CHAIN_SEPARATED: chains of BT and WLAN 2.4G are separated.
+ */
+enum qca_btc_chain_mode {
+	QCA_BTC_CHAIN_SHARED = 0,
+	QCA_BTC_CHAIN_SEPARATED = 1,
+};
+
+/**
+ * enum qca_vendor_attr_btc_chain_mode - Specifies attributes for BT coex
+ * chain mode.
+ * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE.
+ *
+ * @QCA_VENDOR_ATTR_COEX_BTC_CHAIN_MODE: u32 attribute.
+ * Indicates the BT coex chain mode, are 32-bit values from
+ * enum qca_btc_chain_mode. This attribute is mandatory.
+ *
+ * @QCA_VENDOR_ATTR_COEX_BTC_CHAIN_MODE_RESTART: flag attribute.
+ * If set, vdev should be restarted when BT coex chain mode is updated.
+ * This attribute is optional.
+ */
+enum qca_vendor_attr_btc_chain_mode {
+	QCA_VENDOR_ATTR_BTC_CHAIN_MODE_INVALID = 0,
+	QCA_VENDOR_ATTR_BTC_CHAIN_MODE = 1,
+	QCA_VENDOR_ATTR_BTC_CHAIN_MODE_RESTART = 2,
+
+	/* Keep last */
+	QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST,
+	QCA_VENDOR_ATTR_BTC_CHAIN_MODE_MAX =
+	QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST - 1,
+};
+
+/**
+ * enum qca_vendor_wlan_sta_flags - Station feature flags
+ * Bits will be set to 1 if the corresponding features are enabled.
+ * @QCA_VENDOR_WLAN_STA_FLAG_AMPDU: AMPDU is enabled for the station
+ * @QCA_VENDOR_WLAN_STA_FLAG_TX_STBC: TX Space-time block coding is enabled
+    for the station
+ * @QCA_VENDOR_WLAN_STA_FLAG_RX_STBC: RX Space-time block coding is enabled
+    for the station
+ */
+enum qca_vendor_wlan_sta_flags {
+	QCA_VENDOR_WLAN_STA_FLAG_AMPDU = BIT(0),
+	QCA_VENDOR_WLAN_STA_FLAG_TX_STBC = BIT(1),
+	QCA_VENDOR_WLAN_STA_FLAG_RX_STBC = BIT(2),
+};
+
+/**
+ * enum qca_vendor_wlan_sta_guard_interval - Station guard interval
+ * @QCA_VENDOR_WLAN_STA_GI_800_NS: Legacy normal guard interval
+ * @QCA_VENDOR_WLAN_STA_GI_400_NS: Legacy short guard interval
+ * @QCA_VENDOR_WLAN_STA_GI_1600_NS: Guard interval used by HE
+ * @QCA_VENDOR_WLAN_STA_GI_3200_NS: Guard interval used by HE
+ */
+enum qca_vendor_wlan_sta_guard_interval {
+	QCA_VENDOR_WLAN_STA_GI_800_NS = 0,
+	QCA_VENDOR_WLAN_STA_GI_400_NS = 1,
+	QCA_VENDOR_WLAN_STA_GI_1600_NS = 2,
+	QCA_VENDOR_WLAN_STA_GI_3200_NS = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_sta_info - Defines attributes
+ * used by QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC:
+ * Required attribute in request, 6-byte MAC address,
+ * used in both STA and AP modes.
+ * MAC address of the station for which information is requested (BSSID of the
+ * AP in STA mode).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS:
+ * Optionally used in response, u32 attribute, contains a bitmap of different
+ * fields defined in enum qca_vendor_wlan_sta_flags, used in AP mode only.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL:
+ * Optionally used in response, u32 attribute, possible values are defined in
+ * enum qca_vendor_wlan_sta_guard_interval, used in AP mode only.
+ * Guard interval used by the station.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames received from station with retry
+ * bit set to 1 in FC.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Counter for number of data frames with broadcast or multicast address in
+ * the destination address received from the station.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED:
+ * Optionally used in response, u32 attribute, used in both STA and AP modes.
+ * Value indicates the number of data frames successfully transmitted only
+ * after retrying the packets and for which the TX status has been updated
+ * back to host from target.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames not transmitted successfully even
+ * after retrying the packets for the number of times equal to the total number
+ * of retries allowed for that packet and for which the TX status has been
+ * updated back to host from target.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Counter in the target for the number of data frames successfully transmitted
+ * to the station.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames successfully transmitted only
+ * after retrying the packets.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames not transmitted successfully even
+ * after retrying the packets for the number of times equal to the total number
+ * of retries allowed for that packet.
+ */
+enum qca_wlan_vendor_attr_get_sta_info {
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC = 1,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS = 2,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL = 3,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT = 4,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT = 5,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED = 6,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED = 7,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL = 8,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY = 9,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED = 10,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 0d56e55..bf8cc9d 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -12,38 +12,27 @@
 #include "utils/const_time.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "crypto/dh_groups.h"
 #include "ieee802_11_defs.h"
+#include "dragonfly.h"
 #include "sae.h"
 
 
-static int sae_suitable_group(int group)
-{
-#ifdef CONFIG_TESTING_OPTIONS
-	/* Allow all groups for testing purposes in non-production builds. */
-	return 1;
-#else /* CONFIG_TESTING_OPTIONS */
-	/* Enforce REVmd rules on which SAE groups are suitable for production
-	 * purposes: FFC groups whose prime is >= 3072 bits and ECC groups
-	 * defined over a prime field whose prime is >= 256 bits. Furthermore,
-	 * ECC groups defined over a characteristic 2 finite field and ECC
-	 * groups with a co-factor greater than 1 are not suitable. */
-	return group == 19 || group == 20 || group == 21 ||
-		group == 28 || group == 29 || group == 30 ||
-		group == 15 || group == 16 || group == 17 || group == 18;
-#endif /* CONFIG_TESTING_OPTIONS */
-}
-
-
 int sae_set_group(struct sae_data *sae, int group)
 {
 	struct sae_temporary_data *tmp;
 
-	if (!sae_suitable_group(group)) {
+#ifdef CONFIG_TESTING_OPTIONS
+	/* Allow all groups for testing purposes in non-production builds. */
+#else /* CONFIG_TESTING_OPTIONS */
+	if (!dragonfly_suitable_group(group, 0)) {
 		wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
 		return -1;
 	}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	sae_clear_data(sae);
 	tmp = sae->tmp = os_zalloc(sizeof(*tmp));
@@ -58,6 +47,7 @@
 		sae->group = group;
 		tmp->prime_len = crypto_ec_prime_len(tmp->ec);
 		tmp->prime = crypto_ec_get_prime(tmp->ec);
+		tmp->order_len = crypto_ec_order_len(tmp->ec);
 		tmp->order = crypto_ec_get_order(tmp->ec);
 		return 0;
 	}
@@ -82,6 +72,7 @@
 		}
 		tmp->prime = tmp->prime_buf;
 
+		tmp->order_len = tmp->dh->order_len;
 		tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
 							tmp->dh->order_len);
 		if (tmp->order_buf == NULL) {
@@ -118,6 +109,8 @@
 	crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
 	crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
 	wpabuf_free(tmp->anti_clogging_token);
+	wpabuf_free(tmp->own_rejected_groups);
+	wpabuf_free(tmp->peer_rejected_groups);
 	os_free(tmp->pw_id);
 	bin_clear_free(tmp, sizeof(*tmp));
 	sae->tmp = NULL;
@@ -134,58 +127,6 @@
 }
 
 
-static void buf_shift_right(u8 *buf, size_t len, size_t bits)
-{
-	size_t i;
-	for (i = len - 1; i > 0; i--)
-		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
-	buf[0] >>= bits;
-}
-
-
-static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
-{
-	u8 val[SAE_MAX_PRIME_LEN];
-	int iter = 0;
-	struct crypto_bignum *bn = NULL;
-	int order_len_bits = crypto_bignum_bits(sae->tmp->order);
-	size_t order_len = (order_len_bits + 7) / 8;
-
-	if (order_len > sizeof(val))
-		return NULL;
-
-	for (;;) {
-		if (iter++ > 100 || random_get_bytes(val, order_len) < 0)
-			return NULL;
-		if (order_len_bits % 8)
-			buf_shift_right(val, order_len, 8 - order_len_bits % 8);
-		bn = crypto_bignum_init_set(val, order_len);
-		if (bn == NULL)
-			return NULL;
-		if (crypto_bignum_is_zero(bn) ||
-		    crypto_bignum_is_one(bn) ||
-		    crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
-			crypto_bignum_deinit(bn, 0);
-			continue;
-		}
-		break;
-	}
-
-	os_memset(val, 0, order_len);
-	return bn;
-}
-
-
-static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae)
-{
-	crypto_bignum_deinit(sae->tmp->sae_rand, 1);
-	sae->tmp->sae_rand = sae_get_rand(sae);
-	if (sae->tmp->sae_rand == NULL)
-		return NULL;
-	return sae_get_rand(sae);
-}
-
-
 static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
 {
 	wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
@@ -200,103 +141,6 @@
 }
 
 
-static struct crypto_bignum *
-get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
-		  int *r_odd)
-{
-	for (;;) {
-		struct crypto_bignum *r;
-		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
-
-		if (random_get_bytes(tmp, prime_len) < 0)
-			break;
-		if (prime_bits % 8)
-			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
-		if (os_memcmp(tmp, prime, prime_len) >= 0)
-			continue;
-		r = crypto_bignum_init_set(tmp, prime_len);
-		if (!r)
-			break;
-		if (crypto_bignum_is_zero(r)) {
-			crypto_bignum_deinit(r, 0);
-			continue;
-		}
-
-		*r_odd = tmp[prime_len - 1] & 0x01;
-		return r;
-	}
-
-	return NULL;
-}
-
-
-static int is_quadratic_residue_blind(struct sae_data *sae,
-				      const u8 *prime, size_t bits,
-				      const u8 *qr, const u8 *qnr,
-				      const struct crypto_bignum *y_sqr)
-{
-	struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
-	int r_odd, check, res = -1;
-	u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
-	size_t prime_len = sae->tmp->prime_len;
-	unsigned int mask;
-
-	/*
-	 * Use the blinding technique to mask y_sqr while determining
-	 * whether it is a quadratic residue modulo p to avoid leaking
-	 * timing information while determining the Legendre symbol.
-	 *
-	 * v = y_sqr
-	 * r = a random number between 1 and p-1, inclusive
-	 * num = (v * r * r) modulo p
-	 */
-	r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
-	if (!r)
-		return -1;
-
-	num = crypto_bignum_init();
-	if (!num ||
-	    crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 ||
-	    crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
-		goto fail;
-
-	/*
-	 * Need to minimize differences in handling different cases, so try to
-	 * avoid branches and timing differences.
-	 *
-	 * If r_odd:
-	 * num = (num * qr) module p
-	 * LGR(num, p) = 1 ==> quadratic residue
-	 * else:
-	 * num = (num * qnr) module p
-	 * LGR(num, p) = -1 ==> quadratic residue
-	 */
-	mask = const_time_is_zero(r_odd);
-	const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
-	qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
-	if (!qr_or_qnr ||
-	    crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
-		goto fail;
-	/* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
-	check = const_time_select_int(mask, -1, 1);
-
-	res = crypto_bignum_legendre(num, sae->tmp->prime);
-	if (res == -2) {
-		res = -1;
-		goto fail;
-	}
-	/* branchless version of res = res == check
-	 * (res is -1, 0, or 1; check is -1 or 1) */
-	mask = const_time_eq(res, check);
-	res = const_time_select_int(mask, 1, 0);
-fail:
-	crypto_bignum_deinit(num, 1);
-	crypto_bignum_deinit(r, 1);
-	crypto_bignum_deinit(qr_or_qnr, 1);
-	return res;
-}
-
-
 static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
 				 const u8 *prime, const u8 *qr, const u8 *qnr,
 				 u8 *pwd_value)
@@ -304,6 +148,8 @@
 	struct crypto_bignum *y_sqr, *x_cand;
 	int res;
 	size_t bits;
+	int cmp_prime;
+	unsigned int in_range;
 
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
@@ -317,8 +163,13 @@
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
 			pwd_value, sae->tmp->prime_len);
 
-	if (const_time_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
-		return 0;
+	cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len);
+	/* Create a const_time mask for selection based on prf result
+	 * being smaller than prime. */
+	in_range = const_time_fill_msb((unsigned int) cmp_prime);
+	/* The algorithm description would skip the next steps if
+	 * cmp_prime >= 0 (reutnr 0 here), but go through them regardless to
+	 * minimize externally observable differences in behavior. */
 
 	x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
 	if (!x_cand)
@@ -328,9 +179,12 @@
 	if (!y_sqr)
 		return -1;
 
-	res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
+	res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr,
+						   y_sqr);
 	crypto_bignum_deinit(y_sqr, 1);
-	return res;
+	if (res < 0)
+		return res;
+	return const_time_select_int(in_range, res, 0);
 }
 
 
@@ -423,47 +277,11 @@
 }
 
 
-static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
-			     const struct crypto_bignum *prime_bn,
-			     size_t prime_bits, struct crypto_bignum **qr,
-			     struct crypto_bignum **qnr)
-{
-	*qr = NULL;
-	*qnr = NULL;
-
-	while (!(*qr) || !(*qnr)) {
-		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
-		struct crypto_bignum *q;
-		int res;
-
-		if (random_get_bytes(tmp, prime_len) < 0)
-			break;
-		if (prime_bits % 8)
-			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
-		if (os_memcmp(tmp, prime, prime_len) >= 0)
-			continue;
-		q = crypto_bignum_init_set(tmp, prime_len);
-		if (!q)
-			break;
-		res = crypto_bignum_legendre(q, prime_bn);
-
-		if (res == 1 && !(*qr))
-			*qr = q;
-		else if (res == -1 && !(*qnr))
-			*qnr = q;
-		else
-			crypto_bignum_deinit(q, 0);
-	}
-
-	return (*qr && *qnr) ? 0 : -1;
-}
-
-
 static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
 			      const u8 *addr2, const u8 *password,
 			      size_t password_len, const char *identifier)
 {
-	u8 counter, k = 40;
+	u8 counter, k;
 	u8 addrs[2 * ETH_ALEN];
 	const u8 *addr[3];
 	size_t len[3];
@@ -477,7 +295,6 @@
 	u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
 	u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
 	u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
-	size_t bits;
 	int res = -1;
 	u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
 		       * mask */
@@ -494,14 +311,12 @@
 	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
 				 prime_len) < 0)
 		goto fail;
-	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
 
 	/*
 	 * Create a random quadratic residue (qr) and quadratic non-residue
 	 * (qnr) modulo p for blinding purposes during the loop.
 	 */
-	if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
-			      &qr, &qnr) < 0 ||
+	if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 ||
 	    crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
 	    crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
 		goto fail;
@@ -537,6 +352,8 @@
 	 * attacks that attempt to determine the number of iterations required
 	 * in the loop.
 	 */
+	k = dragonfly_min_pwe_loop_iter(sae->group);
+
 	for (counter = 1; counter <= k || !found; counter++) {
 		u8 pwd_seed[SHA256_MAC_LEN];
 
@@ -618,13 +435,6 @@
 }
 
 
-static int sae_modp_group_require_masking(int group)
-{
-	/* Groups for which pwd-value is likely to be >= p frequently */
-	return group == 22 || group == 23 || group == 24;
-}
-
-
 static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
 			      const u8 *addr2, const u8 *password,
 			      size_t password_len, const char *identifier)
@@ -673,7 +483,7 @@
 	len[num_elem] = sizeof(counter);
 	num_elem++;
 
-	k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
+	k = dragonfly_min_pwe_loop_iter(sae->group);
 
 	for (counter = 1; counter <= k || !found; counter++) {
 		u8 pwd_seed[SHA256_MAC_LEN];
@@ -719,6 +529,748 @@
 }
 
 
+static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len,
+			size_t num_elem, const u8 *addr[], const size_t len[],
+			u8 *prk)
+{
+	if (hash_len == 32)
+		return hmac_sha256_vector(salt, salt_len, num_elem, addr, len,
+					  prk);
+#ifdef CONFIG_SHA384
+	if (hash_len == 48)
+		return hmac_sha384_vector(salt, salt_len, num_elem, addr, len,
+					  prk);
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+	if (hash_len == 64)
+		return hmac_sha512_vector(salt, salt_len, num_elem, addr, len,
+					  prk);
+#endif /* CONFIG_SHA512 */
+	return -1;
+}
+
+
+static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len,
+		       const char *info, u8 *okm, size_t okm_len)
+{
+	size_t info_len = os_strlen(info);
+
+	if (hash_len == 32)
+		return hmac_sha256_kdf(prk, prk_len, NULL,
+				       (const u8 *) info, info_len,
+				       okm, okm_len);
+#ifdef CONFIG_SHA384
+	if (hash_len == 48)
+		return hmac_sha384_kdf(prk, prk_len, NULL,
+				       (const u8 *) info, info_len,
+				       okm, okm_len);
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+	if (hash_len == 64)
+		return hmac_sha512_kdf(prk, prk_len, NULL,
+				       (const u8 *) info, info_len,
+				       okm, okm_len);
+#endif /* CONFIG_SHA512 */
+	return -1;
+}
+
+
+static int sswu_curve_param(int group, int *z)
+{
+	switch (group) {
+	case 19:
+		*z = -10;
+		return 0;
+	case 20:
+		*z = -12;
+		return 0;
+	case 21:
+		*z = -4;
+		return 0;
+	case 25:
+	case 29:
+		*z = -5;
+		return 0;
+	case 26:
+		*z = 31;
+		return 0;
+	case 28:
+		*z = -2;
+		return 0;
+	case 30:
+		*z = 7;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static void debug_print_bignum(const char *title, const struct crypto_bignum *a,
+			       size_t prime_len)
+{
+	u8 *bin;
+
+	bin = os_malloc(prime_len);
+	if (bin && crypto_bignum_to_bin(a, bin, prime_len, prime_len) >= 0)
+		wpa_hexdump_key(MSG_DEBUG, title, bin, prime_len);
+	else
+		wpa_printf(MSG_DEBUG, "Could not print bignum (%s)", title);
+	bin_clear_free(bin, prime_len);
+}
+
+
+static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
+				     const struct crypto_bignum *u)
+{
+	int z_int;
+	const struct crypto_bignum *a, *b, *prime;
+	struct crypto_bignum *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three,
+		*x1a, *x1b, *y = NULL;
+	struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL;
+	unsigned int m_is_zero, is_qr, is_eq;
+	size_t prime_len;
+	u8 bin[SAE_MAX_ECC_PRIME_LEN];
+	u8 bin1[SAE_MAX_ECC_PRIME_LEN];
+	u8 bin2[SAE_MAX_ECC_PRIME_LEN];
+	u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN];
+	struct crypto_ec_point *p = NULL;
+
+	if (sswu_curve_param(group, &z_int) < 0)
+		return NULL;
+
+	prime = crypto_ec_get_prime(ec);
+	prime_len = crypto_ec_prime_len(ec);
+	a = crypto_ec_get_a(ec);
+	b = crypto_ec_get_b(ec);
+
+	u2 = crypto_bignum_init();
+	t1 = crypto_bignum_init();
+	t2 = crypto_bignum_init();
+	z = crypto_bignum_init_uint(abs(z_int));
+	t = crypto_bignum_init();
+	zero = crypto_bignum_init_uint(0);
+	one = crypto_bignum_init_uint(1);
+	two = crypto_bignum_init_uint(2);
+	three = crypto_bignum_init_uint(3);
+	x1a = crypto_bignum_init();
+	x1b = crypto_bignum_init();
+	x2 = crypto_bignum_init();
+	gx1 = crypto_bignum_init();
+	gx2 = crypto_bignum_init();
+	if (!u2 || !t1 || !t2 || !z || !t || !zero || !one || !two || !three ||
+	    !x1a || !x1b || !x2 || !gx1 || !gx2)
+		goto fail;
+
+	if (z_int < 0 && crypto_bignum_sub(prime, z, z) < 0)
+		goto fail;
+
+	/* m = z^2 * u^4 + z * u^2 */
+	/* --> tmp = z * u^2, m = tmp^2 + tmp */
+
+	/* u2 = u^2
+	 * t1 = z * u2
+	 * t2 = t1^2
+	 * m = t1 = t1 + t2 */
+	if (crypto_bignum_sqrmod(u, prime, u2) < 0 ||
+	    crypto_bignum_mulmod(z, u2, prime, t1) < 0 ||
+	    crypto_bignum_sqrmod(t1, prime, t2) < 0 ||
+	    crypto_bignum_addmod(t1, t2, prime, t1) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: m", t1, prime_len);
+
+	/* l = CEQ(m, 0)
+	 * t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as
+	 * x^(p-2) modulo p which will handle m == 0 case correctly */
+	/* TODO: Make sure crypto_bignum_is_zero() is constant time */
+	m_is_zero = const_time_eq(crypto_bignum_is_zero(t1), 1);
+	/* t = m^(p-2) modulo p */
+	if (crypto_bignum_sub(prime, two, t2) < 0 ||
+	    crypto_bignum_exptmod(t1, t2, prime, t) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: t", t, prime_len);
+
+	/* b / (z * a) */
+	if (crypto_bignum_mulmod(z, a, prime, t1) < 0 ||
+	    crypto_bignum_inverse(t1, prime, t1) < 0 ||
+	    crypto_bignum_mulmod(b, t1, prime, x1a) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: x1a = b / (z * a)", x1a, prime_len);
+
+	/* (-b/a) * (1 + t) */
+	if (crypto_bignum_sub(prime, b, t1) < 0 ||
+	    crypto_bignum_inverse(a, prime, t2) < 0 ||
+	    crypto_bignum_mulmod(t1, t2, prime, t1) < 0 ||
+	    crypto_bignum_addmod(one, t, prime, t2) < 0 ||
+	    crypto_bignum_mulmod(t1, t2, prime, x1b) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len);
+
+	/* x1 = CSEL(CEQ(m, 0), x1a, x1b) */
+	if (crypto_bignum_to_bin(x1a, bin1, sizeof(bin1), prime_len) < 0 ||
+	    crypto_bignum_to_bin(x1b, bin2, sizeof(bin2), prime_len) < 0)
+		goto fail;
+	const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin);
+	x1 = crypto_bignum_init_set(bin, prime_len);
+	debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len);
+
+	/* gx1 = x1^3 + a * x1 + b */
+	if (crypto_bignum_exptmod(x1, three, prime, t1) < 0 ||
+	    crypto_bignum_mulmod(a, x1, prime, t2) < 0 ||
+	    crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
+	    crypto_bignum_addmod(t1, b, prime, gx1) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len);
+
+	/* x2 = z * u^2 * x1 */
+	if (crypto_bignum_mulmod(z, u2, prime, t1) < 0 ||
+	    crypto_bignum_mulmod(t1, x1, prime, x2) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: x2 = z * u^2 * x1", x2, prime_len);
+
+	/* gx2 = x2^3 + a * x2 + b */
+	if (crypto_bignum_exptmod(x2, three, prime, t1) < 0 ||
+	    crypto_bignum_mulmod(a, x2, prime, t2) < 0 ||
+	    crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
+	    crypto_bignum_addmod(t1, b, prime, gx2) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len);
+
+	/* l = gx1 is a quadratic residue modulo p
+	 * --> gx1^((p-1)/2) modulo p is zero or one */
+	if (crypto_bignum_sub(prime, one, t1) < 0 ||
+	    crypto_bignum_rshift(t1, 1, t1) < 0 ||
+	    crypto_bignum_exptmod(gx1, t1, prime, t1) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: gx1^((p-1)/2) modulo p", t1, prime_len);
+	is_qr = const_time_eq(crypto_bignum_is_zero(t1) |
+			      crypto_bignum_is_one(t1), 1);
+
+	/* v = CSEL(l, gx1, gx2) */
+	if (crypto_bignum_to_bin(gx1, bin1, sizeof(bin1), prime_len) < 0 ||
+	    crypto_bignum_to_bin(gx2, bin2, sizeof(bin2), prime_len) < 0)
+		goto fail;
+	const_time_select_bin(is_qr, bin1, bin2, prime_len, bin);
+	v = crypto_bignum_init_set(bin, prime_len);
+	debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len);
+
+	/* x = CSEL(l, x1, x2) */
+	if (crypto_bignum_to_bin(x1, bin1, sizeof(bin1), prime_len) < 0 ||
+	    crypto_bignum_to_bin(x2, bin2, sizeof(bin2), prime_len) < 0)
+		goto fail;
+	const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y);
+	wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len);
+
+	/* y = sqrt(v)
+	 * For prime p such that p = 3 mod 4 --> v^((p+1)/4) */
+	if (crypto_bignum_to_bin(prime, bin1, sizeof(bin1), prime_len) < 0)
+		goto fail;
+	if ((bin1[prime_len - 1] & 0x03) != 3) {
+		wpa_printf(MSG_DEBUG, "SSWU: prime does not have p = 3 mod 4");
+		goto fail;
+	}
+	y = crypto_bignum_init();
+	if (!y ||
+	    crypto_bignum_add(prime, one, t1) < 0 ||
+	    crypto_bignum_rshift(t1, 2, t1) < 0 ||
+	    crypto_bignum_exptmod(v, t1, prime, y) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len);
+
+	/* l = CEQ(LSB(u), LSB(y)) */
+	if (crypto_bignum_to_bin(u, bin1, sizeof(bin1), prime_len) < 0 ||
+	    crypto_bignum_to_bin(y, bin2, sizeof(bin2), prime_len) < 0)
+		goto fail;
+	is_eq = const_time_eq(bin1[prime_len - 1] & 0x01,
+			      bin2[prime_len - 1] & 0x01);
+
+	/* P = CSEL(l, (x,y), (x, p-y)) */
+	if (crypto_bignum_sub(prime, y, t1) < 0)
+		goto fail;
+	debug_print_bignum("SSWU: p - y", t1, prime_len);
+	if (crypto_bignum_to_bin(y, bin1, sizeof(bin1), prime_len) < 0 ||
+	    crypto_bignum_to_bin(t1, bin2, sizeof(bin2), prime_len) < 0)
+		goto fail;
+	const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]);
+
+	/* output P */
+	wpa_hexdump_key(MSG_DEBUG, "SSWU: P.x", x_y, prime_len);
+	wpa_hexdump_key(MSG_DEBUG, "SSWU: P.y", &x_y[prime_len], prime_len);
+	p = crypto_ec_point_from_bin(ec, x_y);
+
+fail:
+	crypto_bignum_deinit(u2, 1);
+	crypto_bignum_deinit(t1, 1);
+	crypto_bignum_deinit(t2, 1);
+	crypto_bignum_deinit(z, 0);
+	crypto_bignum_deinit(t, 1);
+	crypto_bignum_deinit(x1a, 1);
+	crypto_bignum_deinit(x1b, 1);
+	crypto_bignum_deinit(x1, 1);
+	crypto_bignum_deinit(x2, 1);
+	crypto_bignum_deinit(gx1, 1);
+	crypto_bignum_deinit(gx2, 1);
+	crypto_bignum_deinit(y, 1);
+	crypto_bignum_deinit(v, 1);
+	crypto_bignum_deinit(zero, 0);
+	crypto_bignum_deinit(one, 0);
+	crypto_bignum_deinit(two, 0);
+	crypto_bignum_deinit(three, 0);
+	forced_memzero(bin, sizeof(bin));
+	forced_memzero(bin1, sizeof(bin1));
+	forced_memzero(bin2, sizeof(bin2));
+	forced_memzero(x_y, sizeof(x_y));
+	return p;
+}
+
+
+static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len,
+			const u8 *password, size_t password_len,
+			const char *identifier, u8 *pwd_seed)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	size_t num_elem;
+
+	/* pwd-seed = HKDF-Extract(ssid, password [ || identifier ]) */
+	addr[0] = password;
+	len[0] = password_len;
+	num_elem = 1;
+	wpa_hexdump_ascii(MSG_DEBUG, "SAE: SSID", ssid, ssid_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+	if (identifier) {
+		wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
+			   identifier);
+		addr[num_elem] = (const u8 *) identifier;
+		len[num_elem] = os_strlen(identifier);
+		num_elem++;
+	}
+	if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len,
+			 pwd_seed) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, hash_len);
+	return 0;
+}
+
+
+size_t sae_ecc_prime_len_2_hash_len(size_t prime_len)
+{
+	if (prime_len <= 256 / 8)
+		return 32;
+	if (prime_len <= 384 / 8)
+		return 48;
+	return 64;
+}
+
+
+struct crypto_ec_point *
+sae_derive_pt_ecc(struct crypto_ec *ec, int group,
+		  const u8 *ssid, size_t ssid_len,
+		  const u8 *password, size_t password_len,
+		  const char *identifier)
+{
+	u8 pwd_seed[64];
+	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2];
+	size_t pwd_value_len, hash_len, prime_len;
+	const struct crypto_bignum *prime;
+	struct crypto_bignum *bn = NULL;
+	struct crypto_ec_point *p1 = NULL, *p2 = NULL, *pt = NULL;
+
+	prime = crypto_ec_get_prime(ec);
+	prime_len = crypto_ec_prime_len(ec);
+	if (prime_len > SAE_MAX_ECC_PRIME_LEN)
+		goto fail;
+	hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
+
+	/* len = olen(p) + ceil(olen(p)/2) */
+	pwd_value_len = prime_len + (prime_len + 1) / 2;
+
+	if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
+			 identifier, pwd_seed) < 0)
+		goto fail;
+
+	/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len)
+	 */
+	if (hkdf_expand(hash_len, pwd_seed, hash_len,
+			"SAE Hash to Element u1 P1", pwd_value, pwd_value_len) <
+	    0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u1 P1)",
+			pwd_value, pwd_value_len);
+
+	/* u1 = pwd-value modulo p */
+	bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
+	if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 ||
+	    crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
+				 prime_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: u1", pwd_value, prime_len);
+
+	/* P1 = SSWU(u1) */
+	p1 = sswu(ec, group, bn);
+	if (!p1)
+		goto fail;
+
+	/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u2 P2", len)
+	 */
+	if (hkdf_expand(hash_len, pwd_seed, hash_len,
+			"SAE Hash to Element u2 P2", pwd_value,
+			pwd_value_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u2 P2)",
+			pwd_value, pwd_value_len);
+
+	/* u2 = pwd-value modulo p */
+	crypto_bignum_deinit(bn, 1);
+	bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
+	if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 ||
+	    crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
+				 prime_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: u2", pwd_value, prime_len);
+
+	/* P2 = SSWU(u2) */
+	p2 = sswu(ec, group, bn);
+	if (!p2)
+		goto fail;
+
+	/* PT = elem-op(P1, P2) */
+	pt = crypto_ec_point_init(ec);
+	if (!pt)
+		goto fail;
+	if (crypto_ec_point_add(ec, p1, p2, pt) < 0) {
+		crypto_ec_point_deinit(pt, 1);
+		pt = NULL;
+	}
+
+fail:
+	forced_memzero(pwd_seed, sizeof(pwd_seed));
+	forced_memzero(pwd_value, sizeof(pwd_value));
+	crypto_bignum_deinit(bn, 1);
+	crypto_ec_point_deinit(p1, 1);
+	crypto_ec_point_deinit(p2, 1);
+	return pt;
+}
+
+
+size_t sae_ffc_prime_len_2_hash_len(size_t prime_len)
+{
+	if (prime_len <= 2048 / 8)
+		return 32;
+	if (prime_len <= 3072 / 8)
+		return 48;
+	return 64;
+}
+
+
+static struct crypto_bignum *
+sae_derive_pt_ffc(const struct dh_group *dh, int group,
+		  const u8 *ssid, size_t ssid_len,
+		  const u8 *password, size_t password_len,
+		  const char *identifier)
+{
+	size_t hash_len, prime_len, pwd_value_len;
+	struct crypto_bignum *prime, *order;
+	struct crypto_bignum *one = NULL, *two = NULL, *bn = NULL, *tmp = NULL,
+		*pt = NULL;
+	u8 pwd_seed[64];
+	u8 pwd_value[SAE_MAX_PRIME_LEN + SAE_MAX_PRIME_LEN / 2];
+
+	prime = crypto_bignum_init_set(dh->prime, dh->prime_len);
+	order = crypto_bignum_init_set(dh->order, dh->order_len);
+	if (!prime || !order)
+		goto fail;
+	prime_len = dh->prime_len;
+	if (prime_len > SAE_MAX_PRIME_LEN)
+		goto fail;
+	hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
+
+	/* len = olen(p) + ceil(olen(p)/2) */
+	pwd_value_len = prime_len + (prime_len + 1) / 2;
+	if (pwd_value_len > sizeof(pwd_value))
+		goto fail;
+
+	if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
+			 identifier, pwd_seed) < 0)
+		goto fail;
+
+	/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */
+	if (hkdf_expand(hash_len, pwd_seed, hash_len,
+			"SAE Hash to Element", pwd_value, pwd_value_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+			pwd_value, pwd_value_len);
+
+	/* pwd-value = (pwd-value modulo (p-2)) + 2 */
+	bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
+	one = crypto_bignum_init_uint(1);
+	two = crypto_bignum_init_uint(2);
+	tmp = crypto_bignum_init();
+	if (!bn || !one || !two || !tmp ||
+	    crypto_bignum_sub(prime, two, tmp) < 0 ||
+	    crypto_bignum_mod(bn, tmp, bn) < 0 ||
+	    crypto_bignum_add(bn, two, bn) < 0 ||
+	    crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
+				 prime_len) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value(reduced)",
+			pwd_value, prime_len);
+
+	/* PT = pwd-value^((p-1)/q) modulo p */
+	pt = crypto_bignum_init();
+	if (!pt ||
+	    crypto_bignum_sub(prime, one, tmp) < 0 ||
+	    crypto_bignum_div(tmp, order, tmp) < 0 ||
+	    crypto_bignum_exptmod(bn, tmp, prime, pt) < 0) {
+		crypto_bignum_deinit(pt, 1);
+		pt = NULL;
+		goto fail;
+	}
+	debug_print_bignum("SAE: PT", pt, prime_len);
+
+fail:
+	forced_memzero(pwd_seed, sizeof(pwd_seed));
+	forced_memzero(pwd_value, sizeof(pwd_value));
+	crypto_bignum_deinit(bn, 1);
+	crypto_bignum_deinit(tmp, 1);
+	crypto_bignum_deinit(one, 0);
+	crypto_bignum_deinit(two, 0);
+	crypto_bignum_deinit(prime, 0);
+	crypto_bignum_deinit(order, 0);
+	return pt;
+}
+
+
+static struct sae_pt *
+sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
+		    const u8 *password, size_t password_len,
+		    const char *identifier)
+{
+	struct sae_pt *pt;
+
+	wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group);
+
+	pt = os_zalloc(sizeof(*pt));
+	if (!pt)
+		return NULL;
+
+	pt->group = group;
+	pt->ec = crypto_ec_init(group);
+	if (pt->ec) {
+		pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len,
+					       password, password_len,
+					       identifier);
+		if (!pt->ecc_pt) {
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
+			goto fail;
+		}
+
+		return pt;
+	}
+
+	pt->dh = dh_groups_get(group);
+	if (!pt->dh) {
+		wpa_printf(MSG_DEBUG, "SAE: Unsupported group %d", group);
+		goto fail;
+	}
+
+	pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len,
+				       password, password_len, identifier);
+	if (!pt->ffc_pt) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
+		goto fail;
+	}
+
+	return pt;
+fail:
+	sae_deinit_pt(pt);
+	return NULL;
+}
+
+
+struct sae_pt * sae_derive_pt(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 };
+	int i;
+
+	if (!groups)
+		groups = default_groups;
+	for (i = 0; groups[i] > 0; i++) {
+		tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password,
+					  password_len, identifier);
+		if (!tmp)
+			continue;
+
+		if (last)
+			last->next = tmp;
+		else
+			pt = tmp;
+		last = tmp;
+	}
+
+	return pt;
+}
+
+
+static void sae_max_min_addr(const u8 *addr[], size_t len[],
+			     const u8 *addr1, const u8 *addr2)
+{
+	len[0] = ETH_ALEN;
+	len[1] = ETH_ALEN;
+	if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
+		addr[0] = addr1;
+		addr[1] = addr2;
+	} else {
+		addr[0] = addr2;
+		addr[1] = addr1;
+	}
+}
+
+
+struct crypto_ec_point *
+sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt,
+			   const u8 *addr1, const u8 *addr2)
+{
+	u8 bin[SAE_MAX_ECC_PRIME_LEN * 2];
+	size_t prime_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 salt[64], hash[64];
+	size_t hash_len;
+	const struct crypto_bignum *order;
+	struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL;
+	struct crypto_ec_point *pwe = NULL;
+
+	wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT");
+	prime_len = crypto_ec_prime_len(pt->ec);
+	if (crypto_ec_point_to_bin(pt->ec, pt->ecc_pt,
+				   bin, bin + prime_len) < 0)
+		return NULL;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PT.x", bin, prime_len);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PT.y", bin + prime_len, prime_len);
+
+	sae_max_min_addr(addr, len, addr1, addr2);
+
+	/* val = H(0^n,
+	 *         MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */
+	wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))");
+	hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
+	os_memset(salt, 0, hash_len);
+	if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len);
+
+	/* val = val modulo (q - 1) + 1 */
+	order = crypto_ec_get_order(pt->ec);
+	tmp = crypto_bignum_init();
+	val = crypto_bignum_init_set(hash, hash_len);
+	one = crypto_bignum_init_uint(1);
+	if (!tmp || !val || !one ||
+	    crypto_bignum_sub(order, one, tmp) < 0 ||
+	    crypto_bignum_mod(val, tmp, val) < 0 ||
+	    crypto_bignum_add(val, one, val) < 0)
+		goto fail;
+	debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len);
+
+	/* PWE = scalar-op(val, PT) */
+	pwe = crypto_ec_point_init(pt->ec);
+	if (!pwe ||
+	    crypto_ec_point_mul(pt->ec, pt->ecc_pt, val, pwe) < 0 ||
+	    crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) < 0) {
+		crypto_ec_point_deinit(pwe, 1);
+		pwe = NULL;
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.x", bin, prime_len);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.y", bin + prime_len, prime_len);
+
+fail:
+	crypto_bignum_deinit(tmp, 1);
+	crypto_bignum_deinit(val, 1);
+	crypto_bignum_deinit(one, 0);
+	return pwe;
+}
+
+
+struct crypto_bignum *
+sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt,
+			   const u8 *addr1, const u8 *addr2)
+{
+	size_t prime_len;
+	const u8 *addr[2];
+	size_t len[2];
+	u8 salt[64], hash[64];
+	size_t hash_len;
+	struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL;
+	struct crypto_bignum *pwe = NULL, *order = NULL, *prime = NULL;
+
+	wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT");
+	prime = crypto_bignum_init_set(pt->dh->prime, pt->dh->prime_len);
+	order = crypto_bignum_init_set(pt->dh->order, pt->dh->order_len);
+	if (!prime || !order)
+		goto fail;
+	prime_len = pt->dh->prime_len;
+
+	sae_max_min_addr(addr, len, addr1, addr2);
+
+	/* val = H(0^n,
+	 *         MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */
+	wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))");
+	hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
+	os_memset(salt, 0, hash_len);
+	if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len);
+
+	/* val = val modulo (q - 1) + 1 */
+	tmp = crypto_bignum_init();
+	val = crypto_bignum_init_set(hash, hash_len);
+	one = crypto_bignum_init_uint(1);
+	if (!tmp || !val || !one ||
+	    crypto_bignum_sub(order, one, tmp) < 0 ||
+	    crypto_bignum_mod(val, tmp, val) < 0 ||
+	    crypto_bignum_add(val, one, val) < 0)
+		goto fail;
+	debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len);
+
+	/* PWE = scalar-op(val, PT) */
+	pwe = crypto_bignum_init();
+	if (!pwe || crypto_bignum_exptmod(pt->ffc_pt, val, prime, pwe) < 0) {
+		crypto_bignum_deinit(pwe, 1);
+		pwe = NULL;
+		goto fail;
+	}
+	debug_print_bignum("SAE: PWE", pwe, prime_len);
+
+fail:
+	crypto_bignum_deinit(tmp, 1);
+	crypto_bignum_deinit(val, 1);
+	crypto_bignum_deinit(one, 0);
+	crypto_bignum_deinit(prime, 0);
+	crypto_bignum_deinit(order, 0);
+	return pwe;
+}
+
+
+void sae_deinit_pt(struct sae_pt *pt)
+{
+	struct sae_pt *prev;
+
+	while (pt) {
+		crypto_ec_point_deinit(pt->ecc_pt, 1);
+		crypto_bignum_deinit(pt->ffc_pt, 1);
+		crypto_ec_deinit(pt->ec);
+		prev = pt;
+		pt = pt->next;
+		os_free(prev);
+	}
+}
+
+
 static int sae_derive_commit_element_ecc(struct sae_data *sae,
 					 struct crypto_bignum *mask)
 {
@@ -768,48 +1320,23 @@
 static int sae_derive_commit(struct sae_data *sae)
 {
 	struct crypto_bignum *mask;
-	int ret = -1;
-	unsigned int counter = 0;
+	int ret;
 
-	do {
-		counter++;
-		if (counter > 100) {
-			/*
-			 * This cannot really happen in practice if the random
-			 * number generator is working. Anyway, to avoid even a
-			 * theoretical infinite loop, break out after 100
-			 * attemps.
-			 */
-			return -1;
-		}
-
-		mask = sae_get_rand_and_mask(sae);
-		if (mask == NULL) {
-			wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
-			return -1;
-		}
-
-		/* commit-scalar = (rand + mask) modulo r */
-		if (!sae->tmp->own_commit_scalar) {
-			sae->tmp->own_commit_scalar = crypto_bignum_init();
-			if (!sae->tmp->own_commit_scalar)
-				goto fail;
-		}
-		crypto_bignum_add(sae->tmp->sae_rand, mask,
-				  sae->tmp->own_commit_scalar);
-		crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
-				  sae->tmp->own_commit_scalar);
-	} while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) ||
-		 crypto_bignum_is_one(sae->tmp->own_commit_scalar));
-
-	if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) ||
-	    (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0))
-		goto fail;
-
-	ret = 0;
-fail:
+	mask = crypto_bignum_init();
+	if (!sae->tmp->sae_rand)
+		sae->tmp->sae_rand = crypto_bignum_init();
+	if (!sae->tmp->own_commit_scalar)
+		sae->tmp->own_commit_scalar = crypto_bignum_init();
+	ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar ||
+		dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand,
+					  mask,
+					  sae->tmp->own_commit_scalar) < 0 ||
+		(sae->tmp->ec &&
+		 sae_derive_commit_element_ecc(sae, mask) < 0) ||
+		(sae->tmp->dh &&
+		 sae_derive_commit_element_ffc(sae, mask) < 0);
 	crypto_bignum_deinit(mask, 1);
-	return ret;
+	return ret ? -1 : 0;
 }
 
 
@@ -823,10 +1350,66 @@
 						identifier) < 0) ||
 	    (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
 						password_len,
-						identifier) < 0) ||
-	    sae_derive_commit(sae) < 0)
+						identifier) < 0))
 		return -1;
-	return 0;
+
+	sae->tmp->h2e = 0;
+	return sae_derive_commit(sae);
+}
+
+
+int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
+			  const u8 *addr1, const u8 *addr2,
+			  int *rejected_groups)
+{
+	if (!sae->tmp)
+		return -1;
+
+	while (pt) {
+		if (pt->group == sae->group)
+			break;
+		pt = pt->next;
+	}
+	if (!pt) {
+		wpa_printf(MSG_INFO, "SAE: Could not find PT for group %u",
+			   sae->group);
+		return -1;
+	}
+
+	sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0;
+	wpabuf_free(sae->tmp->own_rejected_groups);
+	sae->tmp->own_rejected_groups = NULL;
+	if (rejected_groups) {
+		int count, i;
+		struct wpabuf *groups;
+
+		count = int_array_len(rejected_groups);
+		groups = wpabuf_alloc(count * 2);
+		if (!groups)
+			return -1;
+		for (i = 0; i < count; i++)
+			wpabuf_put_le16(groups, rejected_groups[i]);
+		sae->tmp->own_rejected_groups = groups;
+	}
+
+	if (pt->ec) {
+		crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1);
+		sae->tmp->pwe_ecc = sae_derive_pwe_from_pt_ecc(pt, addr1,
+							       addr2);
+		if (!sae->tmp->pwe_ecc)
+			return -1;
+	}
+
+	if (pt->dh) {
+		crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
+		sae->tmp->pwe_ffc = sae_derive_pwe_from_pt_ffc(pt, addr1,
+							       addr2);
+		if (!sae->tmp->pwe_ffc)
+			return -1;
+	}
+
+	sae->tmp->h2e = 1;
+	return sae_derive_commit(sae);
 }
 
 
@@ -904,47 +1487,124 @@
 }
 
 
+static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label,
+			const u8 *context, size_t context_len,
+			u8 *out, size_t out_len)
+{
+	if (hash_len == 32)
+		return sha256_prf(k, hash_len, label,
+				  context, context_len, out, out_len);
+#ifdef CONFIG_SHA384
+	if (hash_len == 48)
+		return sha384_prf(k, hash_len, label,
+				  context, context_len, out, out_len);
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+	if (hash_len == 64)
+		return sha512_prf(k, hash_len, label,
+				  context, context_len, out, out_len);
+#endif /* CONFIG_SHA512 */
+	return -1;
+}
+
+
 static int sae_derive_keys(struct sae_data *sae, const u8 *k)
 {
-	u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN];
-	u8 keyseed[SHA256_MAC_LEN];
-	u8 keys[SAE_KCK_LEN + SAE_PMK_LEN];
+	u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN];
+	const u8 *salt;
+	struct wpabuf *rejected_groups = NULL;
+	u8 keyseed[SAE_MAX_HASH_LEN];
+	u8 keys[SAE_MAX_HASH_LEN + SAE_PMK_LEN];
 	struct crypto_bignum *tmp;
 	int ret = -1;
+	size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
+	const u8 *addr[1];
+	size_t len[1];
 
 	tmp = crypto_bignum_init();
 	if (tmp == NULL)
 		goto fail;
 
-	/* keyseed = H(<0>32, k)
-	 * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
+	/* keyseed = H(salt, k)
+	 * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK",
 	 *                      (commit-scalar + peer-commit-scalar) modulo r)
 	 * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
 	 */
+	if (!sae->tmp->h2e)
+		hash_len = SHA256_MAC_LEN;
+	else if (sae->tmp->dh)
+		hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
+	else
+		hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
+	if (sae->tmp->h2e && (sae->tmp->own_rejected_groups ||
+			      sae->tmp->peer_rejected_groups)) {
+		struct wpabuf *own, *peer;
 
-	os_memset(null_key, 0, sizeof(null_key));
-	hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len,
-		    keyseed);
-	wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
-
-	crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar,
-			  tmp);
-	crypto_bignum_mod(tmp, sae->tmp->order, tmp);
-	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
-	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
-	if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
-		       val, sae->tmp->prime_len, keys, sizeof(keys)) < 0)
+		own = sae->tmp->own_rejected_groups;
+		peer = sae->tmp->peer_rejected_groups;
+		salt_len = 0;
+		if (own)
+			salt_len += wpabuf_len(own);
+		if (peer)
+			salt_len += wpabuf_len(peer);
+		rejected_groups = wpabuf_alloc(salt_len);
+		if (!rejected_groups)
+			goto fail;
+		if (sae->tmp->own_addr_higher) {
+			if (own)
+				wpabuf_put_buf(rejected_groups, own);
+			if (peer)
+				wpabuf_put_buf(rejected_groups, peer);
+		} else {
+			if (peer)
+				wpabuf_put_buf(rejected_groups, peer);
+			if (own)
+				wpabuf_put_buf(rejected_groups, own);
+		}
+		salt = wpabuf_head(rejected_groups);
+		salt_len = wpabuf_len(rejected_groups);
+	} else {
+		os_memset(zero, 0, hash_len);
+		salt = zero;
+		salt_len = hash_len;
+	}
+	wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation",
+		    salt, salt_len);
+	addr[0] = k;
+	len[0] = prime_len;
+	if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0)
 		goto fail;
-	os_memset(keyseed, 0, sizeof(keyseed));
-	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
-	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len);
+
+	if (crypto_bignum_add(sae->tmp->own_commit_scalar,
+			      sae->peer_commit_scalar, tmp) < 0 ||
+	    crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0)
+		goto fail;
+	/* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit
+	 * string that is needed for KCK, PMK, and PMKID derivation, but it
+	 * seems to make most sense to encode the
+	 * (commit-scalar + peer-commit-scalar) mod r part as a bit string by
+	 * zero padding it from left to the length of the order (in full
+	 * octets). */
+	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
+	if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
+			 val, sae->tmp->order_len,
+			 keys, hash_len + SAE_PMK_LEN) < 0)
+		goto fail;
+	forced_memzero(keyseed, sizeof(keyseed));
+	os_memcpy(sae->tmp->kck, keys, hash_len);
+	sae->tmp->kck_len = hash_len;
+	os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN);
 	os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
-	os_memset(keys, 0, sizeof(keys));
-	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
+	forced_memzero(keys, sizeof(keys));
+	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
+			sae->tmp->kck, sae->tmp->kck_len);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
 
 	ret = 0;
 fail:
+	wpabuf_free(rejected_groups);
 	crypto_bignum_deinit(tmp, 0);
 	return ret;
 }
@@ -1007,6 +1667,16 @@
 		wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
 			   identifier);
 	}
+
+	if (sae->tmp->h2e && sae->tmp->own_rejected_groups) {
+		wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups",
+				sae->tmp->own_rejected_groups);
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+		wpabuf_put_u8(buf,
+			      1 + wpabuf_len(sae->tmp->own_rejected_groups));
+		wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS);
+		wpabuf_put_buf(buf, sae->tmp->own_rejected_groups);
+	}
 }
 
 
@@ -1062,9 +1732,19 @@
 }
 
 
+static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end)
+{
+	return end - pos >= 3 &&
+		pos[0] == WLAN_EID_EXTENSION &&
+		pos[1] >= 2 &&
+		end - pos - 2 >= pos[1] &&
+		pos[2] == WLAN_EID_EXT_REJECTED_GROUPS;
+}
+
+
 static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
 				   const u8 *end, const u8 **token,
-				   size_t *token_len)
+				   size_t *token_len, int h2e)
 {
 	size_t scalar_elem_len, tlen;
 	const u8 *elem;
@@ -1085,6 +1765,8 @@
 	 * fields, so use that length as a requirement for the received
 	 * token and check for the presence of possible Password
 	 * Identifier element based on the element header information.
+	 * When parsing H2E case, also consider the Rejected Groupd element
+	 * similarly.
 	 */
 	tlen = end - (*pos + scalar_elem_len);
 
@@ -1102,12 +1784,27 @@
 		  * this frame. */
 		return;
 	}
+	if (h2e && sae_is_rejected_groups_elem(elem, end)) {
+		 /* Rejected Groups takes out all available extra octets, so
+		  * there can be no Anti-Clogging token in this frame. */
+		return;
+	}
 
 	elem += SHA256_MAC_LEN;
 	if (sae_is_password_id_elem(elem, end)) {
 		 /* Password Identifier element is included in the end, so
 		  * remove its length from the Anti-Clogging token field. */
 		tlen -= 2 + elem[1];
+		elem += 2 + elem[1];
+		if (h2e && sae_is_rejected_groups_elem(elem, end)) {
+			/* Also remove Rejected Groups element from the
+			 * Anti-Clogging token field length */
+			tlen -= 2 + elem[1];
+		}
+	} else if (h2e && sae_is_rejected_groups_elem(elem, end)) {
+		 /* Rejected Groups element is included in the end, so
+		  * remove its length from the Anti-Clogging token field. */
+		tlen -= 2 + elem[1];
 	}
 
 	wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
@@ -1274,11 +1971,11 @@
 
 
 static int sae_parse_password_identifier(struct sae_data *sae,
-					 const u8 *pos, const u8 *end)
+					 const u8 **pos, const u8 *end)
 {
 	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    pos, end - pos);
-	if (!sae_is_password_id_elem(pos, end)) {
+		    *pos, end - *pos);
+	if (!sae_is_password_id_elem(*pos, end)) {
 		if (sae->tmp->pw_id) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: No Password Identifier included, but expected one (%s)",
@@ -1291,8 +1988,8 @@
 	}
 
 	if (sae->tmp->pw_id &&
-	    (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) ||
-	     os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) {
+	    ((*pos)[1] - 1 != (int) os_strlen(sae->tmp->pw_id) ||
+	     os_memcmp(sae->tmp->pw_id, (*pos) + 3, (*pos)[1] - 1) != 0)) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: The included Password Identifier does not match the expected one (%s)",
 			   sae->tmp->pw_id);
@@ -1300,19 +1997,39 @@
 	}
 
 	os_free(sae->tmp->pw_id);
-	sae->tmp->pw_id = os_malloc(pos[1]);
+	sae->tmp->pw_id = os_malloc((*pos)[1]);
 	if (!sae->tmp->pw_id)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1);
-	sae->tmp->pw_id[pos[1] - 1] = '\0';
+	os_memcpy(sae->tmp->pw_id, (*pos) + 3, (*pos)[1] - 1);
+	sae->tmp->pw_id[(*pos)[1] - 1] = '\0';
 	wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
-			  sae->tmp->pw_id, pos[1] -  1);
+			  sae->tmp->pw_id, (*pos)[1] -  1);
+	*pos = *pos + 2 + (*pos)[1];
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static int sae_parse_rejected_groups(struct sae_data *sae,
+				     const u8 *pos, const u8 *end)
+{
+	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
+		    pos, end - pos);
+	if (!sae_is_rejected_groups_elem(pos, end))
+		return WLAN_STATUS_SUCCESS;
+	wpabuf_free(sae->tmp->peer_rejected_groups);
+	sae->tmp->peer_rejected_groups = wpabuf_alloc(pos[1] - 1);
+	if (!sae->tmp->peer_rejected_groups)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	wpabuf_put_data(sae->tmp->peer_rejected_groups, pos + 3, pos[1] - 1);
+	wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list",
+			sae->tmp->peer_rejected_groups);
 	return WLAN_STATUS_SUCCESS;
 }
 
 
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
-		     const u8 **token, size_t *token_len, int *allowed_groups)
+		     const u8 **token, size_t *token_len, int *allowed_groups,
+		     int h2e)
 {
 	const u8 *pos = data, *end = data + len;
 	u16 res;
@@ -1326,7 +2043,7 @@
 	pos += 2;
 
 	/* Optional Anti-Clogging Token */
-	sae_parse_commit_token(sae, &pos, end, token, token_len);
+	sae_parse_commit_token(sae, &pos, end, token, token_len, h2e);
 
 	/* commit-scalar */
 	res = sae_parse_commit_scalar(sae, &pos, end);
@@ -1339,10 +2056,17 @@
 		return res;
 
 	/* Optional Password Identifier element */
-	res = sae_parse_password_identifier(sae, pos, end);
+	res = sae_parse_password_identifier(sae, &pos, end);
 	if (res != WLAN_STATUS_SUCCESS)
 		return res;
 
+	/* Conditional Rejected Groups element */
+	if (h2e) {
+		res = sae_parse_rejected_groups(sae, pos, end);
+		if (res != WLAN_STATUS_SUCCESS)
+			return res;
+	}
+
 	/*
 	 * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
 	 * the values we sent which would be evidence of a reflection attack.
@@ -1370,12 +2094,12 @@
 }
 
 
-static void sae_cn_confirm(struct sae_data *sae, const u8 *sc,
-			   const struct crypto_bignum *scalar1,
-			   const u8 *element1, size_t element1_len,
-			   const struct crypto_bignum *scalar2,
-			   const u8 *element2, size_t element2_len,
-			   u8 *confirm)
+static int sae_cn_confirm(struct sae_data *sae, const u8 *sc,
+			  const struct crypto_bignum *scalar1,
+			  const u8 *element1, size_t element1_len,
+			  const struct crypto_bignum *scalar2,
+			  const u8 *element2, size_t element2_len,
+			  u8 *confirm)
 {
 	const u8 *addr[5];
 	size_t len[5];
@@ -1389,72 +2113,81 @@
 	 * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
 	 *               PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
 	 */
+	if (crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
+				 sae->tmp->prime_len) < 0 ||
+	    crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
+				 sae->tmp->prime_len) < 0)
+		return -1;
 	addr[0] = sc;
 	len[0] = 2;
-	crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
-			     sae->tmp->prime_len);
 	addr[1] = scalar_b1;
 	len[1] = sae->tmp->prime_len;
 	addr[2] = element1;
 	len[2] = element1_len;
-	crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
-			     sae->tmp->prime_len);
 	addr[3] = scalar_b2;
 	len[3] = sae->tmp->prime_len;
 	addr[4] = element2;
 	len[4] = element2_len;
-	hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len,
-			   confirm);
+	return hkdf_extract(sae->tmp->kck_len, sae->tmp->kck, sae->tmp->kck_len,
+			    5, addr, len, confirm);
 }
 
 
-static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
-			       const struct crypto_bignum *scalar1,
-			       const struct crypto_ec_point *element1,
-			       const struct crypto_bignum *scalar2,
-			       const struct crypto_ec_point *element2,
-			       u8 *confirm)
+static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
+			      const struct crypto_bignum *scalar1,
+			      const struct crypto_ec_point *element1,
+			      const struct crypto_bignum *scalar2,
+			      const struct crypto_ec_point *element2,
+			      u8 *confirm)
 {
 	u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
 	u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
 
-	crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
-			       element_b1 + sae->tmp->prime_len);
-	crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
-			       element_b2 + sae->tmp->prime_len);
-
-	sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len,
-		       scalar2, element_b2, 2 * sae->tmp->prime_len, confirm);
+	if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
+				   element_b1 + sae->tmp->prime_len) < 0 ||
+	    crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
+				   element_b2 + sae->tmp->prime_len) < 0 ||
+	    sae_cn_confirm(sae, sc, scalar1, element_b1,
+			   2 * sae->tmp->prime_len,
+			   scalar2, element_b2, 2 * sae->tmp->prime_len,
+			   confirm) < 0)
+		return -1;
+	return 0;
 }
 
 
-static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
-			       const struct crypto_bignum *scalar1,
-			       const struct crypto_bignum *element1,
-			       const struct crypto_bignum *scalar2,
-			       const struct crypto_bignum *element2,
-			       u8 *confirm)
+static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
+			      const struct crypto_bignum *scalar1,
+			      const struct crypto_bignum *element1,
+			      const struct crypto_bignum *scalar2,
+			      const struct crypto_bignum *element2,
+			      u8 *confirm)
 {
 	u8 element_b1[SAE_MAX_PRIME_LEN];
 	u8 element_b2[SAE_MAX_PRIME_LEN];
 
-	crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
-			     sae->tmp->prime_len);
-	crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
-			     sae->tmp->prime_len);
-
-	sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
-		       scalar2, element_b2, sae->tmp->prime_len, confirm);
+	if (crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
+				 sae->tmp->prime_len) < 0 ||
+	    crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
+				 sae->tmp->prime_len) < 0 ||
+	    sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
+			   scalar2, element_b2, sae->tmp->prime_len,
+			   confirm) < 0)
+		return -1;
+	return 0;
 }
 
 
 void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
 {
 	const u8 *sc;
+	size_t hash_len;
 
 	if (sae->tmp == NULL)
 		return;
 
+	hash_len = sae->tmp->kck_len;
+
 	/* Send-Confirm */
 	sc = wpabuf_put(buf, 0);
 	wpabuf_put_le16(buf, sae->send_confirm);
@@ -1466,59 +2199,63 @@
 				   sae->tmp->own_commit_element_ecc,
 				   sae->peer_commit_scalar,
 				   sae->tmp->peer_commit_element_ecc,
-				   wpabuf_put(buf, SHA256_MAC_LEN));
+				   wpabuf_put(buf, hash_len));
 	else
 		sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
 				   sae->tmp->own_commit_element_ffc,
 				   sae->peer_commit_scalar,
 				   sae->tmp->peer_commit_element_ffc,
-				   wpabuf_put(buf, SHA256_MAC_LEN));
+				   wpabuf_put(buf, hash_len));
 }
 
 
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
 {
-	u8 verifier[SHA256_MAC_LEN];
+	u8 verifier[SAE_MAX_HASH_LEN];
+	size_t hash_len;
 
-	if (len < 2 + SHA256_MAC_LEN) {
+	if (!sae->tmp)
+		return -1;
+
+	hash_len = sae->tmp->kck_len;
+	if (len < 2 + hash_len) {
 		wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
 		return -1;
 	}
 
 	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
 
-	if (!sae->tmp || !sae->peer_commit_scalar ||
-	    !sae->tmp->own_commit_scalar) {
+	if (!sae->peer_commit_scalar || !sae->tmp->own_commit_scalar) {
 		wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
 		return -1;
 	}
 
 	if (sae->tmp->ec) {
 		if (!sae->tmp->peer_commit_element_ecc ||
-		    !sae->tmp->own_commit_element_ecc)
+		    !sae->tmp->own_commit_element_ecc ||
+		    sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
+				       sae->tmp->peer_commit_element_ecc,
+				       sae->tmp->own_commit_scalar,
+				       sae->tmp->own_commit_element_ecc,
+				       verifier) < 0)
 			return -1;
-		sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
-				   sae->tmp->peer_commit_element_ecc,
-				   sae->tmp->own_commit_scalar,
-				   sae->tmp->own_commit_element_ecc,
-				   verifier);
 	} else {
 		if (!sae->tmp->peer_commit_element_ffc ||
-		    !sae->tmp->own_commit_element_ffc)
+		    !sae->tmp->own_commit_element_ffc ||
+		    sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
+				       sae->tmp->peer_commit_element_ffc,
+				       sae->tmp->own_commit_scalar,
+				       sae->tmp->own_commit_element_ffc,
+				       verifier) < 0)
 			return -1;
-		sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
-				   sae->tmp->peer_commit_element_ffc,
-				   sae->tmp->own_commit_scalar,
-				   sae->tmp->own_commit_element_ffc,
-				   verifier);
 	}
 
-	if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+	if (os_memcmp_const(verifier, data + 2, hash_len) != 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
 		wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
-			    data + 2, SHA256_MAC_LEN);
+			    data + 2, hash_len);
 		wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
-			    verifier, SHA256_MAC_LEN);
+			    verifier, hash_len);
 		return -1;
 	}
 
diff --git a/src/common/sae.h b/src/common/sae.h
index 3eb6e32..b3787e4 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -12,17 +12,18 @@
 #define SAE_KCK_LEN 32
 #define SAE_PMK_LEN 32
 #define SAE_PMKID_LEN 16
-#define SAE_KEYSEED_KEY_LEN 32
 #define SAE_MAX_PRIME_LEN 512
 #define SAE_MAX_ECC_PRIME_LEN 66
-#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
-#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
+#define SAE_MAX_HASH_LEN 64
+#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN + 255)
+#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN)
 
 /* Special value returned by sae_parse_commit() */
 #define SAE_SILENTLY_DISCARD 65535
 
 struct sae_temporary_data {
-	u8 kck[SAE_KCK_LEN];
+	u8 kck[SAE_MAX_HASH_LEN];
+	size_t kck_len;
 	struct crypto_bignum *own_commit_scalar;
 	struct crypto_bignum *own_commit_element_ffc;
 	struct crypto_ec_point *own_commit_element_ecc;
@@ -33,6 +34,7 @@
 	struct crypto_bignum *sae_rand;
 	struct crypto_ec *ec;
 	int prime_len;
+	int order_len;
 	const struct dh_group *dh;
 	const struct crypto_bignum *prime;
 	const struct crypto_bignum *order;
@@ -42,6 +44,20 @@
 	char *pw_id;
 	int vlan_id;
 	u8 bssid[ETH_ALEN];
+	struct wpabuf *own_rejected_groups;
+	struct wpabuf *peer_rejected_groups;
+	unsigned int h2e:1;
+	unsigned int own_addr_higher:1;
+};
+
+struct sae_pt {
+	struct sae_pt *next;
+	int group;
+	struct crypto_ec *ec;
+	struct crypto_ec_point *ecc_pt;
+
+	const struct dh_group *dh;
+	struct crypto_bignum *ffc_pt;
 };
 
 enum sae_state {
@@ -67,14 +83,28 @@
 int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
 		       const u8 *password, size_t password_len,
 		       const char *identifier, struct sae_data *sae);
+int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
+			  const u8 *addr1, const u8 *addr2,
+			  int *rejected_groups);
 int sae_process_commit(struct sae_data *sae);
 void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
 		      const struct wpabuf *token, const char *identifier);
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
-		     const u8 **token, size_t *token_len, int *allowed_groups);
+		     const u8 **token, size_t *token_len, int *allowed_groups,
+		     int h2e);
 void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
 u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
 const char * sae_state_txt(enum sae_state state);
+struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
+			      const u8 *password, size_t password_len,
+			      const char *identifier);
+struct crypto_ec_point *
+sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt,
+			   const u8 *addr1, const u8 *addr2);
+struct crypto_bignum *
+sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt,
+			   const u8 *addr1, const u8 *addr2);
+void sae_deinit_pt(struct sae_pt *pt);
 
 #endif /* SAE_H */
diff --git a/src/common/version.h b/src/common/version.h
index eb4f313..0235c9b 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -9,6 +9,6 @@
 #define GIT_VERSION_STR_POSTFIX ""
 #endif /* GIT_VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.8-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.10-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index ed2d1c2..de4b6ec 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -212,11 +212,9 @@
 			return -1;
 		os_memcpy(mic, hash, MD5_MAC_LEN);
 		break;
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
 		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC");
 		return omac1_aes_128(key, buf, len, mic);
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
 		switch (akmp) {
 #ifdef CONFIG_SAE
@@ -410,14 +408,10 @@
 		return -1;
 #endif /* CONFIG_SUITEB192 || CONFIG_FILS */
 	} else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS)
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
 		if (sha256_prf(pmk, pmk_len, label, data, data_len,
 			       tmp, ptk_len) < 0)
 			return -1;
-#else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
-		return -1;
-#endif /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
 #ifdef CONFIG_DPP
 	} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
@@ -692,7 +686,7 @@
 	len[2] = ETH_ALEN;
 	addr[3] = bssid;
 	len[3] = ETH_ALEN;
-	if (g_sta && g_ap_len && g_ap && g_ap_len) {
+	if (g_sta && g_sta_len && g_ap && g_ap_len) {
 		addr[4] = g_sta;
 		len[4] = g_sta_len;
 		addr[5] = g_ap;
@@ -723,7 +717,7 @@
 	addr[1] = snonce;
 	addr[2] = bssid;
 	addr[3] = sta_addr;
-	if (g_sta && g_ap_len && g_ap && g_ap_len) {
+	if (g_sta && g_sta_len && g_ap && g_ap_len) {
 		addr[4] = g_ap;
 		len[4] = g_ap_len;
 		addr[5] = g_sta;
@@ -756,10 +750,12 @@
 	       const u8 *mdie, size_t mdie_len,
 	       const u8 *ftie, size_t ftie_len,
 	       const u8 *rsnie, size_t rsnie_len,
-	       const u8 *ric, size_t ric_len, u8 *mic)
+	       const u8 *ric, size_t ric_len,
+	       const u8 *rsnxe, size_t rsnxe_len,
+	       u8 *mic)
 {
-	const u8 *addr[9];
-	size_t len[9];
+	const u8 *addr[10];
+	size_t len[10];
 	size_t i, num_elem = 0;
 	u8 zero_mic[24];
 	size_t mic_len, fte_fixed_len;
@@ -826,6 +822,12 @@
 		num_elem++;
 	}
 
+	if (rsnxe) {
+		addr[num_elem] = rsnxe;
+		len[num_elem] = rsnxe_len;
+		num_elem++;
+	}
+
 	for (i = 0; i < num_elem; i++)
 		wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
 #ifdef CONFIG_SHA384
@@ -892,12 +894,10 @@
 			parse->r0kh_id = pos;
 			parse->r0kh_id_len = len;
 			break;
-#ifdef CONFIG_IEEE80211W
 		case FTIE_SUBELEM_IGTK:
 			parse->igtk = pos;
 			parse->igtk_len = len;
 			break;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 		case FTIE_SUBELEM_OCI:
 			parse->oci = pos;
@@ -958,6 +958,7 @@
 					   "RSN IE: %d", ret);
 				return -1;
 			}
+			parse->rsn_capab = data.capabilities;
 			if (data.num_pmkid == 1 && data.pmkid)
 				parse->rsn_pmkid = data.pmkid;
 			parse->key_mgmt = data.key_mgmt;
@@ -968,6 +969,13 @@
 				update_use_sha384 = 0;
 			}
 			break;
+		case WLAN_EID_RSNX:
+			wpa_hexdump(MSG_DEBUG, "FT: RSNXE", pos, len);
+			if (len < 1)
+				break;
+			parse->rsnxe = pos;
+			parse->rsnxe_len = len;
+			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
 			wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len);
 			if (len < sizeof(struct rsn_mdie))
@@ -989,9 +997,11 @@
 				wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
 					    ftie_sha384->mic,
 					    sizeof(ftie_sha384->mic));
+				parse->fte_anonce = ftie_sha384->anonce;
 				wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
 					    ftie_sha384->anonce,
 					    WPA_NONCE_LEN);
+				parse->fte_snonce = ftie_sha384->snonce;
 				wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
 					    ftie_sha384->snonce,
 					    WPA_NONCE_LEN);
@@ -1008,8 +1018,10 @@
 				    ftie->mic_control, 2);
 			wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
 				    ftie->mic, sizeof(ftie->mic));
+			parse->fte_anonce = ftie->anonce;
 			wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
 				    ftie->anonce, WPA_NONCE_LEN);
+			parse->fte_snonce = ftie->snonce;
 			wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
 				    ftie->snonce, WPA_NONCE_LEN);
 			prot_ie_count = ftie->mic_control[1];
@@ -1046,6 +1058,8 @@
 		prot_ie_count--;
 	if (parse->ftie)
 		prot_ie_count--;
+	if (parse->rsnxe)
+		prot_ie_count--;
 	if (prot_ie_count < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
 			   "the protected IE count");
@@ -1087,10 +1101,8 @@
 		return WPA_CIPHER_TKIP;
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
 		return WPA_CIPHER_CCMP;
-#ifdef CONFIG_IEEE80211W
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
 		return WPA_CIPHER_AES_128_CMAC;
-#endif /* CONFIG_IEEE80211W */
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
 		return WPA_CIPHER_GCMP;
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256)
@@ -1125,12 +1137,10 @@
 		return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
 #endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
 		return WPA_KEY_MGMT_IEEE8021X_SHA256;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
 		return WPA_KEY_MGMT_PSK_SHA256;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_SAE
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
 		return WPA_KEY_MGMT_SAE;
@@ -1170,7 +1180,6 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
 int wpa_cipher_valid_mgmt_group(int cipher)
 {
 	return cipher == WPA_CIPHER_AES_128_CMAC ||
@@ -1178,7 +1187,6 @@
 		cipher == WPA_CIPHER_BIP_GMAC_256 ||
 		cipher == WPA_CIPHER_BIP_CMAC_256;
 }
-#endif /* CONFIG_IEEE80211W */
 
 
 /**
@@ -1203,11 +1211,7 @@
 	data->capabilities = 0;
 	data->pmkid = NULL;
 	data->num_pmkid = 0;
-#ifdef CONFIG_IEEE80211W
 	data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
-#else /* CONFIG_IEEE80211W */
-	data->mgmt_group_cipher = 0;
-#endif /* CONFIG_IEEE80211W */
 
 	if (rsn_ie_len == 0) {
 		/* No RSN IE - fail silently */
@@ -1282,13 +1286,11 @@
 			pos += RSN_SELECTOR_LEN;
 			left -= RSN_SELECTOR_LEN;
 		}
-#ifdef CONFIG_IEEE80211W
 		if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
 			wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
 				   "pairwise cipher", __func__);
 			return -1;
 		}
-#endif /* CONFIG_IEEE80211W */
 	} else if (left == 1) {
 		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
 			   __func__);
@@ -1340,7 +1342,6 @@
 		}
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (left >= 4) {
 		data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
 		if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
@@ -1353,7 +1354,6 @@
 		pos += RSN_SELECTOR_LEN;
 		left -= RSN_SELECTOR_LEN;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	if (left > 0) {
 		wpa_hexdump(MSG_DEBUG,
@@ -1852,11 +1852,9 @@
 		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384");
 		hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash);
 #endif /* CONFIG_FILS || CONFIG_SHA384 */
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	} else if (wpa_key_mgmt_sha256(akmp)) {
 		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256");
 		hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
-#endif /* CONFIG_IEEE80211W || CONFIG_FILS */
 	} else {
 		wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1");
 		hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
@@ -2007,12 +2005,10 @@
 	case WPA_KEY_MGMT_FT_PSK:
 		return "FT-PSK";
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	case WPA_KEY_MGMT_IEEE8021X_SHA256:
 		return "WPA2-EAP-SHA256";
 	case WPA_KEY_MGMT_PSK_SHA256:
 		return "WPA2-PSK-SHA256";
-#endif /* CONFIG_IEEE80211W */
 	case WPA_KEY_MGMT_WPS:
 		return "WPS";
 	case WPA_KEY_MGMT_SAE:
@@ -2075,6 +2071,16 @@
 		return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
 	if (akm & WPA_KEY_MGMT_FT_FILS_SHA384)
 		return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+	if (akm & WPA_KEY_MGMT_SAE)
+		return RSN_AUTH_KEY_MGMT_SAE;
+	if (akm & WPA_KEY_MGMT_FT_SAE)
+		return RSN_AUTH_KEY_MGMT_FT_SAE;
+	if (akm & WPA_KEY_MGMT_OWE)
+		return RSN_AUTH_KEY_MGMT_OWE;
+	if (akm & WPA_KEY_MGMT_DPP)
+		return RSN_AUTH_KEY_MGMT_DPP;
+	if (akm & WPA_KEY_MGMT_OSEN)
+		return RSN_AUTH_KEY_MGMT_OSEN;
 	return 0;
 }
 
@@ -2116,7 +2122,6 @@
 }
 
 
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
 {
 	u8 *start, *end, *rpos, *rend;
@@ -2131,11 +2136,10 @@
 		start += 2 + start[1];
 	}
 	if (start >= end) {
-		wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in "
-			   "IEs data");
+		wpa_printf(MSG_ERROR, "RSN: Could not find RSNE in IEs data");
 		return -1;
 	}
-	wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification",
+	wpa_hexdump(MSG_DEBUG, "RSN: RSNE before modification",
 		    start, 2 + start[1]);
 
 	/* Find start of PMKID-Count */
@@ -2161,8 +2165,8 @@
 		/* Skip RSN Capabilities */
 		rpos += 2;
 		if (rpos > rend) {
-			wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in "
-				   "IEs data");
+			wpa_printf(MSG_ERROR,
+				   "RSN: Could not parse RSNE in IEs data");
 			return -1;
 		}
 	}
@@ -2193,10 +2197,10 @@
 			 * PMKID(s) first before adding the new one.
 			 */
 			wpa_printf(MSG_DEBUG,
-				   "FT: Remove %u old PMKID(s) from RSN IE",
+				   "RSN: Remove %u old PMKID(s) from RSNE",
 				   num_pmkid);
 			after = rpos + 2 + num_pmkid * PMKID_LEN;
-			os_memmove(rpos + 2, after, rend - after);
+			os_memmove(rpos + 2, after, end - after);
 			start[1] -= num_pmkid * PMKID_LEN;
 			added -= num_pmkid * PMKID_LEN;
 		}
@@ -2208,14 +2212,13 @@
 		start[1] += PMKID_LEN;
 	}
 
-	wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
-		    "(PMKID inserted)", start, 2 + start[1]);
+	wpa_hexdump(MSG_DEBUG, "RSN: RSNE after modification (PMKID inserted)",
+		    start, 2 + start[1]);
 
 	*ies_len += added;
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
 
 
 int wpa_cipher_key_len(int cipher)
@@ -2600,3 +2603,266 @@
 	return 0;
 }
 #endif /* CONFIG_FILS */
+
+
+/**
+ * wpa_parse_vendor_specific - Parse Vendor Specific IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
+				     struct wpa_eapol_ie_parse *ie)
+{
+	unsigned int oui;
+
+	if (pos[1] < 4) {
+		wpa_printf(MSG_MSGDUMP,
+			   "Too short vendor specific IE ignored (len=%u)",
+			   pos[1]);
+		return 1;
+	}
+
+	oui = WPA_GET_BE24(&pos[2]);
+	if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
+		if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
+			ie->wmm = &pos[2];
+			ie->wmm_len = pos[1];
+			wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
+				    ie->wmm, ie->wmm_len);
+		} else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
+			ie->wmm = &pos[2];
+			ie->wmm_len = pos[1];
+			wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
+				    ie->wmm, ie->wmm_len);
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+			     struct wpa_eapol_ie_parse *ie)
+{
+	if (pos[1] == 0)
+		return 1;
+
+	if (pos[1] >= 6 &&
+	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
+	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+		ie->wpa_ie = pos;
+		ie->wpa_ie_len = pos[1] + 2;
+		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
+			    ie->wpa_ie, ie->wpa_ie_len);
+		return 0;
+	}
+
+	if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
+		ie->osen = pos;
+		ie->osen_len = pos[1] + 2;
+		return 0;
+	}
+
+	if (1 + RSN_SELECTOR_LEN < end - pos &&
+	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
+				pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
+				pos, pos[1] + 2);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: IP Address Allocation in EAPOL-Key",
+			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
+		ie->oci = pos + 2 + RSN_SELECTOR_LEN;
+		ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
+{
+	const u8 *pos, *end;
+	int ret = 0;
+
+	os_memset(ie, 0, sizeof(*ie));
+	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
+		if (pos[0] == 0xdd &&
+		    ((pos == buf + len - 1) || pos[1] == 0)) {
+			/* Ignore padding */
+			break;
+		}
+		if (2 + pos[1] > end - pos) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key Key Data underflow (ie=%d len=%d pos=%d)",
+				   pos[0], pos[1], (int) (pos - buf));
+			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", buf, len);
+			ret = -1;
+			break;
+		}
+		if (*pos == WLAN_EID_RSN) {
+			ie->rsn_ie = pos;
+			ie->rsn_ie_len = pos[1] + 2;
+			wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
+				    ie->rsn_ie, ie->rsn_ie_len);
+		} else if (*pos == WLAN_EID_RSNX) {
+			ie->rsnxe = pos;
+			ie->rsnxe_len = pos[1] + 2;
+			wpa_hexdump(MSG_DEBUG, "WPA: RSNXE in EAPOL-Key",
+				    ie->rsnxe, ie->rsnxe_len);
+		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+			ie->mdie = pos;
+			ie->mdie_len = pos[1] + 2;
+			wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
+				    ie->mdie, ie->mdie_len);
+		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+			ie->ftie = pos;
+			ie->ftie_len = pos[1] + 2;
+			wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
+				    ie->ftie, ie->ftie_len);
+		} else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
+			if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
+				ie->reassoc_deadline = pos;
+				wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
+					    "in EAPOL-Key",
+					    ie->reassoc_deadline, pos[1] + 2);
+			} else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
+				ie->key_lifetime = pos;
+				wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
+					    "in EAPOL-Key",
+					    ie->key_lifetime, pos[1] + 2);
+			} else {
+				wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
+					    "EAPOL-Key Key Data IE",
+					    pos, 2 + pos[1]);
+			}
+		} else if (*pos == WLAN_EID_LINK_ID) {
+			if (pos[1] >= 18) {
+				ie->lnkid = pos;
+				ie->lnkid_len = pos[1] + 2;
+			}
+		} else if (*pos == WLAN_EID_EXT_CAPAB) {
+			ie->ext_capab = pos;
+			ie->ext_capab_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_SUPP_RATES) {
+			ie->supp_rates = pos;
+			ie->supp_rates_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
+			ie->ext_supp_rates = pos;
+			ie->ext_supp_rates_len = pos[1] + 2;
+		} else if (*pos == WLAN_EID_HT_CAP &&
+			   pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
+			ie->ht_capabilities = pos + 2;
+		} else if (*pos == WLAN_EID_VHT_AID) {
+			if (pos[1] >= 2)
+				ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
+		} else if (*pos == WLAN_EID_VHT_CAP &&
+			   pos[1] >= sizeof(struct ieee80211_vht_capabilities))
+		{
+			ie->vht_capabilities = pos + 2;
+		} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
+			ie->qosinfo = pos[2];
+		} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
+			ie->supp_channels = pos + 2;
+			ie->supp_channels_len = pos[1];
+		} else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
+			/*
+			 * The value of the Length field of the Supported
+			 * Operating Classes element is between 2 and 253.
+			 * Silently skip invalid elements to avoid interop
+			 * issues when trying to use the value.
+			 */
+			if (pos[1] >= 2 && pos[1] <= 253) {
+				ie->supp_oper_classes = pos + 2;
+				ie->supp_oper_classes_len = pos[1];
+			}
+		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+			ret = wpa_parse_generic(pos, end, ie);
+			if (ret < 0)
+				break;
+			if (ret > 0) {
+				ret = 0;
+				break;
+			}
+
+			ret = wpa_parse_vendor_specific(pos, end, ie);
+			if (ret < 0)
+				break;
+			if (ret > 0) {
+				ret = 0;
+				break;
+			}
+		} else {
+			wpa_hexdump(MSG_DEBUG,
+				    "WPA: Unrecognized EAPOL-Key Key Data IE",
+				    pos, 2 + pos[1]);
+		}
+	}
+
+	return ret;
+}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index e83d688..beb1ecd 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -104,9 +104,7 @@
 #endif
 #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
-#ifdef CONFIG_IEEE80211W
 #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
-#endif /* CONFIG_IEEE80211W */
 #define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
 #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
 #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
@@ -130,10 +128,8 @@
 #pragma pack(push, 1)
 #endif /* _MSC_VER */
 
-#ifdef CONFIG_IEEE80211W
 #define WPA_IGTK_LEN 16
 #define WPA_IGTK_MAX_LEN 32
-#endif /* CONFIG_IEEE80211W */
 
 
 /* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
@@ -226,12 +222,10 @@
 	size_t gtk_len;
 };
 
-#ifdef CONFIG_IEEE80211W
 struct wpa_igtk {
 	u8 igtk[WPA_IGTK_MAX_LEN];
 	size_t igtk_len;
 };
-#endif /* CONFIG_IEEE80211W */
 
 /* WPA IE version 1
  * 00-50-f2:1 (OUI:OUI type)
@@ -291,14 +285,12 @@
 	be16 error_type;
 } STRUCT_PACKED;
 
-#ifdef CONFIG_IEEE80211W
 #define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
 struct wpa_igtk_kde {
 	u8 keyid[2];
 	u8 pn[6];
 	u8 igtk[WPA_IGTK_MAX_LEN];
 } STRUCT_PACKED;
-#endif /* CONFIG_IEEE80211W */
 
 struct rsn_mdie {
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
@@ -372,7 +364,9 @@
 	       const u8 *mdie, size_t mdie_len,
 	       const u8 *ftie, size_t ftie_len,
 	       const u8 *rsnie, size_t rsnie_len,
-	       const u8 *ric, size_t ric_len, u8 *mic);
+	       const u8 *ric, size_t ric_len,
+	       const u8 *rsnxe, size_t rsnxe_len,
+	       u8 *mic);
 int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
 		      const u8 *ssid, size_t ssid_len,
 		      const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
@@ -451,8 +445,11 @@
 	size_t gtk_len;
 	const u8 *r0kh_id;
 	size_t r0kh_id_len;
+	const u8 *fte_anonce;
+	const u8 *fte_snonce;
 	const u8 *rsn;
 	size_t rsn_len;
+	u16 rsn_capab;
 	const u8 *rsn_pmkid;
 	const u8 *tie;
 	size_t tie_len;
@@ -466,11 +463,67 @@
 	size_t ric_len;
 	int key_mgmt;
 	int pairwise_cipher;
+	const u8 *rsnxe;
+	size_t rsnxe_len;
 };
 
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
 		     int use_sha384);
 
+struct wpa_eapol_ie_parse {
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+	const u8 *rsn_ie;
+	size_t rsn_ie_len;
+	const u8 *pmkid;
+	const u8 *gtk;
+	size_t gtk_len;
+	const u8 *mac_addr;
+	size_t mac_addr_len;
+	const u8 *igtk;
+	size_t igtk_len;
+	const u8 *mdie;
+	size_t mdie_len;
+	const u8 *ftie;
+	size_t ftie_len;
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+	const u8 *oci;
+	size_t oci_len;
+	const u8 *osen;
+	size_t osen_len;
+	const u8 *rsnxe;
+	size_t rsnxe_len;
+	const u8 *reassoc_deadline;
+	const u8 *key_lifetime;
+	const u8 *lnkid;
+	size_t lnkid_len;
+	const u8 *ext_capab;
+	size_t ext_capab_len;
+	const u8 *supp_rates;
+	size_t supp_rates_len;
+	const u8 *ext_supp_rates;
+	size_t ext_supp_rates_len;
+	const u8 *ht_capabilities;
+	const u8 *vht_capabilities;
+	const u8 *supp_channels;
+	size_t supp_channels_len;
+	const u8 *supp_oper_classes;
+	size_t supp_oper_classes_len;
+	u8 qosinfo;
+	u16 aid;
+	const u8 *wmm;
+	size_t wmm_len;
+};
+
+int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie);
+static inline int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+					   struct wpa_eapol_ie_parse *ie)
+{
+	return wpa_parse_kde_ies(buf, len, ie);
+}
+
+
 int wpa_cipher_key_len(int cipher);
 int wpa_cipher_rsc_len(int cipher);
 enum wpa_alg wpa_cipher_to_alg(int cipher);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index f65077e..f03c698 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -87,6 +87,9 @@
 #define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS "
 /** Regulatory domain channel */
 #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
+/** Channel switch started (followed by freq=<MHz> and other channel parameters)
+ */
+#define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH "
 /** Channel switch (followed by freq=<MHz> and other channel parameters) */
 #define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
 /** SAE authentication failed due to unknown password identifier */
@@ -165,8 +168,10 @@
 #define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
 #define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
 #define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
+#define DPP_EVENT_CONN_STATUS_RESULT "DPP-CONN-STATUS-RESULT "
 #define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM "
 #define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
+#define DPP_EVENT_CONFOBJ_SSID_CHARSET "DPP-CONFOBJ-SSID-CHARSET "
 #define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
 #define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
 #define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
@@ -296,6 +301,8 @@
 #define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
 #define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
 #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
+#define WPS_EVENT_PIN_ACTIVE "WPS-PIN-ACTIVE "
+#define WPS_EVENT_CANCEL "WPS-CANCEL "
 #define AP_STA_CONNECTED "AP-STA-CONNECTED "
 #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
 #define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
diff --git a/src/crypto/aes_i.h b/src/crypto/aes_i.h
index 54375cf..b20ec92 100644
--- a/src/crypto/aes_i.h
+++ b/src/crypto/aes_i.h
@@ -65,7 +65,7 @@
 
 #else /* AES_SMALL_TABLES */
 
-#define RCON(i) (rcons[(i)] << 24)
+#define RCON(i) ((u32) rcons[(i)] << 24)
 
 static inline u32 rotr(u32 val, int bits)
 {
@@ -94,10 +94,10 @@
 #define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8)
 #define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16)
 #define TD3(i) rotr(Td0[(i) & 0xff], 24)
-#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24)
-#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16)
-#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8)
-#define TD44(i) (Td4s[(i) & 0xff])
+#define TD41(i) ((u32) Td4s[((i) >> 24) & 0xff] << 24)
+#define TD42(i) ((u32) Td4s[((i) >> 16) & 0xff] << 16)
+#define TD43(i) ((u32) Td4s[((i) >> 8) & 0xff] << 8)
+#define TD44(i) ((u32) Td4s[(i) & 0xff])
 #define TD0_(i) Td0[(i) & 0xff]
 #define TD1_(i) rotr(Td0[(i) & 0xff], 8)
 #define TD2_(i) rotr(Td0[(i) & 0xff], 16)
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 12109ce..440da03 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -519,6 +519,13 @@
 struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
 
 /**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value (uint)
+ * @val: Value to set
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_uint(unsigned int val);
+
+/**
  * crypto_bignum_deinit - Free bignum
  * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
  * @clear: Whether to clear the value from memory
@@ -613,6 +620,19 @@
 		      struct crypto_bignum *c);
 
 /**
+ * crypto_bignum_addmod - d = a + b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a + b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_addmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d);
+
+/**
  * crypto_bignum_mulmod - d = a * b (mod c)
  * @a: Bignum
  * @b: Bignum
@@ -626,6 +646,17 @@
 			 struct crypto_bignum *d);
 
 /**
+ * crypto_bignum_sqrmod - c = a^2 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a^2 % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 struct crypto_bignum *c);
+
+/**
  * crypto_bignum_rshift - r = a >> n
  * @a: Bignum
  * @n: Number of bits
@@ -645,13 +676,6 @@
 		      const struct crypto_bignum *b);
 
 /**
- * crypto_bignum_bits - Get size of a bignum in bits
- * @a: Bignum
- * Returns: Number of bits in the bignum
- */
-int crypto_bignum_bits(const struct crypto_bignum *a);
-
-/**
  * crypto_bignum_is_zero - Is the given bignum zero
  * @a: Bignum
  * Returns: 1 if @a is zero or 0 if not
@@ -738,6 +762,9 @@
  */
 const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
 
+const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e);
+const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e);
+
 /**
  * struct crypto_ec_point - Elliptic curve point
  *
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 1b0c1ec..783b293 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -570,8 +570,8 @@
 		failed = !q || !ctx || !tmp ||
 			!BN_mod_exp(tmp, pub, q, p, ctx) ||
 			!BN_is_one(tmp);
-		BN_clear(q);
-		BN_clear(tmp);
+		BN_clear_free(q);
+		BN_clear_free(tmp);
 		BN_CTX_free(ctx);
 		if (failed)
 			goto fail;
@@ -580,8 +580,8 @@
 	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
 			     prime, prime_len, secret, len);
 fail:
-	BN_clear(pub);
-	BN_clear(p);
+	BN_clear_free(pub);
+	BN_clear_free(p);
 	return res;
 }
 
@@ -1283,6 +1283,24 @@
 }
 
 
+struct crypto_bignum * crypto_bignum_init_uint(unsigned int val)
+{
+	BIGNUM *bn;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	bn = BN_new();
+	if (!bn)
+		return NULL;
+	if (BN_set_word(bn, val) != 1) {
+		BN_free(bn);
+		return NULL;
+	}
+	return (struct crypto_bignum *) bn;
+}
+
+
 void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
 {
 	if (clear)
@@ -1303,6 +1321,18 @@
 	if (padlen > buflen)
 		return -1;
 
+	if (padlen) {
+#ifdef OPENSSL_IS_BORINGSSL
+		if (BN_bn2bin_padded(buf, padlen, (const BIGNUM *) a) == 0)
+			return -1;
+		return padlen;
+#else /* OPENSSL_IS_BORINGSSL */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+		return BN_bn2binpad((const BIGNUM *) a, buf, padlen);
+#endif
+#endif
+	}
+
 	num_bytes = BN_num_bytes((const BIGNUM *) a);
 	if ((size_t) num_bytes > buflen)
 		return -1;
@@ -1437,6 +1467,28 @@
 }
 
 
+int crypto_bignum_addmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		return -1;
+	res = BN_mod_add((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
 int crypto_bignum_mulmod(const struct crypto_bignum *a,
 			 const struct crypto_bignum *b,
 			 const struct crypto_bignum *c,
@@ -1460,6 +1512,27 @@
 }
 
 
+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 struct crypto_bignum *c)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		return -1;
+	res = BN_mod_sqr((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
+			 bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
 int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
 			 struct crypto_bignum *r)
 {
@@ -1476,12 +1549,6 @@
 }
 
 
-int crypto_bignum_bits(const struct crypto_bignum *a)
-{
-	return BN_num_bits((const BIGNUM *) a);
-}
-
-
 int crypto_bignum_is_zero(const struct crypto_bignum *a)
 {
 	return BN_is_zero((const BIGNUM *) a);
@@ -1676,6 +1743,18 @@
 }
 
 
+const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->a;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->b;
+}
+
+
 void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
 {
 	if (clear)
@@ -1870,7 +1949,7 @@
 {
 	struct crypto_ecdh *ecdh;
 	EVP_PKEY *params = NULL;
-	EC_KEY *ec_params;
+	EC_KEY *ec_params = NULL;
 	EVP_PKEY_CTX *kctx = NULL;
 
 	ecdh = os_zalloc(sizeof(*ecdh));
@@ -1913,6 +1992,7 @@
 	}
 
 done:
+	EC_KEY_free(ec_params);
 	EVP_PKEY_free(params);
 	EVP_PKEY_CTX_free(kctx);
 
@@ -2052,13 +2132,17 @@
 	secret = wpabuf_alloc(secret_len);
 	if (!secret)
 		goto fail;
-	if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len),
-			    &secret_len) != 1) {
+	if (EVP_PKEY_derive(ctx, wpabuf_put(secret, 0), &secret_len) != 1) {
 		wpa_printf(MSG_ERROR,
 			   "OpenSSL: EVP_PKEY_derive(2) failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
 	}
+	if (secret->size != secret_len)
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: EVP_PKEY_derive(2) changed secret_len %d -> %d",
+			   (int) secret->size, (int) secret_len);
+	wpabuf_put(secret, secret_len);
 
 done:
 	BN_free(x);
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index 976a008..85ce565 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -1042,6 +1042,26 @@
 }
 
 
+struct crypto_bignum * crypto_bignum_init_uint(unsigned int val)
+{
+	mp_int *a;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	a = (mp_int *) crypto_bignum_init();
+	if (!a)
+		return NULL;
+
+	if (mp_set_int(a, val) != MP_OKAY) {
+		os_free(a);
+		a = NULL;
+	}
+
+	return (struct crypto_bignum *) a;
+}
+
+
 void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
 {
 	if (!n)
@@ -1151,7 +1171,7 @@
 	if (TEST_FAIL())
 		return -1;
 
-	return mp_add((mp_int *) a, (mp_int *) b,
+	return mp_sub((mp_int *) a, (mp_int *) b,
 		      (mp_int *) r) == MP_OKAY ? 0 : -1;
 }
 
@@ -1168,6 +1188,19 @@
 }
 
 
+int crypto_bignum_addmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_addmod((mp_int *) a, (mp_int *) b, (mp_int *) c,
+			 (mp_int *) d) == MP_OKAY ?  0 : -1;
+}
+
+
 int crypto_bignum_mulmod(const struct crypto_bignum *a,
 			 const struct crypto_bignum *b,
 			 const struct crypto_bignum *m,
@@ -1181,6 +1214,18 @@
 }
 
 
+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 struct crypto_bignum *c)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_sqrmod((mp_int *) a, (mp_int *) b,
+			 (mp_int *) c) == MP_OKAY ?  0 : -1;
+}
+
+
 int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
 			 struct crypto_bignum *r)
 {
@@ -1198,12 +1243,6 @@
 }
 
 
-int crypto_bignum_bits(const struct crypto_bignum *a)
-{
-	return mp_count_bits((mp_int *) a);
-}
-
-
 int crypto_bignum_is_zero(const struct crypto_bignum *a)
 {
 	return mp_iszero((mp_int *) a);
@@ -1392,6 +1431,18 @@
 }
 
 
+const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->a;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->b;
+}
+
+
 void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
 {
 	ecc_point *point = (ecc_point *) p;
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index a491707..ffa04df 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -224,7 +224,7 @@
 	/* Wipe variables */
 	a = b = c = d = e = 0;
 #ifdef SHA1HANDSOFF
-	os_memset(block, 0, 64);
+	forced_memzero(block, 64);
 #endif
 }
 
@@ -300,7 +300,7 @@
 	os_memset(context->buffer, 0, 64);
 	os_memset(context->state, 0, 20);
 	os_memset(context->count, 0, 8);
-	os_memset(finalcount, 0, 8);
+	forced_memzero(finalcount, sizeof(finalcount));
 }
 
 /* ===== end - public domain SHA1 implementation ===== */
diff --git a/src/crypto/sha1-prf.c b/src/crypto/sha1-prf.c
index 4b2d137..1385149 100644
--- a/src/crypto/sha1-prf.c
+++ b/src/crypto/sha1-prf.c
@@ -61,7 +61,7 @@
 		}
 		counter++;
 	}
-	os_memset(hash, 0, sizeof(hash));
+	forced_memzero(hash, sizeof(hash));
 
 	return 0;
 }
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
index a11649a..5e8d159 100644
--- a/src/crypto/sha1-tlsprf.c
+++ b/src/crypto/sha1-tlsprf.c
@@ -92,10 +92,10 @@
 		SHA1_pos++;
 	}
 
-	os_memset(A_MD5, 0, MD5_MAC_LEN);
-	os_memset(P_MD5, 0, MD5_MAC_LEN);
-	os_memset(A_SHA1, 0, SHA1_MAC_LEN);
-	os_memset(P_SHA1, 0, SHA1_MAC_LEN);
+	forced_memzero(A_MD5, MD5_MAC_LEN);
+	forced_memzero(P_MD5, MD5_MAC_LEN);
+	forced_memzero(A_SHA1, SHA1_MAC_LEN);
+	forced_memzero(P_SHA1, SHA1_MAC_LEN);
 
 	return 0;
 }
diff --git a/src/crypto/sha1-tprf.c b/src/crypto/sha1-tprf.c
index 562510f..c3acf19 100644
--- a/src/crypto/sha1-tprf.c
+++ b/src/crypto/sha1-tprf.c
@@ -66,7 +66,7 @@
 		len[0] = SHA1_MAC_LEN;
 	}
 
-	os_memset(hash, 0, SHA1_MAC_LEN);
+	forced_memzero(hash, SHA1_MAC_LEN);
 
 	return 0;
 }
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
index 8fce139..76d7a68 100644
--- a/src/crypto/sha1.c
+++ b/src/crypto/sha1.c
@@ -86,7 +86,8 @@
 	_addr[1] = mac;
 	_len[1] = SHA1_MAC_LEN;
 	ret = sha1_vector(2, _addr, _len, mac);
-	os_memset(k_pad, 0, sizeof(k_pad));
+	forced_memzero(k_pad, sizeof(k_pad));
+	forced_memzero(tk, sizeof(tk));
 	return ret;
 }
 
diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c
index af7d954..5a6b744 100644
--- a/src/crypto/sha256-kdf.c
+++ b/src/crypto/sha256-kdf.c
@@ -69,7 +69,7 @@
 
 		if (iter == 255) {
 			os_memset(out, 0, outlen);
-			os_memset(T, 0, SHA256_MAC_LEN);
+			forced_memzero(T, SHA256_MAC_LEN);
 			return -1;
 		}
 		iter++;
@@ -77,11 +77,11 @@
 		if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0)
 		{
 			os_memset(out, 0, outlen);
-			os_memset(T, 0, SHA256_MAC_LEN);
+			forced_memzero(T, SHA256_MAC_LEN);
 			return -1;
 		}
 	}
 
-	os_memset(T, 0, SHA256_MAC_LEN);
+	forced_memzero(T, SHA256_MAC_LEN);
 	return 0;
 }
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index 722cad6..d665a99 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -102,7 +102,7 @@
 		buf[pos - 1] &= mask;
 	}
 
-	os_memset(hash, 0, sizeof(hash));
+	forced_memzero(hash, sizeof(hash));
 
 	return 0;
 }
diff --git a/src/crypto/sha256-tlsprf.c b/src/crypto/sha256-tlsprf.c
index 0528dad..9045cd3 100644
--- a/src/crypto/sha256-tlsprf.c
+++ b/src/crypto/sha256-tlsprf.c
@@ -26,8 +26,8 @@
  * This function is used to derive new, cryptographically separate keys from a
  * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
  */
-void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
-		    const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
 {
 	size_t clen;
 	u8 A[SHA256_MAC_LEN];
@@ -50,12 +50,15 @@
 	 * PRF(secret, label, seed) = P_SHA256(secret, label + seed)
 	 */
 
-	hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A);
+	if (hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A) < 0)
+		return -1;
 
 	pos = 0;
 	while (pos < outlen) {
-		hmac_sha256_vector(secret, secret_len, 3, addr, len, P);
-		hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A);
+		if (hmac_sha256_vector(secret, secret_len, 3, addr, len, P) <
+		    0 ||
+		    hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A) < 0)
+			return -1;
 
 		clen = outlen - pos;
 		if (clen > SHA256_MAC_LEN)
@@ -63,4 +66,6 @@
 		os_memcpy(out + pos, P, clen);
 		pos += clen;
 	}
+
+	return 0;
 }
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index 5219022..8054bbe 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -20,9 +20,9 @@
 int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
 		    const u8 *data, size_t data_len, u8 *buf,
 		    size_t buf_len_bits);
-void tls_prf_sha256(const u8 *secret, size_t secret_len,
-		    const char *label, const u8 *seed, size_t seed_len,
-		    u8 *out, size_t outlen);
+int tls_prf_sha256(const u8 *secret, size_t secret_len,
+		   const char *label, const u8 *seed, size_t seed_len,
+		   u8 *out, size_t outlen);
 int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
 		    u8 *out, size_t outlen);
diff --git a/src/crypto/sha384-kdf.c b/src/crypto/sha384-kdf.c
index 1d19627..babcb9e 100644
--- a/src/crypto/sha384-kdf.c
+++ b/src/crypto/sha384-kdf.c
@@ -69,7 +69,7 @@
 
 		if (iter == 255) {
 			os_memset(out, 0, outlen);
-			os_memset(T, 0, SHA384_MAC_LEN);
+			forced_memzero(T, SHA384_MAC_LEN);
 			return -1;
 		}
 		iter++;
@@ -77,11 +77,11 @@
 		if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0)
 		{
 			os_memset(out, 0, outlen);
-			os_memset(T, 0, SHA384_MAC_LEN);
+			forced_memzero(T, SHA384_MAC_LEN);
 			return -1;
 		}
 	}
 
-	os_memset(T, 0, SHA384_MAC_LEN);
+	forced_memzero(T, SHA384_MAC_LEN);
 	return 0;
 }
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
index 03e3cb3..420e78c 100644
--- a/src/crypto/sha384-prf.c
+++ b/src/crypto/sha384-prf.c
@@ -102,7 +102,7 @@
 		buf[pos - 1] &= mask;
 	}
 
-	os_memset(hash, 0, sizeof(hash));
+	forced_memzero(hash, sizeof(hash));
 
 	return 0;
 }
diff --git a/src/crypto/sha384-tlsprf.c b/src/crypto/sha384-tlsprf.c
new file mode 100644
index 0000000..9ff96ac
--- /dev/null
+++ b/src/crypto/sha384-tlsprf.c
@@ -0,0 +1,71 @@
+/*
+ * TLS PRF P_SHA384
+ * Copyright (c) 2011-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 "sha384.h"
+
+
+/**
+ * tls_prf_sha384 - Pseudo-Random Function for TLS v1.2 (P_SHA384, RFC 5246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 5246, Chapter 5.
+ */
+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	size_t clen;
+	u8 A[SHA384_MAC_LEN];
+	u8 P[SHA384_MAC_LEN];
+	size_t pos;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = A;
+	len[0] = SHA384_MAC_LEN;
+	addr[1] = (unsigned char *) label;
+	len[1] = os_strlen(label);
+	addr[2] = seed;
+	len[2] = seed_len;
+
+	/*
+	 * RFC 5246, Chapter 5
+	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+	 * PRF(secret, label, seed) = P_SHA384(secret, label + seed)
+	 */
+
+	if (hmac_sha384_vector(secret, secret_len, 2, &addr[1], &len[1], A) < 0)
+		return -1;
+
+	pos = 0;
+	while (pos < outlen) {
+		if (hmac_sha384_vector(secret, secret_len, 3, addr, len, P) <
+		    0 ||
+		    hmac_sha384(secret, secret_len, A, SHA384_MAC_LEN, A) < 0)
+			return -1;
+
+		clen = outlen - pos;
+		if (clen > SHA384_MAC_LEN)
+			clen = SHA384_MAC_LEN;
+		os_memcpy(out + pos, P, clen);
+		pos += clen;
+	}
+
+	return 0;
+}
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
index 2241425..d946907 100644
--- a/src/crypto/sha384.h
+++ b/src/crypto/sha384.h
@@ -20,6 +20,9 @@
 int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
 		    const u8 *data, size_t data_len, u8 *buf,
 		    size_t buf_len_bits);
+int tls_prf_sha384(const u8 *secret, size_t secret_len,
+		   const char *label, const u8 *seed, size_t seed_len,
+		   u8 *out, size_t outlen);
 int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
 		    u8 *out, size_t outlen);
diff --git a/src/crypto/sha512-kdf.c b/src/crypto/sha512-kdf.c
index 8b71f9b..5bde664 100644
--- a/src/crypto/sha512-kdf.c
+++ b/src/crypto/sha512-kdf.c
@@ -69,7 +69,7 @@
 
 		if (iter == 255) {
 			os_memset(out, 0, outlen);
-			os_memset(T, 0, SHA512_MAC_LEN);
+			forced_memzero(T, SHA512_MAC_LEN);
 			return -1;
 		}
 		iter++;
@@ -77,11 +77,11 @@
 		if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0)
 		{
 			os_memset(out, 0, outlen);
-			os_memset(T, 0, SHA512_MAC_LEN);
+			forced_memzero(T, SHA512_MAC_LEN);
 			return -1;
 		}
 	}
 
-	os_memset(T, 0, SHA512_MAC_LEN);
+	forced_memzero(T, SHA512_MAC_LEN);
 	return 0;
 }
diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c
index 3b2ad88..e48cf5f 100644
--- a/src/crypto/sha512-prf.c
+++ b/src/crypto/sha512-prf.c
@@ -102,7 +102,7 @@
 		buf[pos - 1] &= mask;
 	}
 
-	os_memset(hash, 0, sizeof(hash));
+	forced_memzero(hash, sizeof(hash));
 
 	return 0;
 }
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 8bdb91f..c8b1a82 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -48,6 +48,18 @@
 
 #define TLS_MAX_ALT_SUBJECT 10
 
+struct tls_cert_data {
+	int depth;
+	const char *subject;
+	const struct wpabuf *cert;
+	const u8 *hash;
+	size_t hash_len;
+	const char *altsubject[TLS_MAX_ALT_SUBJECT];
+	int num_altsubject;
+	const char *serial_num;
+	int tod;
+};
+
 union tls_event_data {
 	struct {
 		int depth;
@@ -57,16 +69,7 @@
 		const struct wpabuf *cert;
 	} cert_fail;
 
-	struct {
-		int depth;
-		const char *subject;
-		const struct wpabuf *cert;
-		const u8 *hash;
-		size_t hash_len;
-		const char *altsubject[TLS_MAX_ALT_SUBJECT];
-		int num_altsubject;
-		const char *serial_num;
-	} peer_cert;
+	struct tls_cert_data peer_cert;
 
 	struct {
 		int is_local;
@@ -108,6 +111,7 @@
 #define TLS_CONN_ENABLE_TLSv1_0 BIT(14)
 #define TLS_CONN_ENABLE_TLSv1_1 BIT(15)
 #define TLS_CONN_ENABLE_TLSv1_2 BIT(16)
+#define TLS_CONN_TEAP_ANON_DH BIT(17)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -184,12 +188,15 @@
 	const char *suffix_match;
 	const char *domain_match;
 	const char *client_cert;
+	const char *client_cert2;
 	const u8 *client_cert_blob;
 	size_t client_cert_blob_len;
 	const char *private_key;
+	const char *private_key2;
 	const u8 *private_key_blob;
 	size_t private_key_blob_len;
 	const char *private_key_passwd;
+	const char *private_key_passwd2;
 	const char *dh_file;
 	const u8 *dh_blob;
 	size_t dh_blob_len;
@@ -643,4 +650,24 @@
 
 void tls_connection_remove_session(struct tls_connection *conn);
 
+/**
+ * tls_get_tls_unique - Fetch "tls-unique" for channel binding
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the value
+ * @max_len: Maximum length of the buffer in bytes
+ * Returns: Number of bytes written to buf or -1 on error
+ *
+ * This function can be used to fetch "tls-unique" (RFC 5929, Section 3) which
+ * is the first TLS Finished message sent in the most recent TLS handshake of
+ * the TLS connection.
+ */
+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len);
+
+/**
+ * tls_connection_get_cipher_suite - Get current TLS cipher suite
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: TLS cipher suite of the current connection or 0 on error
+ */
+u16 tls_connection_get_cipher_suite(struct tls_connection *conn);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 620254a..84ec985 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -44,6 +44,13 @@
 #define OPENSSL_NEED_EAP_FAST_PRF
 #endif
 
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
+	defined(EAP_SERVER_FAST) || defined(EAP_TEAP) || \
+	defined(EAP_SERVER_TEAP)
+#define EAP_FAST_OR_TEAP
+#endif
+
+
 #if defined(OPENSSL_IS_BORINGSSL)
 /* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
 typedef size_t stack_index_t;
@@ -1085,11 +1092,8 @@
 	}
 
 #ifndef OPENSSL_NO_ENGINE
-	wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-	ERR_load_ENGINE_strings();
-	ENGINE_load_dynamic();
-#endif /* OPENSSL_VERSION_NUMBER */
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading builtin engines");
+	ENGINE_load_builtin_engines();
 
 	if (conf &&
 	    (conf->opensc_engine_path || conf->pkcs11_engine_path ||
@@ -1345,6 +1349,8 @@
 		return "heartbeat";
 	case 256:
 		return "TLS header info"; /* pseudo content type */
+	case 257:
+		return "inner content type"; /* pseudo content type */
 	default:
 		return "?";
 	}
@@ -1354,6 +1360,8 @@
 static const char * openssl_handshake_type(int content_type, const u8 *buf,
 					   size_t len)
 {
+	if (content_type == 257 && buf && len == 1)
+		return openssl_content_type(buf[0]);
 	if (content_type != 22 || !buf || len == 0)
 		return "";
 	switch (buf[0]) {
@@ -1584,6 +1592,11 @@
 	options |= SSL_OP_NO_COMPRESSION;
 #endif /* SSL_OP_NO_COMPRESSION */
 	SSL_set_options(conn->ssl, options);
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+	/* Hopefully there is no need for middlebox compatibility mechanisms
+	 * when going through EAP authentication. */
+	SSL_clear_options(conn->ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
+#endif
 
 	conn->ssl_in = BIO_new(BIO_s_mem());
 	if (!conn->ssl_in) {
@@ -2170,6 +2183,36 @@
 }
 
 
+static int openssl_cert_tod(X509 *cert)
+{
+	CERTIFICATEPOLICIES *ext;
+	stack_index_t i;
+	char buf[100];
+	int res;
+	int tod = 0;
+
+	ext = X509_get_ext_d2i(cert, NID_certificate_policies, NULL, NULL);
+	if (!ext)
+		return 0;
+
+	for (i = 0; i < sk_POLICYINFO_num(ext); i++) {
+		POLICYINFO *policy;
+
+		policy = sk_POLICYINFO_value(ext, i);
+		res = OBJ_obj2txt(buf, sizeof(buf), policy->policyid, 0);
+		if (res < 0 || (size_t) res >= sizeof(buf))
+			continue;
+		wpa_printf(MSG_DEBUG, "OpenSSL: 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 */
+	}
+
+	return tod;
+}
+
+
 static void openssl_tls_cert_event(struct tls_connection *conn,
 				   X509 *err_cert, int depth,
 				   const char *subject)
@@ -2262,6 +2305,8 @@
 		ev.peer_cert.altsubject[alt] = altsubject[alt];
 	ev.peer_cert.num_altsubject = num_altsubject;
 
+	ev.peer_cert.tod = openssl_cert_tod(err_cert);
+
 	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
 	wpabuf_free(cert);
 	for (alt = 0; alt < num_altsubject; alt++)
@@ -2269,6 +2314,38 @@
 }
 
 
+static void debug_print_cert(X509 *cert, const char *title)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+
+	if (wpa_debug_level > MSG_DEBUG)
+		return;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	X509_print(out, cert);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt);
+		}
+		os_free(txt);
+	}
+
+	BIO_free(out);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
 static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
 {
 	char buf[256];
@@ -2289,6 +2366,8 @@
 	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
 	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
 					 SSL_get_ex_data_X509_STORE_CTX_idx());
+	os_snprintf(buf, sizeof(buf), "Peer certificate - depth %d", depth);
+	debug_print_cert(err_cert, buf);
 	X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
 
 	conn = SSL_get_app_data(ssl);
@@ -2366,7 +2445,30 @@
 	}
 #endif /* CONFIG_SHA256 */
 
+	openssl_tls_cert_event(conn, err_cert, depth, buf);
+
 	if (!preverify_ok) {
+		if (depth > 0) {
+			/* Send cert event for the peer certificate so that
+			 * the upper layers get information about it even if
+			 * validation of a CA certificate fails. */
+			STACK_OF(X509) *chain;
+
+			chain = X509_STORE_CTX_get1_chain(x509_ctx);
+			if (chain && sk_X509_num(chain) > 0) {
+				char buf2[256];
+				X509 *cert;
+
+				cert = sk_X509_value(chain, 0);
+				X509_NAME_oneline(X509_get_subject_name(cert),
+						  buf2, sizeof(buf2));
+
+				openssl_tls_cert_event(conn, cert, 0, buf2);
+			}
+			if (chain)
+				sk_X509_pop_free(chain, X509_free);
+		}
+
 		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
 			   " error %d (%s) depth %d for '%s'", err, err_str,
 			   depth, buf);
@@ -2422,8 +2524,7 @@
 		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
 				       "Domain mismatch",
 				       TLS_FAIL_DOMAIN_MISMATCH);
-	} else
-		openssl_tls_cert_event(conn, err_cert, depth, buf);
+	}
 
 	if (conn->cert_probe && preverify_ok && depth == 0) {
 		wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
@@ -2598,9 +2699,23 @@
 				      (const unsigned char **) &ca_cert_blob,
 				      ca_cert_blob_len);
 		if (cert == NULL) {
-			tls_show_errors(MSG_WARNING, __func__,
-					"Failed to parse ca_cert_blob");
-			return -1;
+			BIO *bio = BIO_new_mem_buf(ca_cert_blob,
+						   ca_cert_blob_len);
+
+			if (bio) {
+				cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+				BIO_free(bio);
+			}
+
+			if (!cert) {
+				tls_show_errors(MSG_WARNING, __func__,
+						"Failed to parse ca_cert_blob");
+				return -1;
+			}
+
+			while (ERR_get_error()) {
+				/* Ignore errors from DER conversion. */
+			}
 		}
 
 		if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
@@ -3034,6 +3149,40 @@
 	}
 #endif /* CONFIG_SUITEB */
 
+	if (flags & TLS_CONN_TEAP_ANON_DH) {
+#ifndef TEAP_DH_ANON_CS
+#define TEAP_DH_ANON_CS \
+	"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:" \
+	"ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:" \
+	"ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:" \
+	"DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" \
+	"DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:" \
+	"DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:" \
+	"ADH-AES256-GCM-SHA384:ADH-AES128-GCM-SHA256:" \
+	"ADH-AES256-SHA256:ADH-AES128-SHA256:ADH-AES256-SHA:ADH-AES128-SHA"
+#endif
+		static const char *cs = TEAP_DH_ANON_CS;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+		/*
+		 * Need to drop to security level 0 to allow anonymous
+		 * cipher suites for EAP-TEAP.
+		 */
+		SSL_set_security_level(conn->ssl, 0);
+#endif
+
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Enable cipher suites for anonymous EAP-TEAP provisioning: %s",
+			   cs);
+		if (SSL_set_cipher_list(conn->ssl, cs) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"Cipher suite configuration failed");
+			return -1;
+		}
+	}
+
 	return 0;
 }
 
@@ -3916,6 +4065,7 @@
 	int cipher, digest;
 	const EVP_CIPHER *c;
 	const EVP_MD *h;
+	int mac_key_len, enc_key_len, fixed_iv_len;
 
 	ssl_cipher = SSL_get_current_cipher(ssl);
 	if (!ssl_cipher)
@@ -3926,17 +4076,33 @@
 		   cipher, digest);
 	if (cipher < 0 || digest < 0)
 		return -1;
-	c = EVP_get_cipherbynid(cipher);
-	h = EVP_get_digestbynid(digest);
-	if (!c || !h)
+	if (cipher == NID_undef) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: no cipher in use?!");
 		return -1;
+	}
+	c = EVP_get_cipherbynid(cipher);
+	if (!c)
+		return -1;
+	enc_key_len = EVP_CIPHER_key_length(c);
+	if (EVP_CIPHER_mode(c) == EVP_CIPH_GCM_MODE ||
+	    EVP_CIPHER_mode(c) == EVP_CIPH_CCM_MODE)
+		fixed_iv_len = 4; /* only part of IV from PRF */
+	else
+		fixed_iv_len = EVP_CIPHER_iv_length(c);
+	if (digest == NID_undef) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: no digest in use (e.g., AEAD)");
+		mac_key_len = 0;
+	} else {
+		h = EVP_get_digestbynid(digest);
+		if (!h)
+			return -1;
+		mac_key_len = EVP_MD_size(h);
+	}
 
 	wpa_printf(MSG_DEBUG,
-		   "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d",
-		   EVP_CIPHER_key_length(c), EVP_MD_size(h),
-		   EVP_CIPHER_iv_length(c));
-	return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) +
-		    EVP_CIPHER_iv_length(c));
+		   "OpenSSL: keyblock size: mac_key_len=%d enc_key_len=%d fixed_iv_len=%d",
+		   mac_key_len, enc_key_len, fixed_iv_len);
+	return 2 * (mac_key_len + enc_key_len + fixed_iv_len);
 #endif
 }
 #endif /* OPENSSL_NEED_EAP_FAST_PRF */
@@ -4020,7 +4186,7 @@
 				    _out, skip + out_len) == 0) {
 		ret = 0;
 	}
-	os_memset(master_key, 0, sizeof(master_key));
+	forced_memzero(master_key, sizeof(master_key));
 	os_free(rnd);
 	if (ret == 0)
 		os_memcpy(out, _out + skip, out_len);
@@ -4210,6 +4376,22 @@
 		wpa_printf(MSG_DEBUG,
 			   "OpenSSL: Handshake finished - resumed=%d",
 			   tls_connection_resumed(conn->ssl_ctx, conn));
+		if (conn->server) {
+			char *buf;
+			size_t buflen = 2000;
+
+			buf = os_malloc(buflen);
+			if (buf) {
+				if (SSL_get_shared_ciphers(conn->ssl, buf,
+							   buflen)) {
+					buf[buflen - 1] = '\0';
+					wpa_printf(MSG_DEBUG,
+						   "OpenSSL: Shared ciphers: %s",
+						   buf);
+				}
+				os_free(buf);
+			}
+		}
 		if (appl_data && in_data)
 			*appl_data = openssl_get_appl_data(conn,
 							   wpabuf_len(in_data));
@@ -4392,11 +4574,15 @@
 
 		c++;
 	}
+	if (!buf[0]) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No ciphers listed");
+		return -1;
+	}
 
 	wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
 
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
 	if (os_strstr(buf, ":ADH-")) {
 		/*
 		 * Need to drop to security level 0 to allow anonymous
@@ -4407,7 +4593,7 @@
 		/* Force at least security level 1 */
 		SSL_set_security_level(conn->ssl, 1);
 	}
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
 #endif
 
 	if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
@@ -4461,7 +4647,7 @@
 }
 
 
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
 /* ClientHello TLS extensions require a patch to openssl, so this function is
  * commented out unless explicitly needed for EAP-FAST in order to be able to
  * build this file with unmodified openssl. */
@@ -4478,7 +4664,7 @@
 
 	return 0;
 }
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
 
 
 int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
@@ -4541,41 +4727,6 @@
 }
 
 
-static void debug_print_cert(X509 *cert, const char *title)
-{
-#ifndef CONFIG_NO_STDOUT_DEBUG
-	BIO *out;
-	size_t rlen;
-	char *txt;
-	int res;
-
-	if (wpa_debug_level > MSG_DEBUG)
-		return;
-
-	out = BIO_new(BIO_s_mem());
-	if (!out)
-		return;
-
-	X509_print(out, cert);
-	rlen = BIO_ctrl_pending(out);
-	txt = os_malloc(rlen + 1);
-	if (!txt) {
-		BIO_free(out);
-		return;
-	}
-
-	res = BIO_read(out, txt, rlen);
-	if (res > 0) {
-		txt[res] = '\0';
-		wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt);
-	}
-	os_free(txt);
-
-	BIO_free(out);
-#endif /* CONFIG_NO_STDOUT_DEBUG */
-}
-
-
 static int ocsp_resp_cb(SSL *s, void *arg)
 {
 	struct tls_connection *conn = arg;
@@ -4687,6 +4838,7 @@
 	res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
 				    &this_update, &next_update);
 	if (!res) {
+		OCSP_CERTID_free(id);
 		id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
 		if (!id) {
 			wpa_printf(MSG_DEBUG,
@@ -4997,6 +5149,114 @@
 }
 
 
+static void openssl_debug_dump_cipher_list(SSL_CTX *ssl_ctx)
+{
+	SSL *ssl;
+	int i;
+
+	ssl = SSL_new(ssl_ctx);
+	if (!ssl)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Enabled cipher suites in priority order");
+	for (i = 0; ; i++) {
+		const char *cipher;
+
+		cipher = SSL_get_cipher_list(ssl, i);
+		if (!cipher)
+			break;
+		wpa_printf(MSG_DEBUG, "Cipher %d: %s", i, cipher);
+	}
+
+	SSL_free(ssl);
+}
+
+
+#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION)
+
+static const char * openssl_pkey_type_str(const EVP_PKEY *pkey)
+{
+	if (!pkey)
+		return "NULL";
+	switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) {
+	case EVP_PKEY_RSA:
+		return "RSA";
+	case EVP_PKEY_DSA:
+		return "DSA";
+	case EVP_PKEY_DH:
+		return "DH";
+	case EVP_PKEY_EC:
+		return "EC";
+	}
+	return "?";
+}
+
+
+static void openssl_debug_dump_certificate(int i, X509 *cert)
+{
+	char buf[256];
+	EVP_PKEY *pkey;
+	ASN1_INTEGER *ser;
+	char serial_num[128];
+
+	X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
+
+	ser = X509_get_serialNumber(cert);
+	if (ser)
+		wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
+					   ASN1_STRING_get0_data(ser),
+					   ASN1_STRING_length(ser));
+	else
+		serial_num[0] = '\0';
+
+	pkey = X509_get_pubkey(cert);
+	wpa_printf(MSG_DEBUG, "%d: %s (%s) %s", i, buf,
+		   openssl_pkey_type_str(pkey), serial_num);
+	EVP_PKEY_free(pkey);
+}
+
+
+static void openssl_debug_dump_certificates(SSL_CTX *ssl_ctx)
+{
+	STACK_OF(X509) *certs;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: Configured certificate chain");
+	if (SSL_CTX_get0_chain_certs(ssl_ctx, &certs) == 1) {
+		int i;
+
+		for (i = sk_X509_num(certs); i > 0; i--)
+			openssl_debug_dump_certificate(i, sk_X509_value(certs,
+									i - 1));
+	}
+	openssl_debug_dump_certificate(0, SSL_CTX_get0_certificate(ssl_ctx));
+}
+
+#endif
+
+
+static void openssl_debug_dump_certificate_chains(SSL_CTX *ssl_ctx)
+{
+#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION)
+	int res;
+
+	for (res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST);
+	     res == 1;
+	     res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_NEXT))
+		openssl_debug_dump_certificates(ssl_ctx);
+
+	SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST);
+#endif
+}
+
+
+static void openssl_debug_dump_ctx(SSL_CTX *ssl_ctx)
+{
+	openssl_debug_dump_cipher_list(ssl_ctx);
+	openssl_debug_dump_certificate_chains(ssl_ctx);
+}
+
+
 int tls_global_set_params(void *tls_ctx,
 			  const struct tls_connection_params *params)
 {
@@ -5022,6 +5282,9 @@
 	    tls_global_client_cert(data, params->client_cert) ||
 	    tls_global_private_key(data, params->private_key,
 				   params->private_key_passwd) ||
+	    tls_global_client_cert(data, params->client_cert2) ||
+	    tls_global_private_key(data, params->private_key2,
+				   params->private_key_passwd2) ||
 	    tls_global_dh(data, params->dh_file)) {
 		wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
 		return -1;
@@ -5091,11 +5354,13 @@
 		tls_global->ocsp_stapling_response = NULL;
 #endif /* HAVE_OCSP */
 
+	openssl_debug_dump_ctx(ssl_ctx);
+
 	return 0;
 }
 
 
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
 /* Pre-shared secred requires a patch to openssl, so this function is
  * commented out unless explicitly needed for EAP-FAST in order to be able to
  * build this file with unmodified openssl. */
@@ -5176,7 +5441,7 @@
 
 	return 1;
 }
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
 
 
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
@@ -5184,7 +5449,7 @@
 					 tls_session_ticket_cb cb,
 					 void *ctx)
 {
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
 	conn->session_ticket_cb = cb;
 	conn->session_ticket_cb_ctx = ctx;
 
@@ -5201,9 +5466,9 @@
 	}
 
 	return 0;
-#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#else /* EAP_FAST_OR_TEAP */
 	return -1;
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
 }
 
 
@@ -5286,3 +5551,36 @@
 		wpa_printf(MSG_DEBUG,
 			   "OpenSSL: Removed cached session to disable session resumption");
 }
+
+
+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
+{
+	size_t len;
+	int reused;
+
+	reused = SSL_session_reused(conn->ssl);
+	if ((conn->server && !reused) || (!conn->server && reused))
+		len = SSL_get_peer_finished(conn->ssl, buf, max_len);
+	else
+		len = SSL_get_finished(conn->ssl, buf, max_len);
+
+	if (len == 0 || len > max_len)
+		return -1;
+
+	return len;
+}
+
+
+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
+{
+	const SSL_CIPHER *cipher;
+
+	cipher = SSL_get_current_cipher(conn->ssl);
+	if (!cipher)
+		return 0;
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+	return SSL_CIPHER_get_protocol_id(cipher);
+#else
+	return SSL_CIPHER_get_id(cipher) & 0xFFFF;
+#endif
+}
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index e9cb425..d222d14 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -141,7 +141,7 @@
 	if (get > (wpabuf_len(data->in_data) - data->consumed))
 		get = wpabuf_len(data->in_data) - data->consumed;
 
-	os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get);
+	os_memcpy(buf, wpabuf_head_u8(data->in_data) + data->consumed, get);
 	data->consumed += get;
 
 	if (get == 0)
@@ -2044,7 +2044,7 @@
 				       _out, skip + out_len);
 	}
 
-	os_memset(master_key, 0, master_key_len);
+	forced_memzero(master_key, master_key_len);
 	if (ret == 0)
 		os_memcpy(out, _out + skip, out_len);
 	bin_clear_free(tmp_out, skip + out_len);
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index e7c8f31..f0e2a6f 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -102,6 +102,20 @@
 };
 
 /**
+ * struct hostapd_wmm_rule - WMM regulatory rule
+ * @min_cwmin: Lower bound of CW_min value
+ * @min_cwmax: Lower bound of CW_max value
+ * @min_aifs: Lower bound of AIFS value
+ * @max_txop: Upper bound of TXOP, value in units of 32 usec
+ */
+struct hostapd_wmm_rule {
+	int min_cwmin;
+	int min_cwmax;
+	int min_aifs;
+	int max_txop;
+};
+
+/**
  * struct hostapd_channel_data - Channel information
  */
 struct hostapd_channel_data {
@@ -156,34 +170,66 @@
 	 * dfs_cac_ms - DFS CAC time in milliseconds
 	 */
 	unsigned int dfs_cac_ms;
+
+	/**
+	 * wmm_rules_valid - Indicates wmm_rules state
+	 */
+	int wmm_rules_valid;
+
+	/**
+	 * wmm_rules - WMM regulatory rules
+	 */
+	struct hostapd_wmm_rule wmm_rules[WMM_AC_NUM];
 };
 
-#define HE_MAX_NUM_SS 		8
-#define HE_MAX_PHY_CAPAB_SIZE	3
-
-/**
- * struct he_ppe_threshold - IEEE 802.11ax HE PPE Threshold
- */
-struct he_ppe_threshold {
-	u32 numss_m1;
-	u32 ru_count;
-	u32 ppet16_ppet8_ru3_ru0[HE_MAX_NUM_SS];
-};
+#define HE_MAX_MAC_CAPAB_SIZE	6
+#define HE_MAX_PHY_CAPAB_SIZE	11
+#define HE_MAX_MCS_CAPAB_SIZE	12
+#define HE_MAX_PPET_CAPAB_SIZE	25
 
 /**
  * struct he_capabilities - IEEE 802.11ax HE capabilities
  */
 struct he_capabilities {
 	u8 he_supported;
-	u32 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
-	u32 mac_cap;
-	u32 mcs;
-	struct he_ppe_threshold ppet;
+	u8 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
+	u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE];
+	u8 mcs[HE_MAX_MCS_CAPAB_SIZE];
+	u8 ppet[HE_MAX_PPET_CAPAB_SIZE];
 };
 
 #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
 #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
 
+
+enum ieee80211_op_mode {
+	IEEE80211_MODE_INFRA = 0,
+	IEEE80211_MODE_IBSS = 1,
+	IEEE80211_MODE_AP = 2,
+	IEEE80211_MODE_MESH = 5,
+
+	/* only add new entries before IEEE80211_MODE_NUM */
+	IEEE80211_MODE_NUM
+};
+
+/**
+ * struct ieee80211_edmg_config - EDMG configuration
+ *
+ * This structure describes most essential parameters needed
+ * for IEEE 802.11ay EDMG configuration
+ *
+ * @channels: Bitmap that indicates the 2.16 GHz channel(s)
+ *	that are allowed to be used for transmissions.
+ *	Bit 0 indicates channel 1, bit 1 indicates channel 2, etc.
+ *	Set to 0 to indicate EDMG not supported.
+ * @bw_config: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations
+ */
+struct ieee80211_edmg_config {
+	u8 channels;
+	enum edmg_bw_config bw_config;
+};
+
 /**
  * struct hostapd_hw_modes - Supported hardware mode information
  */
@@ -243,15 +289,16 @@
 	/**
 	 * he_capab - HE (IEEE 802.11ax) capabilities
 	 */
-	struct he_capabilities he_capab;
+	struct he_capabilities he_capab[IEEE80211_MODE_NUM];
+
+	/**
+	 * This structure describes the most essential parameters needed
+	 * for IEEE 802.11ay EDMG configuration.
+	 */
+	struct ieee80211_edmg_config edmg;
 };
 
 
-#define IEEE80211_MODE_INFRA	0
-#define IEEE80211_MODE_IBSS	1
-#define IEEE80211_MODE_AP	2
-#define IEEE80211_MODE_MESH	5
-
 #define IEEE80211_CAP_ESS	0x0001
 #define IEEE80211_CAP_IBSS	0x0002
 #define IEEE80211_CAP_PRIVACY	0x0010
@@ -470,7 +517,7 @@
 	 * mac_addr - MAC address used with randomization. The address cannot be
 	 * a multicast one, i.e., bit 0 of byte 0 should not be set.
 	 */
-	const u8 *mac_addr;
+	u8 *mac_addr;
 
 	/**
 	 * mac_addr_mask - MAC address mask used with randomization.
@@ -699,6 +746,11 @@
 	int vht_enabled;
 
 	/**
+	 * he_enabled - Whether HE is enabled
+	 */
+	int he_enabled;
+
+	/**
 	 * center_freq1 - Segment 0 center frequency in MHz
 	 *
 	 * Valid for both HT and VHT.
@@ -716,6 +768,12 @@
 	 * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
 	 */
 	int bandwidth;
+
+	/**
+	 * This structure describes the most essential parameters needed
+	 * for IEEE 802.11ay EDMG configuration.
+	 */
+	struct ieee80211_edmg_config edmg;
 };
 
 /**
@@ -1046,6 +1104,14 @@
 	int req_key_mgmt_offload;
 
 	/**
+	 * req_handshake_offload - Request EAPOL handshake offload
+	 *
+	 * Request EAPOL handshake offload for this connection if the device
+	 * supports it.
+	 */
+	int req_handshake_offload;
+
+	/**
 	 * Flag for indicating whether this association includes support for
 	 * RRM (Radio Resource Measurements)
 	 */
@@ -1122,6 +1188,11 @@
 	HIDDEN_SSID_ZERO_CONTENTS
 };
 
+enum ch_switch_state {
+	CH_SW_STARTED,
+	CH_SW_FINISHED
+};
+
 struct wowlan_triggers {
 	u8 any;
 	u8 disconnect;
@@ -1401,6 +1472,21 @@
 	 * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13
 	 */
 	const struct wpabuf *civic;
+
+	/**
+	 * he_spr - Whether Spatial Reuse is enabled
+	 */
+	 int he_spr;
+
+	/**
+	 * he_spr_srg_obss_pd_min_offset - Minimum TX power offset
+	 */
+	 int he_spr_srg_obss_pd_min_offset;
+
+	/**
+	 * he_spr_srg_obss_pd_max_offset - Maximum TX power offset
+	 */
+	 int he_spr_srg_obss_pd_max_offset;
 };
 
 struct wpa_driver_mesh_bss_params {
@@ -1752,6 +1838,7 @@
 struct hostap_sta_driver_data {
 	unsigned long rx_packets, tx_packets;
 	unsigned long long rx_bytes, tx_bytes;
+	unsigned long long rx_airtime, tx_airtime;
 	int bytes_64bit; /* whether 64-bit byte counters are supported */
 	unsigned long current_tx_rate;
 	unsigned long current_rx_rate;
@@ -1761,6 +1848,8 @@
 	unsigned long tx_retry_failed;
 	unsigned long tx_retry_count;
 	s8 last_ack_rssi;
+	unsigned long backlog_packets;
+	unsigned long backlog_bytes;
 	s8 signal;
 	u8 rx_vhtmcs;
 	u8 tx_vhtmcs;
@@ -1781,6 +1870,8 @@
 	const struct ieee80211_vht_capabilities *vht_capabilities;
 	int vht_opmode_enabled;
 	u8 vht_opmode;
+	const struct ieee80211_he_capabilities *he_capab;
+	size_t he_capab_len;
 	u32 flags; /* bitmask of WPA_STA_* flags */
 	u32 flags_mask; /* unset bits in flags */
 #ifdef CONFIG_MESH
@@ -2101,9 +2192,7 @@
 	/* Configured ACS channel width */
 	u16 ch_width;
 
-	/* ACS channel list info */
-	unsigned int ch_list_len;
-	const u8 *ch_list;
+	/* ACS frequency list info */
 	const int *freq_list;
 };
 
@@ -2337,7 +2426,7 @@
 	 *
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
+	int (*deauthenticate)(void *priv, const u8 *addr, u16 reason_code);
 
 	/**
 	 * associate - Request driver to associate
@@ -2806,7 +2895,7 @@
 	 * a Deauthentication frame to be sent to it.
 	 */
 	int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
-			  int reason);
+			  u16 reason);
 
 	/**
 	 * sta_disassoc - Disassociate a station (AP only)
@@ -2820,7 +2909,7 @@
 	 * a Disassociation frame to be sent to it.
 	 */
 	int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
-			    int reason);
+			    u16 reason);
 
 	/**
 	 * sta_remove - Remove a station entry (AP only)
@@ -2938,6 +3027,16 @@
 			     unsigned int flags_and);
 
 	/**
+	 * sta_set_airtime_weight - Set station airtime weight (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * @weight: New weight for station airtime assignment
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_set_airtime_weight)(void *priv, const u8 *addr,
+				      unsigned int weight);
+
+	/**
 	 * set_tx_queue_params - Set TX queue parameters
 	 * @priv: Private driver interface data
 	 * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
@@ -3975,6 +4074,18 @@
 	int (*leave_mesh)(void *priv);
 
 	/**
+	 * probe_mesh_link - Inject a frame over direct mesh link to a given
+	 *	peer skipping the next_hop lookup from mpath table.
+	 * @priv: Private driver interface data
+	 * @addr: Peer MAC address
+	 * @eth: Ethernet frame to be sent
+	 * @len: Ethernet frame lengtn in bytes
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*probe_mesh_link)(void *priv, const u8 *addr, const u8 *eth,
+			       size_t len);
+
+	/**
 	 * do_acs - Automatically select channel
 	 * @priv: Private driver interface data
 	 * @params: Parameters for ACS
@@ -4167,6 +4278,21 @@
 	 * Returns: 0 on success, < 0 on failure
 	 */
 	int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val);
+
+	/**
+	 * update_dh_ie - Update DH IE
+	 * @priv: Private driver interface data
+	 * @peer_mac: Peer MAC address
+	 * @reason_code: Reacon code
+	 * @ie: DH IE
+	 * @ie_len: DH IE length in bytes
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback is used to let the driver know the DH processing result
+	 * and DH IE for a pending association.
+	 */
+	int (*update_dh_ie)(void *priv, const u8 *peer_mac, u16 reason_code,
+			    const u8 *ie, size_t ie_len);
 };
 
 /**
@@ -4541,6 +4667,15 @@
 	EVENT_CH_SWITCH,
 
 	/**
+	 * EVENT_CH_SWITCH_STARTED - AP or GO started to switch channels
+	 *
+	 * This is a pre-switch event indicating the shortly following switch
+	 * of operating channels.
+	 *
+	 * Described in wpa_event_data.ch_switch
+	 */
+	EVENT_CH_SWITCH_STARTED,
+	/**
 	 * EVENT_WNM - Request WNM operation
 	 *
 	 * This event can be used to request a WNM operation to be performed.
@@ -4703,6 +4838,11 @@
 	 * This event is emitted when an interface is added/removed for WDS STA.
 	 */
 	EVENT_WDS_STA_INTERFACE_STATUS,
+
+	/**
+	  * EVENT_UPDATE_DH - Notification of updated DH information
+	  */
+	EVENT_UPDATE_DH,
 };
 
 
@@ -5467,8 +5607,8 @@
 
 	/**
 	 * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
-	 * @pri_channel: Selected primary channel
-	 * @sec_channel: Selected secondary channel
+	 * @pri_freq: Selected primary frequency
+	 * @sec_freq: Selected secondary frequency
 	 * @vht_seg0_center_ch: VHT mode Segment0 center channel
 	 * @vht_seg1_center_ch: VHT mode Segment1 center channel
 	 * @ch_width: Selected Channel width by driver. Driver may choose to
@@ -5477,8 +5617,8 @@
 	 * hw_mode: Selected band (used with hw_mode=any)
 	 */
 	struct acs_selected_channels {
-		u8 pri_channel;
-		u8 sec_channel;
+		unsigned int pri_freq;
+		unsigned int sec_freq;
 		u8 vht_seg0_center_ch;
 		u8 vht_seg1_center_ch;
 		u16 ch_width;
@@ -5536,6 +5676,15 @@
 			INTERFACE_REMOVED
 		} istatus;
 	} wds_sta_interface;
+
+	/**
+	 * struct update_dh - Data for EVENT_UPDATE_DH
+	 */
+	struct update_dh {
+		const u8 *peer;
+		const u8 *ie;
+		size_t ie_len;
+	} update_dh;
 };
 
 /**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index 807cd94..eac3ae8 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -59,10 +59,6 @@
 #include "netlink.h"
 #include "linux_ioctl.h"
 
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) || defined(CONFIG_FILS)
-#define ATHEROS_USE_RAW_RECEIVE
-#endif
-
 
 struct atheros_driver_data {
 	struct hostapd_data *hapd;		/* back pointer */
@@ -86,7 +82,7 @@
 };
 
 static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-			      int reason_code);
+			      u16 reason_code);
 static int atheros_set_privacy(void *priv, int enabled);
 
 static const char * athr_get_ioctl_name(int op)
@@ -366,13 +362,11 @@
 	v = 0;
 	if (params->rsn_preauth)
 		v |= BIT(0);
-#ifdef CONFIG_IEEE80211W
 	if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		v |= BIT(7);
 		if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
 			v |= BIT(6);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
 	if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
@@ -534,7 +528,6 @@
 		cipher = IEEE80211_CIPHER_AES_GCM_256;
 		break;
 #endif /* ATH_GCM_SUPPORT */
-#ifdef CONFIG_IEEE80211W
 	case WPA_ALG_IGTK:
 		cipher = IEEE80211_CIPHER_AES_CMAC;
 		break;
@@ -549,7 +542,6 @@
 		cipher = IEEE80211_CIPHER_AES_GMAC_256;
 		break;
 #endif /* ATH_GCM_SUPPORT */
-#endif /* CONFIG_IEEE80211W */
 	default:
 		wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d",
 			   __func__, alg);
@@ -761,7 +753,7 @@
 
 static int
 atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-		   int reason_code)
+		   u16 reason_code)
 {
 	struct atheros_driver_data *drv = priv;
 	struct ieee80211req_mlme mlme;
@@ -785,7 +777,7 @@
 
 static int
 atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
-		     int reason_code)
+		     u16 reason_code)
 {
 	struct atheros_driver_data *drv = priv;
 	struct ieee80211req_mlme mlme;
@@ -856,7 +848,7 @@
 	return 0;
 }
 
-#ifdef ATHEROS_USE_RAW_RECEIVE
+
 static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 				size_t len)
 {
@@ -953,7 +945,7 @@
 		break;
 	}
 }
-#endif /* ATHEROS_USE_RAW_RECEIVE */
+
 
 static int atheros_receive_pkt(struct atheros_driver_data *drv)
 {
@@ -965,11 +957,9 @@
 #ifdef CONFIG_WPS
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
 #endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
 			       IEEE80211_FILTER_TYPE_AUTH |
 			       IEEE80211_FILTER_TYPE_ACTION);
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
 #ifdef CONFIG_WNM
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
 #endif /* CONFIG_WNM */
@@ -1069,7 +1059,6 @@
 #define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 static int
 atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params)
 {
@@ -1169,7 +1158,7 @@
 	}
 	return ret;
 }
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
+
 
 static void
 atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1315,7 +1304,6 @@
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
 #endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	} else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) {
 		/* Format: "Manage.assoc_req <frame len>" | zero padding |
 		 * frame */
@@ -1339,8 +1327,6 @@
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R || CONFIG_FILS */
-#ifdef ATHEROS_USE_RAW_RECEIVE
 	} else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
 		/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
 		 */
@@ -1353,7 +1339,6 @@
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* ATHEROS_USE_RAW_RECEIVE */
 	}
 }
 
@@ -1973,8 +1958,6 @@
 }
 
 
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
-
 static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
 			     int noack, unsigned int freq,
 			     const u16 *csa_offs, size_t csa_offs_len)
@@ -1999,7 +1982,6 @@
 	return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
 			    sizeof(struct ieee80211req_mgmtbuf) + data_len);
 }
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
 
 
 #ifdef CONFIG_IEEE80211R
@@ -2283,11 +2265,9 @@
 	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
 	.set_authmode		= atheros_set_authmode,
 	.set_ap			= atheros_set_ap,
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
 	.sta_assoc              = atheros_sta_assoc,
 	.sta_auth               = atheros_sta_auth,
 	.send_mlme       	= atheros_send_mgmt,
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R
 	.add_tspec      	= atheros_add_tspec,
 	.add_sta_node    	= atheros_add_sta_node,
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 4675496..8667ee5 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -663,7 +663,7 @@
 #undef WPA_OUI_TYPE
 
 static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-			  int reason_code);
+			  u16 reason_code);
 
 static const char *
 ether_sprintf(const u8 *addr)
@@ -755,7 +755,7 @@
 }
 
 static int
-bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code)
+bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code)
 {
 	return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
 				   addr);
@@ -763,7 +763,7 @@
 
 static int
 bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
-		 int reason_code)
+		 u16 reason_code)
 {
 	return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
 				   addr);
@@ -1026,7 +1026,7 @@
 }
 
 static int
-wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
+wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, u16 reason_code)
 {
 	return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
 				   addr);
@@ -1661,6 +1661,17 @@
 bsd_global_init(void *ctx)
 {
 	struct bsd_driver_global *global;
+#if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER)
+	unsigned char msgfilter[] = {
+		RTM_IEEE80211,
+#ifndef HOSTAPD
+		RTM_IFINFO, RTM_IFANNOUNCE,
+#endif
+	};
+#endif
+#ifdef ROUTE_MSGFILTER
+	unsigned int i, msgfilter_mask;
+#endif
 
 	global = os_zalloc(sizeof(*global));
 	if (global == NULL)
@@ -1683,6 +1694,21 @@
 		goto fail;
 	}
 
+#if defined(RO_MSGFILTER)
+	if (setsockopt(global->route, PF_ROUTE, RO_MSGFILTER,
+	    &msgfilter, sizeof(msgfilter)) < 0)
+		wpa_printf(MSG_ERROR, "socket[PF_ROUTE,RO_MSGFILTER]: %s",
+			   strerror(errno));
+#elif defined(ROUTE_MSGFILTER)
+	msgfilter_mask = 0;
+	for (i = 0; i < (sizeof(msgfilter) / sizeof(msgfilter[0])); i++)
+		msgfilter_mask |= ROUTE_FILTER(msgfilter[i]);
+	if (setsockopt(global->route, PF_ROUTE, ROUTE_MSGFILTER,
+	    &msgfilter_mask, sizeof(msgfilter_mask)) < 0)
+		wpa_printf(MSG_ERROR, "socket[PF_ROUTE,ROUTE_MSGFILTER]: %s",
+			   strerror(errno));
+#endif
+
 	global->event_buf_len = rtbuf_len();
 	global->event_buf = os_malloc(global->event_buf_len);
 	if (global->event_buf == NULL) {
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index e55e6cd..731c6a3 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -67,6 +67,7 @@
 	E2S(DRIVER_CLIENT_POLL_OK);
 	E2S(EAPOL_TX_STATUS);
 	E2S(CH_SWITCH);
+	E2S(CH_SWITCH_STARTED);
 	E2S(WNM);
 	E2S(CONNECT_FAILED_REASON);
 	E2S(DFS_RADAR_DETECTED);
@@ -87,6 +88,7 @@
 	E2S(STATION_OPMODE_CHANGED);
 	E2S(INTERFACE_MAC_CHANGED);
 	E2S(WDS_STA_INTERFACE_STATUS);
+	E2S(UPDATE_DH);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 61b39b1..186eccb 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -1028,7 +1028,7 @@
 
 
 static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-			     int reason)
+			     u16 reason)
 {
 	struct hostap_driver_data *drv = priv;
 	struct ieee80211_mgmt mgmt;
@@ -1076,7 +1076,7 @@
 
 
 static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
-			       int reason)
+			       u16 reason)
 {
 	struct hostap_driver_data *drv = priv;
 	struct ieee80211_mgmt mgmt;
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index 9d981bb..e922503 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -1,6 +1,7 @@
 /*
  * Driver interaction with Linux MACsec kernel module
  * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
+ * Copyright (c) 2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,6 +23,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "common/eapol_common.h"
 #include "pae/ieee802_1x_kay.h"
 #include "driver.h"
 #include "driver_wired_common.h"
@@ -57,6 +59,7 @@
 	char ifname[IFNAMSIZ + 1];
 	int ifi;
 	int parent_ifi;
+	int use_pae_group_addr;
 
 	Boolean created_link;
 
@@ -1399,6 +1402,214 @@
 }
 
 
+#ifdef __linux__
+
+static void macsec_drv_handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+	struct ieee8023_hdr *hdr;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	/* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
+	 * 2 byte ethertype */
+	if (len < 14) {
+		wpa_printf(MSG_MSGDUMP, "%s: too short (%lu)",
+			   __func__, (unsigned long) len);
+		return;
+	}
+
+	hdr = (struct ieee8023_hdr *) buf;
+
+	switch (ntohs(hdr->ethertype)) {
+	case ETH_P_PAE:
+		wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+		sa = hdr->src;
+		os_memset(&event, 0, sizeof(event));
+		event.new_sta.addr = sa;
+		wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+		pos = (u8 *) (hdr + 1);
+		left = len - sizeof(*hdr);
+		drv_event_eapol_rx(ctx, sa, pos, left);
+		break;
+
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+			   ntohs(hdr->ethertype));
+		break;
+	}
+#endif /* HOSTAPD */
+}
+
+
+static void macsec_drv_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "macsec_linux: recv: %s",
+			   strerror(errno));
+		return;
+	}
+
+	macsec_drv_handle_data(eloop_ctx, buf, len);
+}
+
+#endif /* __linux__ */
+
+
+static int macsec_drv_init_sockets(struct macsec_drv_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+
+	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+	if (drv->common.sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->common.sock, macsec_drv_handle_read,
+				     drv->common.ctx, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+	{
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	/* filter multicast address */
+	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
+				       pae_group_addr, 1) < 0) {
+		wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
+			   "membership");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+			   ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+static void * macsec_drv_hapd_init(struct hostapd_data *hapd,
+				   struct wpa_init_params *params)
+{
+	struct macsec_drv_data *drv;
+
+	drv = os_zalloc(sizeof(struct macsec_drv_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for wired driver data");
+		return NULL;
+	}
+
+	drv->common.ctx = hapd;
+	os_strlcpy(drv->common.ifname, params->ifname,
+		   sizeof(drv->common.ifname));
+	drv->use_pae_group_addr = params->use_pae_group_addr;
+
+	if (macsec_drv_init_sockets(drv, params->own_addr)) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void macsec_drv_hapd_deinit(void *priv)
+{
+	struct macsec_drv_data *drv = priv;
+
+	if (drv->common.sock >= 0) {
+		eloop_unregister_read_sock(drv->common.sock);
+		close(drv->common.sock);
+	}
+
+	os_free(drv);
+}
+
+
+static int macsec_drv_send_eapol(void *priv, const u8 *addr,
+				 const u8 *data, size_t data_len, int encrypt,
+				 const u8 *own_addr, u32 flags)
+{
+	struct macsec_drv_data *drv = priv;
+	struct ieee8023_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		wpa_printf(MSG_INFO,
+			   "%s: malloc() failed (len=%lu)",
+			   __func__, (unsigned long) len);
+		return -1;
+	}
+
+	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+		  ETH_ALEN);
+	os_memcpy(hdr->src, own_addr, ETH_ALEN);
+	hdr->ethertype = htons(ETH_P_PAE);
+
+	pos = (u8 *) (hdr + 1);
+	os_memcpy(pos, data, data_len);
+
+	res = send(drv->common.sock, (u8 *) hdr, len, 0);
+	os_free(hdr);
+
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: packet len: %lu - failed: send: %s",
+			   __func__, (unsigned long) len, strerror(errno));
+	}
+
+	return res;
+}
+
+
 const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
 	.name = "macsec_linux",
 	.desc = "MACsec Ethernet driver for Linux",
@@ -1407,6 +1618,9 @@
 	.get_capa = driver_wired_get_capa,
 	.init = macsec_drv_wpa_init,
 	.deinit = macsec_drv_wpa_deinit,
+	.hapd_init = macsec_drv_hapd_init,
+	.hapd_deinit = macsec_drv_hapd_deinit,
+	.hapd_send_eapol = macsec_drv_send_eapol,
 
 	.macsec_init = macsec_drv_macsec_init,
 	.macsec_deinit = macsec_drv_macsec_deinit,
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index 8372393..f4e55d5 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -3,6 +3,7 @@
  * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
  * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -29,6 +30,7 @@
 #include "utils/eloop.h"
 #include "common/defs.h"
 #include "common/ieee802_1x_defs.h"
+#include "common/eapol_common.h"
 #include "pae/ieee802_1x_kay.h"
 #include "driver.h"
 #include "driver_wired_common.h"
@@ -64,6 +66,7 @@
 struct macsec_qca_data {
 	struct driver_wired_common_data common;
 
+	int use_pae_group_addr;
 	u32 secy_id;
 
 	/* shadow */
@@ -126,6 +129,134 @@
 }
 
 
+#ifdef __linux__
+
+static void macsec_qca_handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+	struct ieee8023_hdr *hdr;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	/* at least 6 bytes src macaddress, 6 bytes dst macaddress
+	 * and 2 bytes ethertype
+	*/
+	if (len < 14) {
+		wpa_printf(MSG_MSGDUMP,
+			   "macsec_qca_handle_data: too short (%lu)",
+			   (unsigned long) len);
+		return;
+	}
+	hdr = (struct ieee8023_hdr *) buf;
+
+	switch (ntohs(hdr->ethertype)) {
+	case ETH_P_PAE:
+		wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+		sa = hdr->src;
+		os_memset(&event, 0, sizeof(event));
+		event.new_sta.addr = sa;
+		wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+		pos = (u8 *) (hdr + 1);
+		left = len - sizeof(*hdr);
+		drv_event_eapol_rx(ctx, sa, pos, left);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+			   ntohs(hdr->ethertype));
+		break;
+	}
+#endif /* HOSTAPD */
+}
+
+
+static void macsec_qca_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "macsec_qca: recv: %s", strerror(errno));
+		return;
+	}
+
+	macsec_qca_handle_data(eloop_ctx, buf, len);
+}
+
+#endif /* __linux__ */
+
+
+static int macsec_qca_init_sockets(struct macsec_qca_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+
+	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+	if (drv->common.sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->common.sock, macsec_qca_handle_read,
+				     drv->common.ctx, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->common.sock, (struct sockaddr *) &addr,
+		 sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "macsec_qca: bind: %s", strerror(errno));
+		return -1;
+	}
+
+	/* filter multicast address */
+	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
+				       pae_group_addr, 1) < 0) {
+		wpa_printf(MSG_ERROR,
+			"macsec_qca_init_sockets: Failed to add multicast group membership");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+			   ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
 static void * macsec_qca_init(void *ctx, const char *ifname)
 {
 	struct macsec_qca_data *drv;
@@ -160,6 +291,97 @@
 }
 
 
+static void * macsec_qca_hapd_init(struct hostapd_data *hapd,
+				   struct wpa_init_params *params)
+{
+	struct macsec_qca_data *drv;
+
+	drv = os_zalloc(sizeof(struct macsec_qca_data));
+	if (!drv) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for macsec_qca driver data");
+		return NULL;
+	}
+
+	/* Board specific settings */
+	if (os_memcmp("eth2", params->ifname, 4) == 0)
+		drv->secy_id = 1;
+	else if (os_memcmp("eth3", params->ifname, 4) == 0)
+		drv->secy_id = 2;
+	else if (os_memcmp("eth4", params->ifname, 4) == 0)
+		drv->secy_id = 0;
+	else if (os_memcmp("eth5", params->ifname, 4) == 0)
+		drv->secy_id = 1;
+	else
+		drv->secy_id = -1;
+
+	drv->common.ctx = hapd;
+	os_strlcpy(drv->common.ifname, params->ifname,
+		   sizeof(drv->common.ifname));
+	drv->use_pae_group_addr = params->use_pae_group_addr;
+
+	if (macsec_qca_init_sockets(drv, params->own_addr)) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void macsec_qca_hapd_deinit(void *priv)
+{
+	struct macsec_qca_data *drv = priv;
+
+	if (drv->common.sock >= 0) {
+		eloop_unregister_read_sock(drv->common.sock);
+		close(drv->common.sock);
+	}
+
+	os_free(drv);
+}
+
+
+static int macsec_qca_send_eapol(void *priv, const u8 *addr,
+				 const u8 *data, size_t data_len, int encrypt,
+				 const u8 *own_addr, u32 flags)
+{
+	struct macsec_qca_data *drv = priv;
+	struct ieee8023_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + data_len;
+	hdr = os_zalloc(len);
+	if (!hdr) {
+		wpa_printf(MSG_INFO,
+			   "malloc() failed for macsec_qca_send_eapol(len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+
+	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+		  ETH_ALEN);
+	os_memcpy(hdr->src, own_addr, ETH_ALEN);
+	hdr->ethertype = htons(ETH_P_PAE);
+
+	pos = (u8 *) (hdr + 1);
+	os_memcpy(pos, data, data_len);
+
+	res = send(drv->common.sock, (u8 *) hdr, len, 0);
+	os_free(hdr);
+
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "macsec_qca_send_eapol - packet len: %lu - failed: send: %s",
+			   (unsigned long) len, strerror(errno));
+	}
+
+	return res;
+}
+
+
 static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
 {
 	struct macsec_qca_data *drv = priv;
@@ -800,6 +1022,9 @@
 	.get_capa = driver_wired_get_capa,
 	.init = macsec_qca_init,
 	.deinit = macsec_qca_deinit,
+	.hapd_init = macsec_qca_hapd_init,
+	.hapd_deinit = macsec_qca_hapd_deinit,
+	.hapd_send_eapol = macsec_qca_send_eapol,
 
 	.macsec_init = macsec_qca_macsec_init,
 	.macsec_deinit = macsec_qca_macsec_deinit,
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 614c452..5b4b924 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -719,7 +719,7 @@
 
 
 static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
-					  int reason_code)
+					  u16 reason_code)
 {
 	struct wpa_driver_ndis_data *drv = priv;
 	return wpa_driver_ndis_disconnect(drv);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 21d1398..b7efb6a 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -236,7 +236,7 @@
 				   struct wpa_driver_mesh_bss_params *params);
 #endif /* CONFIG_MESH */
 static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
-			     int reason);
+			     u16 reason);
 
 
 /* Converts nl80211_chan_width to a common format */
@@ -2010,9 +2010,8 @@
 	 */
 	drv->set_rekey_offload = 1;
 
-	drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
+	drv->num_if_indices = ARRAY_SIZE(drv->default_if_indices);
 	drv->if_indices = drv->default_if_indices;
-	drv->if_indices_reason = drv->default_if_indices_reason;
 
 	drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
 	if (!drv->first_bss) {
@@ -2241,7 +2240,6 @@
 					  6) < 0)
 		ret = -1;
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_IEEE80211W
 #ifdef CONFIG_OCV
 	/* SA Query Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x00", 2) < 0)
@@ -2250,7 +2248,6 @@
 	/* SA Query Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
 		ret = -1;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_TDLS
 	if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
 		/* TDLS Discovery Response */
@@ -2386,11 +2383,9 @@
 	/* FT Action frames */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
 		ret = -1;
-#ifdef CONFIG_IEEE80211W
 	/* SA Query */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x08", 1) < 0)
 		ret = -1;
-#endif /* CONFIG_IEEE80211W */
 	/* Protected Dual of Public Action */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x09", 1) < 0)
 		ret = -1;
@@ -2789,9 +2784,6 @@
 	if (drv->if_indices != drv->default_if_indices)
 		os_free(drv->if_indices);
 
-	if (drv->if_indices_reason != drv->default_if_indices_reason)
-		os_free(drv->if_indices_reason);
-
 	if (drv->disabled_11b_rates)
 		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
 
@@ -3019,7 +3011,8 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ifindex;
-	struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
+	struct nl_msg *key_msg;
 	int ret;
 	int tdls = 0;
 
@@ -3053,26 +3046,31 @@
 	    (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X))
 		return nl80211_set_pmk(drv, key, key_len, addr);
 
+	key_msg = nlmsg_alloc();
+	if (!key_msg)
+		return -ENOBUFS;
+
 	if (alg == WPA_ALG_NONE) {
 		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
 		if (!msg)
-			return -ENOBUFS;
+			goto fail2;
 	} else {
 		u32 suite;
 
 		suite = wpa_alg_to_cipher_suite(alg, key_len);
 		if (!suite)
-			goto fail;
+			goto fail2;
 		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
-		if (!msg ||
-		    nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) ||
-		    nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, suite))
+		if (!msg)
+			goto fail2;
+		if (nla_put(key_msg, NL80211_KEY_DATA, key_len, key) ||
+		    nla_put_u32(key_msg, NL80211_KEY_CIPHER, suite))
 			goto fail;
 		wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
 	}
 
 	if (seq && seq_len) {
-		if (nla_put(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq))
+		if (nla_put(key_msg, NL80211_KEY_SEQ, seq_len, seq))
 			goto fail;
 		wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len);
 	}
@@ -3084,7 +3082,7 @@
 
 		if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
 			wpa_printf(MSG_DEBUG, "   RSN IBSS RX GTK");
-			if (nla_put_u32(msg, NL80211_ATTR_KEY_TYPE,
+			if (nla_put_u32(key_msg, NL80211_KEY_TYPE,
 					NL80211_KEYTYPE_GROUP))
 				goto fail;
 		}
@@ -3093,14 +3091,18 @@
 
 		wpa_printf(MSG_DEBUG, "   broadcast key");
 
-		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+		types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
 		if (!types ||
-		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+		    nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
 			goto fail;
-		nla_nest_end(msg, types);
+		nla_nest_end(key_msg, types);
 	}
-	if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
+	if (nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
+	    nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
 		goto fail;
+	nl80211_nlmsg_clear(key_msg);
+	nlmsg_free(key_msg);
+	key_msg = NULL;
 
 	ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
 	if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
@@ -3119,34 +3121,46 @@
 	    !is_broadcast_ether_addr(addr))
 		return ret;
 
+	key_msg = nlmsg_alloc();
+	if (!key_msg)
+		return -ENOBUFS;
+
 	msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
-	if (!msg ||
-	    nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) ||
-	    nla_put_flag(msg, (alg == WPA_ALG_IGTK ||
-			       alg == WPA_ALG_BIP_GMAC_128 ||
-			       alg == WPA_ALG_BIP_GMAC_256 ||
-			       alg == WPA_ALG_BIP_CMAC_256) ?
-			 NL80211_ATTR_KEY_DEFAULT_MGMT :
-			 NL80211_ATTR_KEY_DEFAULT))
+	if (!msg)
+		goto fail2;
+	if (!key_msg ||
+	    nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
+	    nla_put_flag(key_msg, (alg == WPA_ALG_IGTK ||
+				   alg == WPA_ALG_BIP_GMAC_128 ||
+				   alg == WPA_ALG_BIP_GMAC_256 ||
+				   alg == WPA_ALG_BIP_CMAC_256) ?
+				   NL80211_KEY_DEFAULT_MGMT :
+				   NL80211_KEY_DEFAULT))
 		goto fail;
 	if (addr && is_broadcast_ether_addr(addr)) {
 		struct nlattr *types;
 
-		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+		types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
 		if (!types ||
-		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+		    nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
 			goto fail;
-		nla_nest_end(msg, types);
+		nla_nest_end(key_msg, types);
 	} else if (addr) {
 		struct nlattr *types;
 
-		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+		types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
 		if (!types ||
-		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
+		    nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
 			goto fail;
-		nla_nest_end(msg, types);
+		nla_nest_end(key_msg, types);
 	}
 
+	if (nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
+		goto fail;
+	nl80211_nlmsg_clear(key_msg);
+	nlmsg_free(key_msg);
+	key_msg = NULL;
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret == -ENOENT)
 		ret = 0;
@@ -3158,6 +3172,9 @@
 fail:
 	nl80211_nlmsg_clear(msg);
 	nlmsg_free(msg);
+fail2:
+	nl80211_nlmsg_clear(key_msg);
+	nlmsg_free(key_msg);
 	return -ENOBUFS;
 }
 
@@ -3282,7 +3299,7 @@
 
 
 static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
-					 int reason_code,
+					 u16 reason_code,
 					 struct nl_handle *nl_connect)
 {
 	int ret;
@@ -3304,7 +3321,7 @@
 
 
 static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
-					     const u8 *addr, int reason_code)
+					     const u8 *addr, u16 reason_code)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ret;
@@ -4171,8 +4188,10 @@
 	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
 		goto fail;
 
-	if (drv->device_ap_sme && (params->key_mgmt_suites & WPA_KEY_MGMT_SAE))
-	     nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT);
+	if (drv->device_ap_sme &&
+	    (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) &&
+	    nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+		goto fail;
 
 	wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
 		   params->pairwise_ciphers);
@@ -4288,6 +4307,23 @@
 		nla_nest_end(msg, ftm);
 	}
 
+#ifdef CONFIG_IEEE80211AX
+	if (params->he_spr) {
+		struct nlattr *spr;
+
+		spr = nla_nest_start(msg, NL80211_ATTR_HE_OBSS_PD);
+		wpa_printf(MSG_DEBUG, "nl80211: he_spr=%d", params->he_spr);
+
+		if (nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
+			       params->he_spr_srg_obss_pd_min_offset) ||
+		    nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
+			       params->he_spr_srg_obss_pd_max_offset))
+			goto fail;
+
+		nla_nest_end(msg, spr);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -4346,14 +4382,23 @@
 static int nl80211_put_freq_params(struct nl_msg *msg,
 				   const struct hostapd_freq_params *freq)
 {
+	enum hostapd_hw_mode hw_mode;
+	int is_24ghz;
+	u8 channel;
+
 	wpa_printf(MSG_DEBUG, "  * freq=%d", freq->freq);
 	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
 		return -ENOBUFS;
 
+	wpa_printf(MSG_DEBUG, "  * he_enabled=%d", freq->he_enabled);
 	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
 	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
 
-	if (freq->vht_enabled) {
+	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+	is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+		hw_mode == HOSTAPD_MODE_IEEE80211B;
+
+	if (freq->vht_enabled || (freq->he_enabled && !is_24ghz)) {
 		enum nl80211_chan_width cw;
 
 		wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
@@ -4409,6 +4454,15 @@
 		wpa_printf(MSG_DEBUG, "  * channel_type=%d", ct);
 		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
 			return -ENOBUFS;
+	} else if (freq->edmg.channels && freq->edmg.bw_config) {
+		wpa_printf(MSG_DEBUG,
+			   "  * EDMG configuration: channels=0x%x bw_config=%d",
+			   freq->edmg.channels, freq->edmg.bw_config);
+		if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+			       freq->edmg.channels) ||
+		    nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+			       freq->edmg.bw_config))
+			return -1;
 	} else {
 		wpa_printf(MSG_DEBUG, "  * channel_type=%d",
 			   NL80211_CHAN_NO_HT);
@@ -4428,8 +4482,8 @@
 	int ret;
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
-		   freq->freq, freq->ht_enabled, freq->vht_enabled,
+		   "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+		   freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled,
 		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
 
 	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
@@ -4561,6 +4615,14 @@
 				goto fail;
 		}
 
+		if (params->he_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * he_capab",
+				    params->he_capab, params->he_capab_len);
+			if (nla_put(msg, NL80211_ATTR_HE_CAPABILITY,
+				    params->he_capab_len, params->he_capab))
+				goto fail;
+		}
+
 		if (params->ext_capab) {
 			wpa_hexdump(MSG_DEBUG, "  * ext_capab",
 				    params->ext_capab, params->ext_capab_len);
@@ -4693,8 +4755,9 @@
 		goto fail;
 #endif /* CONFIG_MESH */
 
-	if ((!params->set || FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) &&
-	    (params->flags & WPA_STA_WMM)) {
+	if ((!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+	     FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) &&
+	     (params->flags & WPA_STA_WMM)) {
 		struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
 
 		wpa_printf(MSG_DEBUG, "  * qosinfo=0x%x", params->qosinfo);
@@ -5185,6 +5248,28 @@
 }
 
 
+static int driver_nl80211_sta_set_airtime_weight(void *priv, const u8 *addr,
+						 unsigned int weight)
+{
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set STA airtime weight - ifname=%s addr=" MACSTR
+		   " weight=%u", bss->ifname, MAC2STR(addr), weight);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u16(msg, NL80211_ATTR_AIRTIME_WEIGHT, weight))
+		goto fail;
+
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
 static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
 				 struct wpa_driver_associate_params *params)
 {
@@ -5457,6 +5542,18 @@
 			return -1;
 	}
 
+	if (params->freq.edmg.channels && params->freq.edmg.bw_config) {
+		wpa_printf(MSG_DEBUG,
+			   "  * EDMG configuration: channels=0x%x bw_config=%d",
+			   params->freq.edmg.channels,
+			   params->freq.edmg.bw_config);
+		if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+			       params->freq.edmg.channels) ||
+		    nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+			       params->freq.edmg.bw_config))
+			return -1;
+	}
+
 	if (params->bg_scan_period >= 0) {
 		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
 			   params->bg_scan_period);
@@ -5603,7 +5700,7 @@
 			return -1;
 	}
 
-	if (params->req_key_mgmt_offload &&
+	if (params->req_handshake_offload &&
 	    (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) {
 		    wpa_printf(MSG_DEBUG, "  * WANT_1X_4WAY_HS");
 		    if (nla_put_flag(msg, NL80211_ATTR_WANT_1X_4WAY_HS))
@@ -5665,7 +5762,8 @@
 	    nl80211_put_fils_connect_params(drv, params, msg) != 0)
 		return -1;
 
-	if ((params->auth_alg & WPA_AUTH_ALG_SAE) &&
+	if ((params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+	     params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) &&
 	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
 	    nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
 		return -1;
@@ -5820,7 +5918,8 @@
 
 		if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
 			return -1;
-		if (params->auth_alg & WPA_AUTH_ALG_SAE) {
+		if (params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+		    params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) {
 			nl_connect = bss->nl_connect;
 			bss->use_nl_connect = 1;
 		} else {
@@ -6264,6 +6363,36 @@
 }
 
 
+static void get_sta_tid_stats(struct hostap_sta_driver_data *data,
+			      struct nlattr *attr)
+{
+	struct nlattr *tid_stats[NL80211_TID_STATS_MAX + 1], *tidattr;
+	struct nlattr *txq_stats[NL80211_TXQ_STATS_MAX + 1];
+	static struct nla_policy txq_stats_policy[NL80211_TXQ_STATS_MAX + 1] = {
+		[NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 },
+		[NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 },
+	};
+	int rem;
+
+	nla_for_each_nested(tidattr, attr, rem) {
+		if (nla_parse_nested(tid_stats, NL80211_TID_STATS_MAX,
+				     tidattr, NULL) != 0 ||
+		    !tid_stats[NL80211_TID_STATS_TXQ_STATS] ||
+		    nla_parse_nested(txq_stats, NL80211_TXQ_STATS_MAX,
+				     tid_stats[NL80211_TID_STATS_TXQ_STATS],
+				     txq_stats_policy) != 0)
+			continue;
+		/* sum the backlogs over all TIDs for station */
+		if (txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES])
+			data->backlog_bytes += nla_get_u32(
+				txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES]);
+		if (txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS])
+			data->backlog_bytes += nla_get_u32(
+				txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS]);
+	}
+}
+
+
 static int get_sta_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -6281,6 +6410,8 @@
 		[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
 		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
 		[NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
 	};
 	struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
 	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -6338,6 +6469,12 @@
 	if (stats[NL80211_STA_INFO_TX_PACKETS])
 		data->tx_packets =
 			nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
+	if (stats[NL80211_STA_INFO_RX_DURATION])
+		data->rx_airtime =
+			nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
+	if (stats[NL80211_STA_INFO_TX_DURATION])
+		data->tx_airtime =
+			nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
 	if (stats[NL80211_STA_INFO_TX_FAILED])
 		data->tx_retry_failed =
 			nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
@@ -6408,6 +6545,9 @@
 		}
 	}
 
+	if (stats[NL80211_STA_INFO_TID_STATS])
+		get_sta_tid_stats(data, stats[NL80211_STA_INFO_TID_STATS]);
+
 	return NL_SKIP;
 }
 
@@ -6548,7 +6688,7 @@
 
 
 static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-			   int reason)
+			   u16 reason)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -6583,7 +6723,7 @@
 
 
 static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
-			     int reason)
+			     u16 reason)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -6618,11 +6758,11 @@
 	end = pos + sizeof(buf);
 
 	for (i = 0; i < drv->num_if_indices; i++) {
-		if (!drv->if_indices[i])
+		if (!drv->if_indices[i].ifindex)
 			continue;
 		res = os_snprintf(pos, end - pos, " %d(%d)",
-				  drv->if_indices[i],
-				  drv->if_indices_reason[i]);
+				  drv->if_indices[i].ifindex,
+				  drv->if_indices[i].reason);
 		if (os_snprintf_error(end - pos, res))
 			break;
 		pos += res;
@@ -6638,7 +6778,7 @@
 		      int ifidx_reason)
 {
 	int i;
-	int *old, *old_reason;
+	struct drv_nl80211_if_info *old;
 
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: Add own interface ifindex %d (ifidx_reason %d)",
@@ -6649,9 +6789,9 @@
 		return;
 	}
 	for (i = 0; i < drv->num_if_indices; i++) {
-		if (drv->if_indices[i] == 0) {
-			drv->if_indices[i] = ifidx;
-			drv->if_indices_reason[i] = ifidx_reason;
+		if (drv->if_indices[i].ifindex == 0) {
+			drv->if_indices[i].ifindex = ifidx;
+			drv->if_indices[i].reason = ifidx_reason;
 			dump_ifidx(drv);
 			return;
 		}
@@ -6662,29 +6802,13 @@
 	else
 		old = NULL;
 
-	if (drv->if_indices_reason != drv->default_if_indices_reason)
-		old_reason = drv->if_indices_reason;
-	else
-		old_reason = NULL;
-
 	drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
-					   sizeof(int));
-	drv->if_indices_reason = os_realloc_array(old_reason,
-						  drv->num_if_indices + 1,
-						  sizeof(int));
+					   sizeof(*old));
 	if (!drv->if_indices) {
 		if (!old)
 			drv->if_indices = drv->default_if_indices;
 		else
 			drv->if_indices = old;
-	}
-	if (!drv->if_indices_reason) {
-		if (!old_reason)
-			drv->if_indices_reason = drv->default_if_indices_reason;
-		else
-			drv->if_indices_reason = old_reason;
-	}
-	if (!drv->if_indices || !drv->if_indices_reason) {
 		wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
 			   "interfaces");
 		wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
@@ -6693,12 +6817,8 @@
 	if (!old)
 		os_memcpy(drv->if_indices, drv->default_if_indices,
 			  sizeof(drv->default_if_indices));
-	if (!old_reason)
-		os_memcpy(drv->if_indices_reason,
-			  drv->default_if_indices_reason,
-			  sizeof(drv->default_if_indices_reason));
-	drv->if_indices[drv->num_if_indices] = ifidx;
-	drv->if_indices_reason[drv->num_if_indices] = ifidx_reason;
+	drv->if_indices[drv->num_if_indices].ifindex = ifidx;
+	drv->if_indices[drv->num_if_indices].reason = ifidx_reason;
 	drv->num_if_indices++;
 	dump_ifidx(drv);
 }
@@ -6710,10 +6830,12 @@
 	int i;
 
 	for (i = 0; i < drv->num_if_indices; i++) {
-		if ((drv->if_indices[i] == ifidx || ifidx == IFIDX_ANY) &&
-		    (drv->if_indices_reason[i] == ifidx_reason ||
+		if ((drv->if_indices[i].ifindex == ifidx ||
+		     ifidx == IFIDX_ANY) &&
+		    (drv->if_indices[i].reason == ifidx_reason ||
 		     ifidx_reason == IFIDX_ANY)) {
-			drv->if_indices[i] = 0;
+			drv->if_indices[i].ifindex = 0;
+			drv->if_indices[i].reason = 0;
 			break;
 		}
 	}
@@ -6727,8 +6849,8 @@
 	int i;
 
 	for (i = 0; i < drv->num_if_indices; i++)
-		if (drv->if_indices[i] == ifidx &&
-		    (drv->if_indices_reason[i] == ifidx_reason ||
+		if (drv->if_indices[i].ifindex == ifidx &&
+		    (drv->if_indices[i].reason == ifidx_reason ||
 		     ifidx_reason == IFIDX_ANY))
 			return 1;
 
@@ -6871,10 +6993,14 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
 		   ifname, brname);
 	if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
-			   "into bridge %s: %s",
+		wpa_printf(MSG_WARNING,
+			   "nl80211: Failed to add interface %s into bridge %s: %s",
 			   ifname, brname, strerror(errno));
-		return -1;
+		/* Try to continue without the interface being in a bridge. This
+		 * may be needed for some cases, e.g., with Open vSwitch, where
+		 * an external component will need to handle bridge
+		 * configuration. */
+		return 0;
 	}
 	bss->added_if_into_bridge = 1;
 
@@ -8370,8 +8496,8 @@
 	struct nl_msg *msg;
 	int ret;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
-		   freq->freq, freq->ht_enabled, freq->vht_enabled,
+	wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+		   freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled,
 		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
 
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
@@ -8583,7 +8709,7 @@
 
 
 static int driver_nl80211_deauthenticate(void *priv, const u8 *addr,
-					 int reason_code)
+					 u16 reason_code)
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code);
@@ -8698,6 +8824,35 @@
 }
 
 
+static int nl80211_update_dh_ie(void *priv, const u8 *peer_mac,
+				u16 reason_code, const u8 *ie, size_t ie_len)
+{
+	int ret;
+	struct nl_msg *msg;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Updating DH IE peer: " MACSTR
+		   " reason %u", MAC2STR(peer_mac), reason_code);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_OWE_INFO)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer_mac) ||
+	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, reason_code) ||
+	    (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: update_dh_ie failed err=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
 static const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
 {
 	struct i802_bss *bss = priv;
@@ -9372,7 +9527,7 @@
 			QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
 	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
 	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
-			QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) ||
+			QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID) ||
 	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
 			WPA_SUPPLICANT_CLIENT_ID) ||
 	    nla_put_u32(msg,
@@ -9407,6 +9562,40 @@
 	return -1;
 }
 
+
+static int nl80211_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+
+	if (!drv->add_sta_node_vendor_cmd_avail)
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Add STA node");
+
+	if (!(msg = nl80211_drv_msg(drv, 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) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    (addr &&
+	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_MAC_ADDR, ETH_ALEN,
+		     addr)) ||
+	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO,
+			auth_alg)) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -9646,6 +9835,36 @@
 	return ret;
 }
 
+
+static int nl80211_probe_mesh_link(void *priv, const u8 *addr, const u8 *eth,
+				   size_t len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_drv_msg(drv, 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)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh link probe to " MACSTR
+			   " failed: ret=%d (%s)",
+			   MAC2STR(addr), ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Mesh link to " MACSTR
+			   " probed successfully", MAC2STR(addr));
+	}
+
+	return ret;
+}
+
 #endif /* CONFIG_MESH */
 
 
@@ -9942,6 +10161,48 @@
 }
 
 
+static int add_acs_ch_list(struct nl_msg *msg, const int *freq_list)
+{
+	int num_channels = 0, num_freqs;
+	u8 *ch_list;
+	enum hostapd_hw_mode hw_mode;
+	int ret = 0;
+	int i;
+
+	if (!freq_list)
+		return 0;
+
+	num_freqs = int_array_len(freq_list);
+	ch_list = os_malloc(sizeof(u8) * num_freqs);
+	if (!ch_list)
+		return -1;
+
+	for (i = 0; i < num_freqs; i++) {
+		const int freq = freq_list[i];
+
+		if (freq == 0)
+			break;
+		/* Send 2.4 GHz and 5 GHz channels with
+		 * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST to maintain backwards
+		 * compatibility.
+		 */
+		if (!(freq >= 2412 && freq <= 2484) &&
+		    !(freq >= 5180 && freq <= 5900))
+			continue;
+		hw_mode = ieee80211_freq_to_chan(freq, &ch_list[num_channels]);
+		if (hw_mode != NUM_HOSTAPD_MODES)
+			num_channels++;
+	}
+
+	if (num_channels)
+		ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+			      num_channels, ch_list);
+
+	os_free(ch_list);
+	return ret;
+}
+
+
 static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
 {
 	int i, len, ret;
@@ -9989,9 +10250,7 @@
 	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
 	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
 			params->ch_width) ||
-	    (params->ch_list_len &&
-	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
-		     params->ch_list)) ||
+	    add_acs_ch_list(msg, params->freq_list) ||
 	    add_acs_freq_list(msg, params->freq_list)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -9999,9 +10258,9 @@
 	nla_nest_end(msg, data);
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u",
+		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d",
 		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
-		   params->vht_enabled, params->ch_width, params->ch_list_len);
+		   params->vht_enabled, params->ch_width);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
@@ -10634,22 +10893,37 @@
 {
 	int fd, len;
 	char tmp[128];
+	int ret = 0;
 
 	fd = open(name, O_RDWR);
 	if (fd < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to open %s: %s",
+		int level;
+		/*
+		 * Flags may not exist on older kernels, or while we're tearing
+		 * down a disappearing device.
+		 */
+		if (errno == ENOENT) {
+			ret = 0;
+			level = MSG_DEBUG;
+		} else {
+			ret = -1;
+			level = MSG_ERROR;
+		}
+		wpa_printf(level, "nl80211: Failed to open %s: %s",
 			   name, strerror(errno));
-		return fd;
+		return ret;
 	}
 
 	len = os_snprintf(tmp, sizeof(tmp), "%u\n", val);
 	len = write(fd, tmp, len);
-	if (len < 0)
+	if (len < 0) {
+		ret = -1;
 		wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s",
 			   name, strerror(errno));
+	}
 	close(fd);
 
-	return 0;
+	return ret;
 }
 
 
@@ -10764,6 +11038,14 @@
 	int ret = -1;
 	enum nl80211_auth_type type;
 
+	/* Update Connection Params is intended for drivers that implement
+	 * internal SME and expect these updated connection params from
+	 * wpa_supplicant. Do not send this request for the drivers using
+	 * SME from wpa_supplicant.
+	 */
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+		return 0;
+
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
 	if (!msg)
 		goto fail;
@@ -10813,11 +11095,11 @@
 	int ret = -1;
 
 	/* External auth command/status is intended for drivers that implement
-	 * intenral SME but want to offload authentication processing (e.g.,
-	 * SAE) to hostapd/wpa_supplicant. Do nott send the status to drivers
+	 * internal SME but want to offload authentication processing (e.g.,
+	 * SAE) to hostapd/wpa_supplicant. Do not send the status to drivers
 	 * which do not support AP SME or use wpa_supplicant/hostapd SME.
 	 */
-	if (!bss->drv->device_ap_sme ||
+	if ((is_ap_interface(drv->nlmode) && !bss->drv->device_ap_sme) ||
 	    (drv->capa.flags & WPA_DRIVER_FLAGS_SME))
 		return -1;
 
@@ -10924,6 +11206,7 @@
 	.sta_remove = driver_nl80211_sta_remove,
 	.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
 	.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
+	.sta_set_airtime_weight = driver_nl80211_sta_set_airtime_weight,
 	.hapd_init = i802_init,
 	.hapd_deinit = i802_deinit,
 	.set_wds_sta = i802_set_wds_sta,
@@ -10969,6 +11252,7 @@
 	.tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
 #endif /* CONFIG_TDLS */
 	.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
+	.update_dh_ie = nl80211_update_dh_ie,
 	.get_mac_addr = wpa_driver_nl80211_get_macaddr,
 	.get_survey = wpa_driver_nl80211_get_survey,
 	.status = wpa_driver_nl80211_status,
@@ -10991,6 +11275,7 @@
 	.init_mesh = wpa_driver_nl80211_init_mesh,
 	.join_mesh = wpa_driver_nl80211_join_mesh,
 	.leave_mesh = wpa_driver_nl80211_leave_mesh,
+	.probe_mesh_link = nl80211_probe_mesh_link,
 #endif /* CONFIG_MESH */
 	.br_add_ip_neigh = wpa_driver_br_add_ip_neigh,
 	.br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh,
@@ -11015,6 +11300,7 @@
 	.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
 #endif /* CONFIG_MBO */
 	.set_bssid_blacklist = nl80211_set_bssid_blacklist,
+	.add_sta_node = nl80211_add_sta_node,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
 	.get_ext_capab = nl80211_get_ext_capab,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 1e7fe7a..716504c 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -83,6 +83,12 @@
 	u8 rand_addr[ETH_ALEN];
 };
 
+struct drv_nl80211_if_info {
+	int ifindex;
+	/* the AP/AP_VLAN iface that is in this bridge */
+	int reason;
+};
+
 struct wpa_driver_nl80211_data {
 	struct nl80211_global *global;
 	struct dl_list list;
@@ -163,10 +169,10 @@
 	unsigned int scan_vendor_cmd_avail:1;
 	unsigned int connect_reassoc:1;
 	unsigned int set_wifi_conf_vendor_cmd_avail:1;
-	unsigned int he_capab_vendor_cmd_avail:1;
 	unsigned int fetch_bss_trans_status:1;
 	unsigned int roam_vendor_cmd_avail:1;
 	unsigned int get_supported_akm_suites_avail:1;
+	unsigned int add_sta_node_vendor_cmd_avail:1;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
@@ -188,11 +194,8 @@
 
 	struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
 
-	int default_if_indices[16];
-	/* the AP/AP_VLAN iface that is in this bridge */
-	int default_if_indices_reason[16];
-	int *if_indices;
-	int *if_indices_reason;
+	struct drv_nl80211_if_info default_if_indices[16];
+	struct drv_nl80211_if_info *if_indices;
 	int num_if_indices;
 
 	/* From failed authentication command */
@@ -215,8 +218,6 @@
 	 * (NL80211_CMD_VENDOR). 0 if no pending scan request.
 	 */
 	int last_scan_cmd;
-
-	struct he_capabilities he_capab;
 };
 
 struct nl_msg;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 37eeb5e..9a82cd1 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -778,9 +778,6 @@
 				case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION:
 					drv->set_wifi_conf_vendor_cmd_avail = 1;
 					break;
-				case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
-					drv->he_capab_vendor_cmd_avail = 1;
-					break;
 				case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
 					drv->fetch_bss_trans_status = 1;
 					break;
@@ -790,6 +787,9 @@
 				case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS:
 					drv->get_supported_akm_suites_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE:
+					drv->add_sta_node_vendor_cmd_avail = 1;
+					break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 			}
@@ -1082,100 +1082,6 @@
 }
 
 
-static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct he_capabilities *he_capab = arg;
-	struct nlattr *nl_vend;
-	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX + 1];
-	size_t len;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (!tb[NL80211_ATTR_VENDOR_DATA])
-		return NL_SKIP;
-
-	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-	nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX,
-		  nla_data(nl_vend), nla_len(nl_vend), NULL);
-
-	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]) {
-		u8 he_supported;
-
-		he_supported = nla_get_u8(
-			tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]);
-		wpa_printf(MSG_DEBUG, "nl80211: HE capabilities supported: %u",
-			   he_supported);
-		he_capab->he_supported = he_supported;
-		if (!he_supported)
-			return NL_SKIP;
-	}
-
-	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]) {
-		len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]);
-
-		if (len > sizeof(he_capab->phy_cap))
-			len = sizeof(he_capab->phy_cap);
-		os_memcpy(he_capab->phy_cap,
-			  nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]),
-			  len);
-	}
-
-	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB])
-		he_capab->mac_cap =
-			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]);
-
-	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS])
-		he_capab->mcs =
-			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]);
-
-	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS])
-		he_capab->ppet.numss_m1 =
-			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]);
-
-	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK])
-		he_capab->ppet.ru_count =
-			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]);
-
-	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]) {
-		len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]);
-
-		if (len > sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0))
-			len = sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0);
-		os_memcpy(he_capab->ppet.ppet16_ppet8_ru3_ru0,
-			  nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]),
-			  len);
-	}
-
-	return NL_SKIP;
-}
-
-
-static void qca_nl80211_check_he_capab(struct wpa_driver_nl80211_data *drv)
-{
-	struct nl_msg *msg;
-	int ret;
-
-	if (!drv->he_capab_vendor_cmd_avail)
-		return;
-
-	if (!(msg = nl80211_drv_msg(drv, 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_GET_HE_CAPABILITIES)) {
-		nlmsg_free(msg);
-		return;
-	}
-
-	ret = send_and_recv_msgs(drv, msg, qca_nl80211_he_capab_handler,
-				 &drv->he_capab);
-	if (!ret && drv->he_capab.he_supported)
-		drv->capa.flags |= WPA_DRIVER_FLAGS_HE_CAPABILITIES;
-}
-
-
 struct features_info {
 	u8 *flags;
 	size_t flags_len;
@@ -1299,10 +1205,13 @@
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
 		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
-		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 |
 		WPA_DRIVER_CAPA_KEY_MGMT_OWE |
 		WPA_DRIVER_CAPA_KEY_MGMT_DPP;
 
+	if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
+			     WPA_DRIVER_CAPA_ENC_GCMP_256))
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+
 	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
 		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
 			WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
@@ -1373,7 +1282,6 @@
 	if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
 		qca_nl80211_check_dfs_capa(drv);
 	qca_nl80211_get_features(drv);
-	qca_nl80211_check_he_capab(drv);
 
 	/*
 	 * To enable offchannel simultaneous support in wpa_supplicant, the
@@ -1435,17 +1343,40 @@
 }
 
 
+static int phy_info_edmg_capa(struct hostapd_hw_modes *mode,
+			      struct nlattr *bw_config,
+			      struct nlattr *channels)
+{
+	if (!bw_config || !channels)
+		return NL_OK;
+
+	mode->edmg.bw_config = nla_get_u8(bw_config);
+	mode->edmg.channels = nla_get_u8(channels);
+
+	if (!mode->edmg.channels || !mode->edmg.bw_config)
+		return NL_STOP;
+
+	return NL_OK;
+}
+
+
 static void phy_info_freq(struct hostapd_hw_modes *mode,
 			  struct hostapd_channel_data *chan,
 			  struct nlattr *tb_freq[])
 {
 	u8 channel;
+
+	os_memset(chan, 0, sizeof(*chan));
 	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
 	chan->flag = 0;
 	chan->allowed_bw = ~0;
 	chan->dfs_cac_ms = 0;
 	if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
 		chan->chan = channel;
+	else
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: No channel number found for frequency %u MHz",
+			   chan->freq);
 
 	if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
 		chan->flag |= HOSTAPD_CHAN_DISABLED;
@@ -1492,6 +1423,57 @@
 		chan->dfs_cac_ms = nla_get_u32(
 			tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
 	}
+
+	chan->wmm_rules_valid = 0;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_WMM]) {
+		static struct nla_policy wmm_policy[NL80211_WMMR_MAX + 1] = {
+			[NL80211_WMMR_CW_MIN] = { .type = NLA_U16 },
+			[NL80211_WMMR_CW_MAX] = { .type = NLA_U16 },
+			[NL80211_WMMR_AIFSN] = { .type = NLA_U8 },
+			[NL80211_WMMR_TXOP] = { .type = NLA_U16 },
+		};
+		struct nlattr *nl_wmm;
+		struct nlattr *tb_wmm[NL80211_WMMR_MAX + 1];
+		int rem_wmm, ac, count = 0;
+
+		nla_for_each_nested(nl_wmm, tb_freq[NL80211_FREQUENCY_ATTR_WMM],
+				    rem_wmm) {
+			if (nla_parse_nested(tb_wmm, NL80211_WMMR_MAX, nl_wmm,
+					     wmm_policy)) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Failed to parse WMM rules attribute");
+				return;
+			}
+			if (!tb_wmm[NL80211_WMMR_CW_MIN] ||
+			    !tb_wmm[NL80211_WMMR_CW_MAX] ||
+			    !tb_wmm[NL80211_WMMR_AIFSN] ||
+			    !tb_wmm[NL80211_WMMR_TXOP]) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Channel is missing WMM rule attribute");
+				return;
+			}
+			ac = nl_wmm->nla_type;
+			if (ac < 0 || ac >= WMM_AC_NUM) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Invalid AC value %d", ac);
+				return;
+			}
+
+			chan->wmm_rules[ac].min_cwmin =
+				nla_get_u16(tb_wmm[NL80211_WMMR_CW_MIN]);
+			chan->wmm_rules[ac].min_cwmax =
+				nla_get_u16(tb_wmm[NL80211_WMMR_CW_MAX]);
+			chan->wmm_rules[ac].min_aifs =
+				nla_get_u8(tb_wmm[NL80211_WMMR_AIFSN]);
+			chan->wmm_rules[ac].max_txop =
+				nla_get_u16(tb_wmm[NL80211_WMMR_TXOP]) / 32;
+			count++;
+		}
+
+		/* Set valid flag if all the AC rules are present */
+		if (count == WMM_AC_NUM)
+			chan->wmm_rules_valid = 1;
+	}
 }
 
 
@@ -1598,6 +1580,101 @@
 }
 
 
+static void phy_info_iftype_copy(struct he_capabilities *he_capab,
+				 enum ieee80211_op_mode opmode,
+				 struct nlattr **tb, struct nlattr **tb_flags)
+{
+	enum nl80211_iftype iftype;
+	size_t len;
+
+	switch (opmode) {
+	case IEEE80211_MODE_INFRA:
+		iftype = NL80211_IFTYPE_STATION;
+		break;
+	case IEEE80211_MODE_IBSS:
+		iftype = NL80211_IFTYPE_ADHOC;
+		break;
+	case IEEE80211_MODE_AP:
+		iftype = NL80211_IFTYPE_AP;
+		break;
+	case IEEE80211_MODE_MESH:
+		iftype = NL80211_IFTYPE_MESH_POINT;
+		break;
+	default:
+		return;
+	}
+
+	if (!nla_get_flag(tb_flags[iftype]))
+		return;
+
+	he_capab->he_supported = 1;
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
+
+		if (len > sizeof(he_capab->phy_cap))
+			len = sizeof(he_capab->phy_cap);
+		os_memcpy(he_capab->phy_cap,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]);
+
+		if (len > sizeof(he_capab->mac_cap))
+			len = sizeof(he_capab->mac_cap);
+		os_memcpy(he_capab->mac_cap,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]);
+
+		if (len > sizeof(he_capab->mcs))
+			len = sizeof(he_capab->mcs);
+		os_memcpy(he_capab->mcs,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]);
+
+		if (len > sizeof(he_capab->ppet))
+			len = sizeof(he_capab->ppet);
+		os_memcpy(&he_capab->ppet,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
+			  len);
+	}
+}
+
+
+static int phy_info_iftype(struct hostapd_hw_modes *mode,
+			   struct nlattr *nl_iftype)
+{
+	struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
+	struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1];
+	unsigned int i;
+
+	nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
+		  nla_data(nl_iftype), nla_len(nl_iftype), NULL);
+
+	if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
+		return NL_STOP;
+
+	if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX,
+			     tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL))
+		return NL_STOP;
+
+	for (i = 0; i < IEEE80211_MODE_NUM; i++)
+		phy_info_iftype_copy(&mode->he_capab[i], i, tb, tb_flags);
+
+	return NL_OK;
+}
+
+
 static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
 {
 	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
@@ -1646,7 +1723,12 @@
 			 tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
 	phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
 			  tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
-	ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
+	ret = phy_info_edmg_capa(mode,
+				 tb_band[NL80211_BAND_ATTR_EDMG_BW_CONFIG],
+				 tb_band[NL80211_BAND_ATTR_EDMG_CHANNELS]);
+	if (ret == NL_OK)
+		ret = phy_info_freqs(phy_info, mode,
+				     tb_band[NL80211_BAND_ATTR_FREQS]);
 	if (ret == NL_OK)
 		ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
 	if (ret != NL_OK) {
@@ -1654,6 +1736,19 @@
 		return ret;
 	}
 
+	if (tb_band[NL80211_BAND_ATTR_IFTYPE_DATA]) {
+		struct nlattr *nl_iftype;
+		int rem_band;
+
+		nla_for_each_nested(nl_iftype,
+				    tb_band[NL80211_BAND_ATTR_IFTYPE_DATA],
+				    rem_band) {
+			ret = phy_info_iftype(mode, nl_iftype);
+			if (ret != NL_OK)
+				return ret;
+		}
+	}
+
 	return NL_OK;
 }
 
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index ee7b4da..2aecc74 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -136,6 +136,7 @@
 	C2S(NL80211_CMD_EXTERNAL_AUTH)
 	C2S(NL80211_CMD_STA_OPMODE_CHANGED)
 	C2S(NL80211_CMD_CONTROL_PORT_FRAME)
+	C2S(NL80211_CMD_UPDATE_OWE_INFO)
 	default:
 		return "NL80211_CMD_UNKNOWN";
 	}
@@ -523,6 +524,10 @@
 		break;
 	case CHAN_WIDTH_UNKNOWN:
 	case CHAN_WIDTH_80P80:
+	case CHAN_WIDTH_2160:
+	case CHAN_WIDTH_4320:
+	case CHAN_WIDTH_6480:
+	case CHAN_WIDTH_8640:
 		/* FIXME: implement this */
 		return 0;
 	}
@@ -534,7 +539,8 @@
 static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
 				 struct nlattr *ifindex, struct nlattr *freq,
 				 struct nlattr *type, struct nlattr *bw,
-				 struct nlattr *cf1, struct nlattr *cf2)
+				 struct nlattr *cf1, struct nlattr *cf2,
+				 int finished)
 {
 	struct i802_bss *bss;
 	union wpa_event_data data;
@@ -542,7 +548,8 @@
 	int chan_offset = 0;
 	int ifidx;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+	wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event",
+		   finished ? "" : " started");
 
 	if (!freq)
 		return;
@@ -593,10 +600,12 @@
 	if (cf2)
 		data.ch_switch.cf2 = nla_get_u32(cf2);
 
-	bss->freq = data.ch_switch.freq;
+	if (finished)
+		bss->freq = data.ch_switch.freq;
 	drv->assoc_freq = data.ch_switch.freq;
 
-	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
+	wpa_supplicant_event(bss->ctx, finished ?
+			     EVENT_CH_SWITCH : EVENT_CH_SWITCH_STARTED, &data);
 }
 
 
@@ -1101,6 +1110,29 @@
 }
 
 
+static void mlme_event_dh_event(struct wpa_driver_nl80211_data *drv,
+				struct i802_bss *bss,
+				struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	if (!is_ap_interface(drv->nlmode))
+		return;
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.update_dh.peer = nla_data(tb[NL80211_ATTR_MAC]);
+	data.update_dh.ie = nla_data(tb[NL80211_ATTR_IE]);
+	data.update_dh.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DH event - peer " MACSTR,
+		   MAC2STR(data.update_dh.peer));
+
+	wpa_supplicant_event(bss->ctx, EVENT_UPDATE_DH, &data);
+}
+
+
 static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
 			    struct nlattr *tb[], int external_scan)
 {
@@ -1708,26 +1740,57 @@
 }
 
 
+static unsigned int chan_2ghz_or_5ghz_to_freq(u8 chan)
+{
+	if (chan >= 1 && chan <= 13)
+		return 2407 + 5 * chan;
+	if (chan == 14)
+		return 2484;
+	if (chan >= 36 && chan <= 169)
+		return 5000 + 5 * chan;
+
+	return 0;
+}
+
+
 static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
 				   const u8 *data, size_t len)
 {
 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
 	union wpa_event_data event;
+	u8 chan;
 
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: ACS channel selection vendor event received");
 
 	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
 		      (struct nlattr *) data, len, NULL) ||
-	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
-	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
+	    (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY] &&
+	     !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]) ||
+	    (!tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY] &&
+	     !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]))
 		return;
 
 	os_memset(&event, 0, sizeof(event));
-	event.acs_selected_channels.pri_channel =
-		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
-	event.acs_selected_channels.sec_channel =
-		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]) {
+		event.acs_selected_channels.pri_freq = nla_get_u32(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]);
+	} else {
+		chan = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
+		event.acs_selected_channels.pri_freq =
+			chan_2ghz_or_5ghz_to_freq(chan);
+	}
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]) {
+		event.acs_selected_channels.sec_freq = nla_get_u32(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]);
+	} else {
+		chan = nla_get_u8(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+		event.acs_selected_channels.sec_freq =
+			chan_2ghz_or_5ghz_to_freq(chan);
+	}
+
 	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
 		event.acs_selected_channels.vht_seg0_center_ch =
 			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
@@ -1752,9 +1815,9 @@
 	}
 
 	wpa_printf(MSG_INFO,
-		   "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
-		   event.acs_selected_channels.pri_channel,
-		   event.acs_selected_channels.sec_channel,
+		   "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
+		   event.acs_selected_channels.pri_freq,
+		   event.acs_selected_channels.sec_freq,
 		   event.acs_selected_channels.ch_width,
 		   event.acs_selected_channels.vht_seg0_center_ch,
 		   event.acs_selected_channels.vht_seg1_center_ch,
@@ -2508,6 +2571,16 @@
 				   tb[NL80211_ATTR_PMK],
 				   tb[NL80211_ATTR_PMKID]);
 		break;
+	case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY:
+		mlme_event_ch_switch(drv,
+				     tb[NL80211_ATTR_IFINDEX],
+				     tb[NL80211_ATTR_WIPHY_FREQ],
+				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+				     tb[NL80211_ATTR_CHANNEL_WIDTH],
+				     tb[NL80211_ATTR_CENTER_FREQ1],
+				     tb[NL80211_ATTR_CENTER_FREQ2],
+				     0);
+		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
 		mlme_event_ch_switch(drv,
 				     tb[NL80211_ATTR_IFINDEX],
@@ -2515,7 +2588,8 @@
 				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
 				     tb[NL80211_ATTR_CHANNEL_WIDTH],
 				     tb[NL80211_ATTR_CENTER_FREQ1],
-				     tb[NL80211_ATTR_CENTER_FREQ2]);
+				     tb[NL80211_ATTR_CENTER_FREQ2],
+				     1);
 		break;
 	case NL80211_CMD_DISCONNECT:
 		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
@@ -2586,6 +2660,9 @@
 	case NL80211_CMD_STA_OPMODE_CHANGED:
 		nl80211_sta_opmode_change_event(drv, tb);
 		break;
+	case NL80211_CMD_UPDATE_OWE_INFO:
+		mlme_event_dh_event(drv, bss, tb);
+		break;
 	default:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
 			"(cmd=%d)", cmd);
@@ -2634,8 +2711,9 @@
 			}
 		}
 		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)",
-			   gnlh->cmd, ifidx, (long long unsigned int) wdev_id);
+			   "nl80211: Ignored event %d (%s) for foreign interface (ifindex %d wdev 0x%llx)",
+			   gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+			   ifidx, (long long unsigned int) wdev_id);
 	}
 
 	return NL_SKIP;
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index a3f0837..55cf618 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -368,7 +368,7 @@
 
 
 static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
-					  int reason_code)
+					     u16 reason_code)
 {
 	//struct wpa_driver_privsep_data *drv = priv;
 	wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index f7755cc..32c2971 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -1767,11 +1767,9 @@
 	case WPA_ALG_PMK:
 		ext->alg = IW_ENCODE_ALG_PMK;
 		break;
-#ifdef CONFIG_IEEE80211W
 	case WPA_ALG_IGTK:
 		ext->alg = IW_ENCODE_ALG_AES_CMAC;
 		break;
-#endif /* CONFIG_IEEE80211W */
 	default:
 		wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
 			   __FUNCTION__, alg);
@@ -1915,7 +1913,7 @@
 
 
 static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
-				const u8 *addr, int cmd, int reason_code)
+				const u8 *addr, int cmd, u16 reason_code)
 {
 	struct iwreq iwr;
 	struct iw_mlme mlme;
@@ -1998,7 +1996,7 @@
 
 
 static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
-					  int reason_code)
+					  u16 reason_code)
 {
 	struct wpa_driver_wext_data *drv = priv;
 	int ret;
@@ -2201,7 +2199,6 @@
 					   IW_AUTH_RX_UNENCRYPTED_EAPOL,
 					   allow_unencrypted_eapol) < 0)
 		ret = -1;
-#ifdef CONFIG_IEEE80211W
 	switch (params->mgmt_frame_protection) {
 	case NO_MGMT_FRAME_PROTECTION:
 		value = IW_AUTH_MFP_DISABLED;
@@ -2215,7 +2212,6 @@
 	};
 	if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
 		ret = -1;
-#endif /* CONFIG_IEEE80211W */
 	if (params->freq.freq &&
 	    wpa_driver_wext_set_freq(drv, params->freq.freq) < 0)
 		ret = -1;
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 442c59c..bc2e87e 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -140,10 +140,6 @@
 DRV_OBJS += ../src/drivers/netlink.o
 endif
 
-ifdef NEED_LINUX_IOCTL
-DRV_OBJS += ../src/drivers/linux_ioctl.o
-endif
-
 ifdef NEED_RFKILL
 DRV_OBJS += ../src/drivers/rfkill.o
 endif
@@ -152,13 +148,18 @@
 DRV_OBJS += ../src/utils/radiotap.o
 endif
 
-ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
 NEED_LIBNL=y
 CONFIG_LIBNL3_ROUTE=y
 endif
 endif
 
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += ../src/drivers/linux_ioctl.o
+endif
+
 ifdef NEED_LIBNL
 ifndef CONFIG_LIBNL32
 ifndef CONFIG_LIBNL20
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 599a0b5..c3c2c0f 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -132,10 +132,6 @@
 DRV_OBJS += src/drivers/netlink.c
 endif
 
-ifdef NEED_LINUX_IOCTL
-DRV_OBJS += src/drivers/linux_ioctl.c
-endif
-
 ifdef NEED_RFKILL
 DRV_OBJS += src/drivers/rfkill.c
 endif
@@ -148,13 +144,18 @@
 DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
 endif
 
-ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
 NEED_LIBNL=y
 CONFIG_LIBNL3_ROUTE=y
 endif
 endif
 
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
 ifdef NEED_LIBNL
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index dd4f86e..341e0e8 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -11,7 +11,7 @@
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
  * Copyright 2015-2017	Intel Deutschland GmbH
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018-2019 Intel Corporation
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -52,6 +52,11 @@
 #define NL80211_MULTICAST_GROUP_NAN		"nan"
 #define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
 
+#define NL80211_EDMG_BW_CONFIG_MIN	4
+#define NL80211_EDMG_BW_CONFIG_MAX	15
+#define NL80211_EDMG_CHANNELS_MIN	1
+#define NL80211_EDMG_CHANNELS_MAX	0x3c /* 0b00111100 */
+
 /**
  * DOC: Station handling
  *
@@ -235,6 +240,31 @@
  */
 
 /**
+ * DOC: SAE authentication offload
+ *
+ * By setting @NL80211_EXT_FEATURE_SAE_OFFLOAD flag drivers can indicate they
+ * support offloading SAE authentication for WPA3-Personal networks. In
+ * %NL80211_CMD_CONNECT the password for SAE should be specified using
+ * %NL80211_ATTR_SAE_PASSWORD.
+ */
+
+/**
+ * DOC: VLAN offload support for setting group keys and binding STAs to VLANs
+ *
+ * By setting @NL80211_EXT_FEATURE_VLAN_OFFLOAD flag drivers can indicate they
+ * support offloading VLAN functionality in a manner where the driver exposes a
+ * single netdev that uses VLAN tagged frames and separate VLAN-specific netdevs
+ * can then be added using RTM_NEWLINK/IFLA_VLAN_ID similarly to the Ethernet
+ * case. Frames received from stations that are not assigned to any VLAN are
+ * delivered on the main netdev and frames to such stations can be sent through
+ * that main netdev.
+ *
+ * %NL80211_CMD_NEW_KEY (for group keys), %NL80211_CMD_NEW_STATION, and
+ * %NL80211_CMD_SET_STATION will optionally specify vlan_id using
+ * %NL80211_ATTR_VLAN_ID.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -557,6 +587,14 @@
  *	set of BSSID,frequency parameters is used (i.e., either the enforcing
  *	%NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
  *	%NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
+ *	Driver shall not modify the IEs specified through %NL80211_ATTR_IE if
+ *	%NL80211_ATTR_MAC is included. However, if %NL80211_ATTR_MAC_HINT is
+ *	included, these IEs through %NL80211_ATTR_IE are specified by the user
+ *	space based on the best possible BSS selected. Thus, if the driver ends
+ *	up selecting a different BSS, it can modify these IEs accordingly (e.g.
+ *	userspace asks the driver to perform PMKSA caching with BSS1 and the
+ *	driver ends up selecting BSS2 with different PMKSA cache entry; RSNIE
+ *	has to get updated with the apt PMKID).
  *	%NL80211_ATTR_PREV_BSSID can be used to request a reassociation within
  *	the ESS in case the device is already associated and an association with
  *	a different BSS is desired.
@@ -648,7 +686,9 @@
  *	is used during CSA period.
  * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
  *	command may be used with the corresponding cookie to cancel the wait
- *	time if it is known that it is no longer necessary.
+ *	time if it is known that it is no longer necessary.  This command is
+ *	also sent as an event whenever the driver has completed the off-channel
+ *	wait time.
  * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
  * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
  *	transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
@@ -1065,6 +1105,26 @@
  *	indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes
  *	determining the width and type.
  *
+ * @NL80211_CMD_UPDATE_OWE_INFO: This interface allows the host driver to
+ *	offload OWE processing to user space. This intends to support
+ *	OWE AKM by the host drivers that implement SME but rely
+ *	on the user space for the cryptographic/DH IE processing in AP mode.
+ *
+ * @NL80211_CMD_PROBE_MESH_LINK: The requirement for mesh link metric
+ *	refreshing, is that from one mesh point we be able to send some data
+ *	frames to other mesh points which are not currently selected as a
+ *	primary traffic path, but which are only 1 hop away. The absence of
+ *	the primary path to the chosen node makes it necessary to apply some
+ *	form of marking on a chosen packet stream so that the packets can be
+ *	properly steered to the selected node for testing, and not by the
+ *	regular mesh path lookup. Further, the packets must be of type data
+ *	so that the rate control (often embedded in firmware) is used for
+ *	rate selection.
+ *
+ *	Here attribute %NL80211_ATTR_MAC is used to specify connected mesh
+ *	peer MAC address and %NL80211_ATTR_FRAME is used to specify the frame
+ *	content. The frame is ethernet data.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1285,6 +1345,10 @@
 
 	NL80211_CMD_NOTIFY_RADAR,
 
+	NL80211_CMD_UPDATE_OWE_INFO,
+
+	NL80211_CMD_PROBE_MESH_LINK,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -2308,6 +2372,34 @@
  * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
  *	scheduler.
  *
+ * @NL80211_ATTR_STA_TX_POWER_SETTING: Transmit power setting type (u8) for
+ *	station associated with the AP. See &enum nl80211_tx_power_setting for
+ *	possible values.
+ * @NL80211_ATTR_STA_TX_POWER: Transmit power level (s16) in dBm units. This
+ *	allows to set Tx power for a station. If this attribute is not included,
+ *	the default per-interface tx power setting will be overriding. Driver
+ *	should be picking up the lowest tx power, either tx power per-interface
+ *	or per-station.
+ *
+ * @NL80211_ATTR_SAE_PASSWORD: attribute for passing SAE password material. It
+ *	is used with %NL80211_CMD_CONNECT to provide password for offloading
+ *	SAE authentication for WPA3-Personal networks.
+ *
+ * @NL80211_ATTR_TWT_RESPONDER: Enable target wait time responder support.
+ *
+ * @NL80211_ATTR_HE_OBSS_PD: nested attribute for OBSS Packet Detection
+ *	functionality.
+ *
+ * @NL80211_ATTR_WIPHY_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ *	channel(s) that are allowed to be used for EDMG transmissions.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251. (u8 attribute)
+ * @NL80211_ATTR_WIPHY_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations. (u8 attribute)
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
+ *
+ * @NL80211_ATTR_VLAN_ID: VLAN ID (1..4094) for the station and VLAN group key
+ *	(u16).
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2758,6 +2850,19 @@
 	NL80211_ATTR_PEER_MEASUREMENTS,
 
 	NL80211_ATTR_AIRTIME_WEIGHT,
+	NL80211_ATTR_STA_TX_POWER_SETTING,
+	NL80211_ATTR_STA_TX_POWER,
+
+	NL80211_ATTR_SAE_PASSWORD,
+
+	NL80211_ATTR_TWT_RESPONDER,
+
+	NL80211_ATTR_HE_OBSS_PD,
+
+	NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+	NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+
+	NL80211_ATTR_VLAN_ID,
 
 	/* add attributes here, update the policy in nl80211.c */
 
@@ -2802,14 +2907,14 @@
 
 #define NL80211_MAX_SUPP_RATES			32
 #define NL80211_MAX_SUPP_HT_RATES		77
-#define NL80211_MAX_SUPP_REG_RULES		64
+#define NL80211_MAX_SUPP_REG_RULES		128
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY	16
 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY	24
 #define NL80211_HT_CAPABILITY_LEN		26
 #define NL80211_VHT_CAPABILITY_LEN		12
 #define NL80211_HE_MIN_CAPABILITY_LEN           16
-#define NL80211_HE_MAX_CAPABILITY_LEN           51
+#define NL80211_HE_MAX_CAPABILITY_LEN           54
 #define NL80211_MAX_NR_CIPHER_SUITES		5
 #define NL80211_MAX_NR_AKM_SUITES		2
 
@@ -3139,6 +3244,9 @@
  * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames
  *	sent to the station (u64, usec)
  * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
+ * @NL80211_STA_INFO_AIRTIME_LINK_METRIC: airtime link metric for mesh station
+ * @NL80211_STA_INFO_ASSOC_AT_BOOTTIME: Timestamp (CLOCK_BOOTTIME, nanoseconds)
+ *	of STA's association
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -3184,6 +3292,8 @@
 	NL80211_STA_INFO_CONNECTED_TO_GATE,
 	NL80211_STA_INFO_TX_DURATION,
 	NL80211_STA_INFO_AIRTIME_WEIGHT,
+	NL80211_STA_INFO_AIRTIME_LINK_METRIC,
+	NL80211_STA_INFO_ASSOC_AT_BOOTTIME,
 
 	/* keep last */
 	__NL80211_STA_INFO_AFTER_LAST,
@@ -3365,6 +3475,12 @@
  * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using
  *	attributes from &enum nl80211_band_iftype_attr
+ * @NL80211_BAND_ATTR_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ *	channel(s) that are allowed to be used for EDMG transmissions.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251.
+ * @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
  * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
  * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
  */
@@ -3382,6 +3498,9 @@
 	NL80211_BAND_ATTR_VHT_CAPA,
 	NL80211_BAND_ATTR_IFTYPE_DATA,
 
+	NL80211_BAND_ATTR_EDMG_CHANNELS,
+	NL80211_BAND_ATTR_EDMG_BW_CONFIG,
+
 	/* keep last */
 	__NL80211_BAND_ATTR_AFTER_LAST,
 	NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
@@ -3638,6 +3757,14 @@
  *	value as specified by &struct nl80211_bss_select_rssi_adjust.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
  *	(this cannot be used together with SSID).
+ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
+ *	band specific minimum rssi thresholds for the bands defined in
+ *	enum nl80211_band. The minimum rssi threshold value(s32) specific to a
+ *	band shall be encapsulated in attribute with type value equals to one
+ *	of the NL80211_BAND_* defined in enum nl80211_band. For example, the
+ *	minimum rssi threshold value for 2.4GHZ band shall be encapsulated
+ *	within an attribute of type NL80211_BAND_2GHZ. And one or more of such
+ *	attributes will be nested within this attribute.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *	attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -3650,6 +3777,7 @@
 	NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
 	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
 	NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
+	NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
 
 	/* keep last */
 	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -3771,6 +3899,8 @@
  * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
  *	(on this channel or globally)
  * @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_SURVEY_INFO_TIME_BSS_RX: amount of time the radio spent
+ *	receiving frames destined to the local BSS
  * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
  *	currently defined
  * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
@@ -3787,6 +3917,7 @@
 	NL80211_SURVEY_INFO_TIME_TX,
 	NL80211_SURVEY_INFO_TIME_SCAN,
 	NL80211_SURVEY_INFO_PAD,
+	NL80211_SURVEY_INFO_TIME_BSS_RX,
 
 	/* keep last */
 	__NL80211_SURVEY_INFO_AFTER_LAST,
@@ -4135,6 +4266,27 @@
 };
 
 /**
+ * enum nl80211_key_mode - Key mode
+ *
+ * @NL80211_KEY_RX_TX: (Default)
+ *	Key can be used for Rx and Tx immediately
+ *
+ * The following modes can only be selected for unicast keys and when the
+ * driver supports @NL80211_EXT_FEATURE_EXT_KEY_ID:
+ *
+ * @NL80211_KEY_NO_TX: Only allowed in combination with @NL80211_CMD_NEW_KEY:
+ *	Unicast key can only be used for Rx, Tx not allowed, yet
+ * @NL80211_KEY_SET_TX: Only allowed in combination with @NL80211_CMD_SET_KEY:
+ *	The unicast key identified by idx and mac is cleared for Tx and becomes
+ *	the preferred Tx key for the station.
+ */
+enum nl80211_key_mode {
+	NL80211_KEY_RX_TX,
+	NL80211_KEY_NO_TX,
+	NL80211_KEY_SET_TX
+};
+
+/**
  * enum nl80211_chan_width - channel width definitions
  *
  * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH
@@ -4339,6 +4491,7 @@
 enum nl80211_wpa_versions {
 	NL80211_WPA_VERSION_1 = 1 << 0,
 	NL80211_WPA_VERSION_2 = 1 << 1,
+	NL80211_WPA_VERSION_3 = 1 << 2,
 };
 
 /**
@@ -4377,6 +4530,9 @@
  * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags
  *	attributes, specifying what a key should be set as default as.
  *	See &enum nl80211_key_default_types.
+ * @NL80211_KEY_MODE: the mode from enum nl80211_key_mode.
+ *	Defaults to @NL80211_KEY_RX_TX.
+ *
  * @__NL80211_KEY_AFTER_LAST: internal
  * @NL80211_KEY_MAX: highest key attribute
  */
@@ -4390,6 +4546,7 @@
 	NL80211_KEY_DEFAULT_MGMT,
 	NL80211_KEY_TYPE,
 	NL80211_KEY_DEFAULT_TYPES,
+	NL80211_KEY_MODE,
 
 	/* keep last */
 	__NL80211_KEY_AFTER_LAST,
@@ -4445,6 +4602,7 @@
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
  * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
+ * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
  * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
  *	since newer kernel versions may support more bands
  */
@@ -4452,6 +4610,7 @@
 	NL80211_BAND_2GHZ,
 	NL80211_BAND_5GHZ,
 	NL80211_BAND_60GHZ,
+	NL80211_BAND_6GHZ,
 
 	NUM_NL80211_BANDS,
 };
@@ -5243,7 +5402,7 @@
 	NL80211_FEATURE_TDLS_CHANNEL_SWITCH		= 1 << 28,
 	NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR		= 1 << 29,
 	NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR	= 1 << 30,
-	NL80211_FEATURE_ND_RANDOM_MAC_ADDR		= 1 << 31,
+	NL80211_FEATURE_ND_RANDOM_MAC_ADDR		= 1U << 31,
 };
 
 /**
@@ -5335,6 +5494,8 @@
  *      able to rekey an in-use key correctly. Userspace must not rekey PTK keys
  *      if this flag is not set. Ignoring this can leak clear text packets and/or
  *      freeze the connection.
+ * @NL80211_EXT_FEATURE_EXT_KEY_ID: Driver supports "Extended Key ID for
+ *      Individually Addressed Frames" from IEEE802.11-2016.
  *
  * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime
  *	fairness for transmitted packets and has enabled airtime fairness
@@ -5343,6 +5504,19 @@
  * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
  *	(set/del PMKSA operations) in AP mode.
  *
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
+ *	filtering of sched scan results using band specific RSSI thresholds.
+ *
+ * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
+ *	to a station.
+ *
+ * @NL80211_EXT_FEATURE_SAE_OFFLOAD: Device wants to do SAE authentication in
+ *	station mode (SAE password is passed as part of the connect command).
+ *
+ * @NL80211_EXT_FEATURE_VLAN_OFFLOAD: The driver supports a single netdev
+ *	with VLAN tagged frames and separate VLAN-specific netdevs added using
+ *	vconfig similarly to the Ethernet case.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -5384,6 +5558,11 @@
 	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
 	NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
 	NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
+	NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
+	NL80211_EXT_FEATURE_EXT_KEY_ID,
+	NL80211_EXT_FEATURE_STA_TX_PWR,
+	NL80211_EXT_FEATURE_SAE_OFFLOAD,
+	NL80211_EXT_FEATURE_VLAN_OFFLOAD,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -6382,4 +6561,26 @@
 	NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1
 };
 
+/**
+ * enum nl80211_obss_pd_attributes - OBSS packet detection attributes
+ * @__NL80211_HE_OBSS_PD_ATTR_INVALID: Invalid
+ *
+ * @NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET: the OBSS PD minimum tx power offset.
+ * @NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET: the OBSS PD maximum tx power offset.
+ *
+ * @__NL80211_HE_OBSS_PD_ATTR_LAST: Internal
+ * @NL80211_HE_OBSS_PD_ATTR_MAX: highest OBSS PD attribute.
+ */
+enum nl80211_obss_pd_attributes {
+	__NL80211_HE_OBSS_PD_ATTR_INVALID,
+
+	NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
+	NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
+
+	/* keep last */
+	__NL80211_HE_OBSS_PD_ATTR_LAST,
+	NL80211_HE_OBSS_PD_ATTR_MAX = __NL80211_HE_OBSS_PD_ATTR_LAST - 1,
+};
+
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c
index 51a15d7..e27b965 100644
--- a/src/eap_common/eap_common.c
+++ b/src/eap_common/eap_common.c
@@ -63,7 +63,7 @@
  * the payload regardless of whether the packet used the expanded EAP header or
  * not.
  */
-const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+const u8 * eap_hdr_validate(int vendor, enum eap_type eap_type,
 			    const struct wpabuf *msg, size_t *plen)
 {
 	const struct eap_hdr *hdr;
@@ -125,8 +125,8 @@
  * function to allocate the message buffers. The returned buffer has room for
  * payload_len bytes and has the EAP header and Type field already filled in.
  */
-struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
-			      u8 code, u8 identifier)
+struct wpabuf * eap_msg_alloc(int vendor, enum eap_type type,
+			      size_t payload_len, u8 code, u8 identifier)
 {
 	struct wpabuf *buf;
 	struct eap_hdr *hdr;
@@ -196,7 +196,7 @@
  * @msg: Buffer starting with an EAP header
  * Returns: The EAP Type after the EAP header
  */
-EapType eap_get_type(const struct wpabuf *msg)
+enum eap_type eap_get_type(const struct wpabuf *msg)
 {
 	if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
 		return EAP_TYPE_NONE;
diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h
index e62f167..e40cabe 100644
--- a/src/eap_common/eap_common.h
+++ b/src/eap_common/eap_common.h
@@ -20,13 +20,13 @@
 };
 
 int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
-const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+const u8 * eap_hdr_validate(int vendor, enum eap_type eap_type,
 			    const struct wpabuf *msg, size_t *plen);
-struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
-			      u8 code, u8 identifier);
+struct wpabuf * eap_msg_alloc(int vendor, enum eap_type type,
+			      size_t payload_len, u8 code, u8 identifier);
 void eap_update_len(struct wpabuf *msg);
 u8 eap_get_id(const struct wpabuf *msg);
-EapType eap_get_type(const struct wpabuf *msg);
+enum eap_type eap_get_type(const struct wpabuf *msg);
 int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
 		   int stop_at_keyname);
 
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
index 54f26ca..70999c4 100644
--- a/src/eap_common/eap_defs.h
+++ b/src/eap_common/eap_defs.h
@@ -64,7 +64,7 @@
  * EAP Method Types as allocated by IANA:
  * http://www.iana.org/assignments/eap-numbers
  */
-typedef enum {
+enum eap_type {
 	EAP_TYPE_NONE = 0,
 	EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
 	EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
@@ -92,8 +92,9 @@
 	EAP_TYPE_GPSK = 51 /* RFC 5433 */,
 	EAP_TYPE_PWD = 52 /* RFC 5931 */,
 	EAP_TYPE_EKE = 53 /* RFC 6124 */,
+	EAP_TYPE_TEAP = 55 /* RFC 7170 */,
 	EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
-} EapType;
+};
 
 
 /* SMI Network Management Private Enterprise Code for vendor specific types */
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 6ca2c8b..2b2b8ef 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 #include "common.h"
 #include "utils/const_time.h"
+#include "common/dragonfly.h"
 #include "crypto/sha256.h"
 #include "crypto/crypto.h"
 #include "eap_defs.h"
@@ -85,20 +86,11 @@
 }
 
 
-static int eap_pwd_suitable_group(u16 num)
-{
-	/* Do not allow ECC groups with prime under 256 bits based on guidance
-	 * for the similar design in SAE. */
-	return num == 19 || num == 20 || num == 21 ||
-		num == 28 || num == 29 || num == 30;
-}
-
-
 EAP_PWD_group * get_eap_pwd_group(u16 num)
 {
 	EAP_PWD_group *grp;
 
-	if (!eap_pwd_suitable_group(num)) {
+	if (!dragonfly_suitable_group(num, 1)) {
 		wpa_printf(MSG_INFO, "EAP-pwd: unsuitable group %u", num);
 		return NULL;
 	}
@@ -119,15 +111,6 @@
 }
 
 
-static void buf_shift_right(u8 *buf, size_t len, size_t bits)
-{
-	size_t i;
-	for (i = len - 1; i > 0; i--)
-		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
-	buf[0] >>= bits;
-}
-
-
 /*
  * compute a "random" secret point on an elliptic curve based
  * on the password and identities.
@@ -138,23 +121,24 @@
 			     const u8 *id_peer, size_t id_peer_len,
 			     const u8 *token)
 {
-	struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
-	struct crypto_bignum *qr_or_qnr = NULL;
+	struct crypto_bignum *qr = NULL, *qnr = NULL;
 	u8 qr_bin[MAX_ECC_PRIME_LEN];
 	u8 qnr_bin[MAX_ECC_PRIME_LEN];
 	u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
 	u8 x_bin[MAX_ECC_PRIME_LEN];
 	u8 prime_bin[MAX_ECC_PRIME_LEN];
-	struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
+	struct crypto_bignum *tmp2 = NULL;
 	struct crypto_hash *hash;
 	unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
-	int ret = 0, check, res;
+	int ret = 0, res;
 	u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
 		       * mask */
 	size_t primebytelen = 0, primebitlen;
 	struct crypto_bignum *x_candidate = NULL;
 	const struct crypto_bignum *prime;
-	u8 mask, found_ctr = 0, is_odd = 0;
+	u8 found_ctr = 0, is_odd = 0;
+	int cmp_prime;
+	unsigned int in_range;
 
 	if (grp->pwe)
 		return -1;
@@ -168,10 +152,7 @@
 				 primebytelen) < 0)
 		return -1;
 	grp->pwe = crypto_ec_point_init(grp->group);
-	tmp1 = crypto_bignum_init();
-	pm1 = crypto_bignum_init();
-	one = crypto_bignum_init_set((const u8 *) "\x01", 1);
-	if (!grp->pwe || !tmp1 || !pm1 || !one) {
+	if (!grp->pwe) {
 		wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
 		goto fail;
 	}
@@ -181,25 +162,10 @@
 			   "buffer");
 		goto fail;
 	}
-	if (crypto_bignum_sub(prime, one, pm1) < 0)
-		goto fail;
 
 	/* get a random quadratic residue and nonresidue */
-	while (!qr || !qnr) {
-		if (crypto_bignum_rand(tmp1, prime) < 0)
-			goto fail;
-		res = crypto_bignum_legendre(tmp1, prime);
-		if (!qr && res == 1) {
-			qr = tmp1;
-			tmp1 = crypto_bignum_init();
-		} else if (!qnr && res == -1) {
-			qnr = tmp1;
-			tmp1 = crypto_bignum_init();
-		}
-		if (!tmp1)
-			goto fail;
-	}
-	if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin),
+	if (dragonfly_get_random_qr_qnr(prime, &qr, &qnr) < 0 ||
+	    crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin),
 				 primebytelen) < 0 ||
 	    crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin),
 				 primebytelen) < 0)
@@ -241,8 +207,13 @@
 		if (primebitlen % 8)
 			buf_shift_right(prfbuf, primebytelen,
 					8 - primebitlen % 8);
-		if (const_time_memcmp(prfbuf, prime_bin, primebytelen) >= 0)
-			continue;
+		cmp_prime = const_time_memcmp(prfbuf, prime_bin, primebytelen);
+		/* Create a const_time mask for selection based on prf result
+		 * being smaller than prime. */
+		in_range = const_time_fill_msb((unsigned int) cmp_prime);
+		/* The algorithm description would skip the next steps if
+		 * cmp_prime >= 0, but go through them regardless to minimize
+		 * externally observable differences in behavior. */
 
 		crypto_bignum_deinit(x_candidate, 1);
 		x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
@@ -267,46 +238,16 @@
 		if (!tmp2)
 			goto fail;
 
-		/*
-		 * mask tmp2 so doing legendre won't leak timing info
-		 *
-		 * tmp1 is a random number between 1 and p-1
-		 */
-		if (crypto_bignum_rand(tmp1, pm1) < 0 ||
-		    crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 ||
-		    crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0)
+		res = dragonfly_is_quadratic_residue_blind(grp->group, qr_bin,
+							   qnr_bin, tmp2);
+		if (res < 0)
 			goto fail;
-
-		/*
-		 * Now tmp2 (y^2) is masked, all values between 1 and p-1
-		 * are equally probable. Multiplying by r^2 does not change
-		 * whether or not tmp2 is a quadratic residue, just masks it.
-		 *
-		 * Flip a coin, multiply by the random quadratic residue or the
-		 * random quadratic nonresidue and record heads or tails.
-		 */
-		mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1);
-		check = const_time_select_s8(mask, 1, -1);
-		const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen,
-				      qr_or_qnr_bin);
-		crypto_bignum_deinit(qr_or_qnr, 1);
-		qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen);
-		if (!qr_or_qnr ||
-		    crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0)
-			goto fail;
-
-		/*
-		 * Now it's safe to do legendre, if check is 1 then it's
-		 * a straightforward test (multiplying by qr does not
-		 * change result), if check is -1 then it's the opposite test
-		 * (multiplying a qr by qnr would make a qnr).
-		 */
-		res = crypto_bignum_legendre(tmp2, prime);
-		if (res == -2)
-			goto fail;
-		mask = const_time_eq(res, check);
 		found_ctr = const_time_select_u8(found, found_ctr, ctr);
-		found |= mask;
+		/* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
+		 * (with res converted to 0/0xff and masked with prf being below
+		 * prime) handles this in constant time.
+		 */
+		found |= (res & in_range) * 0xff;
 	}
 	if (found == 0) {
 		wpa_printf(MSG_INFO,
@@ -347,13 +288,9 @@
 	}
 	/* cleanliness and order.... */
 	crypto_bignum_deinit(x_candidate, 1);
-	crypto_bignum_deinit(pm1, 0);
-	crypto_bignum_deinit(tmp1, 1);
 	crypto_bignum_deinit(tmp2, 1);
 	crypto_bignum_deinit(qr, 1);
 	crypto_bignum_deinit(qnr, 1);
-	crypto_bignum_deinit(qr_or_qnr, 1);
-	crypto_bignum_deinit(one, 0);
 	bin_clear_free(prfbuf, primebytelen);
 	os_memset(qr_bin, 0, sizeof(qr_bin));
 	os_memset(qnr_bin, 0, sizeof(qnr_bin));
@@ -507,25 +444,6 @@
 			  struct crypto_bignum *_mask,
 			  struct crypto_bignum *scalar)
 {
-	const struct crypto_bignum *order;
-	int count;
-
-	order = crypto_ec_get_order(group->group);
-
-	/* Select two random values rand,mask such that 1 < rand,mask < r and
-	 * rand + mask mod r > 1. */
-	for (count = 0; count < 100; count++) {
-		if (crypto_bignum_rand(_rand, order) == 0 &&
-		    !crypto_bignum_is_zero(_rand) &&
-		    crypto_bignum_rand(_mask, order) == 0 &&
-		    !crypto_bignum_is_zero(_mask) &&
-		    crypto_bignum_add(_rand, _mask, scalar) == 0 &&
-		    crypto_bignum_mod(scalar, order, scalar) == 0 &&
-		    !crypto_bignum_is_zero(scalar) &&
-		    !crypto_bignum_is_one(scalar))
-			return 0;
-	}
-
-	wpa_printf(MSG_INFO, "EAP-pwd: unable to get randomness");
-	return -1;
+	return dragonfly_generate_scalar(crypto_ec_get_order(group->group),
+					 _rand, _mask, scalar);
 }
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
index 8819541..8ee9e32 100644
--- a/src/eap_common/eap_sake_common.c
+++ b/src/eap_common/eap_sake_common.c
@@ -1,6 +1,6 @@
 /*
  * EAP server/peer: EAP-SAKE shared routines
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -201,14 +201,15 @@
  * @data2_len: Length of the data2
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success or -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
  */
-static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
-			 const u8 *data, size_t data_len,
-			 const u8 *data2, size_t data2_len,
-			 u8 *buf, size_t buf_len)
+static int eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
+			const u8 *data, size_t data_len,
+			const u8 *data2, size_t data2_len,
+			u8 *buf, size_t buf_len)
 {
 	u8 counter = 0;
 	size_t pos, plen;
@@ -230,17 +231,21 @@
 	while (pos < buf_len) {
 		plen = buf_len - pos;
 		if (plen >= SHA1_MAC_LEN) {
-			hmac_sha1_vector(key, key_len, 4, addr, len,
-					 &buf[pos]);
+			if (hmac_sha1_vector(key, key_len, 4, addr, len,
+					     &buf[pos]) < 0)
+				return -1;
 			pos += SHA1_MAC_LEN;
 		} else {
-			hmac_sha1_vector(key, key_len, 4, addr, len,
-					 hash);
+			if (hmac_sha1_vector(key, key_len, 4, addr, len,
+					     hash) < 0)
+				return -1;
 			os_memcpy(&buf[pos], hash, plen);
 			break;
 		}
 		counter++;
 	}
+
+	return 0;
 }
 
 
@@ -253,12 +258,13 @@
  * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16])
  * @msk: Buffer for 64-byte MSK
  * @emsk: Buffer for 64-byte EMSK
+ * Returns: 0 on success or -1 on failure
  *
  * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6.
  */
-void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
-			  const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
-			  u8 *emsk)
+int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+			 const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
+			 u8 *emsk)
 {
 	u8 sms_a[EAP_SAKE_SMS_LEN];
 	u8 sms_b[EAP_SAKE_SMS_LEN];
@@ -268,14 +274,16 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A",
 			root_secret_a, EAP_SAKE_ROOT_SECRET_LEN);
-	eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
-		     "SAKE Master Secret A",
-		     rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
-		     sms_a, EAP_SAKE_SMS_LEN);
+	if (eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
+			 "SAKE Master Secret A",
+			 rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+			 sms_a, EAP_SAKE_SMS_LEN) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN);
-	eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
-		     rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
-		     tek, EAP_SAKE_TEK_LEN);
+	if (eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
+			 rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+			 tek, EAP_SAKE_TEK_LEN) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth",
 			tek, EAP_SAKE_TEK_AUTH_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher",
@@ -283,18 +291,21 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B",
 			root_secret_b, EAP_SAKE_ROOT_SECRET_LEN);
-	eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
-		     "SAKE Master Secret B",
-		     rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
-		     sms_b, EAP_SAKE_SMS_LEN);
+	if (eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
+			 "SAKE Master Secret B",
+			 rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+			 sms_b, EAP_SAKE_SMS_LEN) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN);
-	eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
-		     rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
-		     key_buf, sizeof(key_buf));
+	if (eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
+			 rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+			 key_buf, sizeof(key_buf)) < 0)
+		return -1;
 	os_memcpy(msk, key_buf, EAP_MSK_LEN);
 	os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN);
+	return 0;
 }
 
 
@@ -312,6 +323,7 @@
  * @eap_len: EAP packet length
  * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len])
  * @mic: Buffer for the computed 16-byte MIC
+ * Returns: 0 on success or -1 on failure
  */
 int eap_sake_compute_mic(const u8 *tek_auth,
 			 const u8 *rand_s, const u8 *rand_p,
@@ -323,6 +335,7 @@
 	u8 _rand[2 * EAP_SAKE_RAND_LEN];
 	u8 *tmp, *pos;
 	size_t tmplen;
+	int ret;
 
 	tmplen = serverid_len + 1 + peerid_len + 1 + eap_len;
 	tmp = os_malloc(tmplen);
@@ -364,14 +377,14 @@
 	os_memcpy(pos, eap, eap_len);
 	os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN);
 
-	eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
-		     peer ? "Peer MIC" : "Server MIC",
-		     _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
-		     mic, EAP_SAKE_MIC_LEN);
+	ret = eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
+			   peer ? "Peer MIC" : "Server MIC",
+			   _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
+			   mic, EAP_SAKE_MIC_LEN);
 
 	os_free(tmp);
 
-	return 0;
+	return ret;
 }
 
 
diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h
index 9e1e757..a817a35 100644
--- a/src/eap_common/eap_sake_common.h
+++ b/src/eap_common/eap_sake_common.h
@@ -1,6 +1,6 @@
 /*
  * EAP server/peer: EAP-SAKE shared routines
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -81,9 +81,9 @@
 
 int eap_sake_parse_attributes(const u8 *buf, size_t len,
 			      struct eap_sake_parse_attr *attr);
-void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
-			  const u8 *rand_s, const u8 *rand_p,
-			  u8 *tek, u8 *msk, u8 *emsk);
+int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+			 const u8 *rand_s, const u8 *rand_p,
+			 u8 *tek, u8 *msk, u8 *emsk);
 int eap_sake_compute_mic(const u8 *tek_auth,
 			 const u8 *rand_s, const u8 *rand_p,
 			 const u8 *serverid, size_t serverid_len,
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
index cfdd1bf..4a93244 100644
--- a/src/eap_common/eap_sim_common.c
+++ b/src/eap_common/eap_sim_common.c
@@ -945,10 +945,15 @@
 	if (decrypted == NULL)
 		return NULL;
 
+#ifdef TEST_FUZZ
+		wpa_printf(MSG_INFO,
+			   "TEST: Skip AES-128-CBC decryption for fuzz testing");
+#else /* TEST_FUZZ */
 	if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
 		os_free(decrypted);
 		return NULL;
 	}
+#endif /* TEST_FUZZ */
 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
 		    decrypted, encr_data_len);
 
@@ -1214,6 +1219,10 @@
 	    os_memcmp(id, anonymous_id_prefix, anonymous_id_len) == 0)
 		return 1; /* 'anonymous@realm' */
 
+	if (id_len > anonymous_id_len + 1 &&
+	    os_memcmp(id + 1, anonymous_id_prefix, anonymous_id_len) == 0)
+		return 1; /* 'Xanonymous@realm' where X is an EAP method code */
+
 	if (id_len > 1 && id[0] == '@')
 		return 1; /* '@realm' */
 
diff --git a/src/eap_common/eap_teap_common.c b/src/eap_common/eap_teap_common.c
new file mode 100644
index 0000000..ffb9a62
--- /dev/null
+++ b/src/eap_common/eap_teap_common.c
@@ -0,0 +1,744 @@
+/*
+ * EAP-TEAP common helper functions (RFC 7170)
+ * Copyright (c) 2008-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 "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/tls.h"
+#include "eap_defs.h"
+#include "eap_teap_common.h"
+
+
+static int tls_cipher_suite_mac_sha384(u16 cs);
+
+
+void eap_teap_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
+{
+	struct teap_tlv_hdr hdr;
+
+	hdr.tlv_type = host_to_be16(type);
+	hdr.length = host_to_be16(len);
+	wpabuf_put_data(buf, &hdr, sizeof(hdr));
+}
+
+
+void eap_teap_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len)
+{
+	eap_teap_put_tlv_hdr(buf, type, len);
+	wpabuf_put_data(buf, data, len);
+}
+
+
+void eap_teap_put_tlv_buf(struct wpabuf *buf, u16 type,
+			  const struct wpabuf *data)
+{
+	eap_teap_put_tlv_hdr(buf, type, wpabuf_len(data));
+	wpabuf_put_buf(buf, data);
+}
+
+
+struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf)
+{
+	struct wpabuf *e;
+
+	if (!buf)
+		return NULL;
+
+	/* Encapsulate EAP packet in EAP-Payload TLV */
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add EAP-Payload TLV");
+	e = wpabuf_alloc(sizeof(struct teap_tlv_hdr) + wpabuf_len(buf));
+	if (!e) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TEAP: Failed to allocate memory for TLV encapsulation");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	eap_teap_put_tlv_buf(e, TEAP_TLV_MANDATORY | TEAP_TLV_EAP_PAYLOAD, buf);
+	wpabuf_free(buf);
+
+	/* TODO: followed by optional TLVs associated with the EAP packet */
+
+	return e;
+}
+
+
+static int eap_teap_tls_prf(u16 tls_cs, const u8 *secret, size_t secret_len,
+			    const char *label, const u8 *seed, size_t seed_len,
+			    u8 *out, size_t outlen)
+{
+	/* TODO: TLS-PRF for TLSv1.3 */
+	if (tls_cipher_suite_mac_sha384(tls_cs))
+		return tls_prf_sha384(secret, secret_len, label, seed, seed_len,
+				      out, outlen);
+	return tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+			      out, outlen);
+}
+
+
+int eap_teap_derive_eap_msk(u16 tls_cs, const u8 *simck, u8 *msk)
+{
+	/*
+	 * RFC 7170, Section 5.4: EAP Master Session Key Generation
+	 * MSK = TLS-PRF(S-IMCK[j], "Session Key Generating Function", 64)
+	 */
+
+	if (eap_teap_tls_prf(tls_cs, simck, EAP_TEAP_SIMCK_LEN,
+			     "Session Key Generating Function", (u8 *) "", 0,
+			     msk, EAP_TEAP_KEY_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Derived key (MSK)",
+			msk, EAP_TEAP_KEY_LEN);
+	return 0;
+}
+
+
+int eap_teap_derive_eap_emsk(u16 tls_cs, const u8 *simck, u8 *emsk)
+{
+	/*
+	 * RFC 7170, Section 5.4: EAP Master Session Key Generation
+	 * EMSK = TLS-PRF(S-IMCK[j],
+	 *        "Extended Session Key Generating Function", 64)
+	 */
+
+	if (eap_teap_tls_prf(tls_cs, simck, EAP_TEAP_SIMCK_LEN,
+			     "Extended Session Key Generating Function",
+			     (u8 *) "", 0, emsk, EAP_EMSK_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Derived key (EMSK)",
+			emsk, EAP_EMSK_LEN);
+	return 0;
+}
+
+
+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,
+			 const u8 *msk, size_t msk_len,
+			 const u8 *emsk, size_t emsk_len,
+			 u8 *s_imck_msk, u8 *cmk_msk,
+			 u8 *s_imck_emsk, u8 *cmk_emsk)
+{
+	u8 imsk[64], imck[EAP_TEAP_IMCK_LEN];
+	int res;
+
+	/*
+	 * RFC 7170, Section 5.2:
+	 * IMSK = First 32 octets of TLS-PRF(EMSK, "TEAPbindkey@ietf.org" |
+	 *                                   "\0" | 64)
+	 * (if EMSK is not available, MSK is used instead; if neither is
+	 * available, IMSK is 32 octets of zeros; MSK is truncated to 32 octets
+	 * or padded to 32 octets, if needed)
+	 * (64 is encoded as a 2-octet field in network byte order)
+	 *
+	 * S-IMCK[0] = session_key_seed
+	 * IMCK[j] = TLS-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+	 *                   IMSK[j], 60)
+	 * S-IMCK[j] = first 40 octets of IMCK[j]
+	 * CMK[j] = last 20 octets of IMCK[j]
+	 */
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK[j]", msk, msk_len);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK[j]", emsk, emsk_len);
+
+	if (emsk && emsk_len > 0) {
+		u8 context[3];
+
+		context[0] = 0;
+		context[1] = 0;
+		context[2] = 64;
+		if (eap_teap_tls_prf(tls_cs, emsk, emsk_len,
+				     "TEAPbindkey@ietf.org",
+				     context, sizeof(context), imsk, 64) < 0)
+			return -1;
+
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: IMSK from EMSK",
+				imsk, 32);
+
+		res = eap_teap_tls_prf(tls_cs,
+				       prev_s_imck_emsk, EAP_TEAP_SIMCK_LEN,
+				       "Inner Methods Compound Keys",
+				       imsk, 32, imck, EAP_TEAP_IMCK_LEN);
+		forced_memzero(imsk, sizeof(imsk));
+		if (res < 0)
+			return -1;
+
+		os_memcpy(s_imck_emsk, imck, EAP_TEAP_SIMCK_LEN);
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK S-IMCK[j]",
+				s_imck_emsk, EAP_TEAP_SIMCK_LEN);
+		os_memcpy(cmk_emsk, &imck[EAP_TEAP_SIMCK_LEN],
+			  EAP_TEAP_CMK_LEN);
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK CMK[j]",
+				cmk_emsk, EAP_TEAP_CMK_LEN);
+		forced_memzero(imck, EAP_TEAP_IMCK_LEN);
+	}
+
+	if (msk && msk_len > 0) {
+		size_t copy_len = msk_len;
+
+		os_memset(imsk, 0, 32); /* zero pad, if needed */
+		if (copy_len > 32)
+			copy_len = 32;
+		os_memcpy(imsk, msk, copy_len);
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: IMSK from MSK", imsk, 32);
+	} else {
+		os_memset(imsk, 0, 32);
+		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,
+			       "Inner Methods Compound Keys",
+			       imsk, 32, imck, EAP_TEAP_IMCK_LEN);
+	forced_memzero(imsk, sizeof(imsk));
+	if (res < 0)
+		return -1;
+
+	os_memcpy(s_imck_msk, imck, EAP_TEAP_SIMCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK S-IMCK[j]",
+			s_imck_msk, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(cmk_msk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK CMK[j]",
+			cmk_msk, EAP_TEAP_CMK_LEN);
+	forced_memzero(imck, EAP_TEAP_IMCK_LEN);
+
+	return 0;
+}
+
+
+static int tls_cipher_suite_match(const u16 *list, size_t count, u16 cs)
+{
+	size_t i;
+
+	for (i = 0; i < count; i++) {
+		if (list[i] == cs)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int tls_cipher_suite_mac_sha1(u16 cs)
+{
+	static const u16 sha1_cs[] = {
+		0x0005, 0x0007, 0x000a, 0x000d, 0x0010, 0x0013, 0x0016, 0x001b,
+		0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036,
+		0x0037, 0x0038, 0x0039, 0x003a, 0x0041, 0x0042, 0x0043, 0x0044,
+		0x0045, 0x0046, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089,
+		0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091,
+		0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099,
+		0x009a, 0x009b,
+		0xc002, 0xc003, 0xc004, 0xc005, 0xc007, 0xc008, 0xc009, 0xc009,
+		0xc00a, 0xc00c, 0xc00d, 0xc00e, 0xc00f, 0xc011, 0xc012, 0xc013,
+		0xc014, 0xc016, 0xc017, 0xc018, 0xc019, 0xc01a, 0xc01b, 0xc01c,
+		0xc014, 0xc01e, 0xc01f, 0xc020, 0xc021, 0xc022, 0xc033, 0xc034,
+		0xc035, 0xc036
+	};
+
+	return tls_cipher_suite_match(sha1_cs, ARRAY_SIZE(sha1_cs), cs);
+}
+
+
+static int tls_cipher_suite_mac_sha256(u16 cs)
+{
+	static const u16 sha256_cs[] = {
+		0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0067, 0x0068, 0x0069,
+		0x006a, 0x006b, 0x006c, 0x006d, 0x009c, 0x009e, 0x00a0, 0x00a2,
+		0x00a4, 0x00a6, 0x00a8, 0x00aa, 0x00ac, 0x00ae, 0x00b2, 0x00b6,
+		0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bd, 0x00be, 0x00be,
+		0x00bf, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5,
+		0x1301, 0x1303, 0x1304, 0x1305,
+		0xc023, 0xc025, 0xc027, 0xc029, 0xc02b, 0xc02d, 0xc02f, 0xc031,
+		0xc037, 0xc03c, 0xc03e, 0xc040, 0xc040, 0xc042, 0xc044, 0xc046,
+		0xc048, 0xc04a, 0xc04c, 0xc04e, 0xc050, 0xc052, 0xc054, 0xc056,
+		0xc058, 0xc05a, 0xc05c, 0xc05e, 0xc060, 0xc062, 0xc064, 0xc066,
+		0xc068, 0xc06a, 0xc06c, 0xc06e, 0xc070, 0xc072, 0xc074, 0xc076,
+		0xc078, 0xc07a, 0xc07c, 0xc07e, 0xc080, 0xc082, 0xc084, 0xc086,
+		0xc088, 0xc08a, 0xc08c, 0xc08e, 0xc090, 0xc092, 0xc094, 0xc096,
+		0xc098, 0xc09a, 0xc0b0, 0xc0b2, 0xc0b4,
+		0xcca8, 0xcca9, 0xccaa, 0xccab, 0xccac, 0xccad, 0xccae,
+		0xd001, 0xd003, 0xd005
+	};
+
+	return tls_cipher_suite_match(sha256_cs, ARRAY_SIZE(sha256_cs), cs);
+}
+
+
+static int tls_cipher_suite_mac_sha384(u16 cs)
+{
+	static const u16 sha384_cs[] = {
+		0x009d, 0x009f, 0x00a1, 0x00a3, 0x00a5, 0x00a7, 0x00a9, 0x00ab,
+		0x00ad, 0x00af, 0x00b3, 0x00b7, 0x1302,
+		0xc024, 0xc026, 0xc028, 0xc02a, 0xc02c, 0xc02e, 0xc030, 0xc032,
+		0xc038, 0xc03d, 0xc03f, 0xc041, 0xc043, 0xc045, 0xc047, 0xc049,
+		0xc04b, 0xc04d, 0xc04f, 0xc051, 0xc053, 0xc055, 0xc057, 0xc059,
+		0xc05b, 0xc05d, 0xc05f, 0xc061, 0xc063, 0xc065, 0xc067, 0xc069,
+		0xc06b, 0xc06d, 0xc06f, 0xc071, 0xc073, 0xc075, 0xc077, 0xc079,
+		0xc07b, 0xc07d, 0xc07f, 0xc081, 0xc083, 0xc085, 0xc087, 0xc089,
+		0xc08b, 0xc08d, 0xc08f, 0xc091, 0xc093, 0xc095, 0xc097, 0xc099,
+		0xc09b, 0xc0b1, 0xc0b3, 0xc0b5,
+		0xd002
+	};
+
+	return tls_cipher_suite_match(sha384_cs, ARRAY_SIZE(sha384_cs), cs);
+}
+
+
+static int eap_teap_tls_mac(u16 tls_cs, const u8 *cmk, size_t cmk_len,
+			    const u8 *buffer, size_t buffer_len,
+			    u8 *mac, size_t mac_len)
+{
+	int res;
+	u8 tmp[48];
+
+	os_memset(tmp, 0, sizeof(tmp));
+	os_memset(mac, 0, mac_len);
+
+	if (tls_cipher_suite_mac_sha1(tls_cs)) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA1");
+		res = hmac_sha1(cmk, cmk_len, buffer, buffer_len, tmp);
+	} else if (tls_cipher_suite_mac_sha256(tls_cs)) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA256");
+		res = hmac_sha256(cmk, cmk_len, buffer, buffer_len, tmp);
+	} else if (tls_cipher_suite_mac_sha384(tls_cs)) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA384");
+		res = hmac_sha384(cmk, cmk_len, buffer, buffer_len, tmp);
+	} else {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Unsupported TLS cipher suite 0x%04x",
+			   tls_cs);
+		res = -1;
+	}
+	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);
+	return 0;
+}
+
+
+int eap_teap_compound_mac(u16 tls_cs, const struct teap_tlv_crypto_binding *cb,
+			  const struct wpabuf *server_outer_tlvs,
+			  const struct wpabuf *peer_outer_tlvs,
+			  const u8 *cmk, u8 *compound_mac)
+{
+	u8 *pos, *buffer;
+	size_t bind_len, buffer_len;
+	struct teap_tlv_crypto_binding *tmp_cb;
+	int res;
+
+	/* RFC 7170, Section 5.3 */
+	bind_len = sizeof(struct teap_tlv_hdr) + be_to_host16(cb->length);
+	buffer_len = bind_len + 1;
+	if (server_outer_tlvs)
+		buffer_len += wpabuf_len(server_outer_tlvs);
+	if (peer_outer_tlvs)
+		buffer_len += wpabuf_len(peer_outer_tlvs);
+	buffer = os_malloc(buffer_len);
+	if (!buffer)
+		return -1;
+
+	pos = buffer;
+	/* 1. The entire Crypto-Binding TLV attribute with both the EMSK and MSK
+	 * Compound MAC fields zeroed out. */
+	os_memcpy(pos, cb, bind_len);
+	pos += bind_len;
+	tmp_cb = (struct teap_tlv_crypto_binding *) buffer;
+	os_memset(tmp_cb->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
+	os_memset(tmp_cb->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
+
+	/* 2. The EAP Type sent by the other party in the first TEAP message. */
+	/* This is supposed to be the EAP Type sent by the other party in the
+	 * first TEAP message, but since we cannot get here without having
+	 * successfully negotiated use of TEAP, this can only be the fixed EAP
+	 * Type of TEAP. */
+	*pos++ = EAP_TYPE_TEAP;
+
+	/* 3. All the Outer TLVs from the first TEAP message sent by EAP server
+	 * to peer. */
+	if (server_outer_tlvs) {
+		os_memcpy(pos, wpabuf_head(server_outer_tlvs),
+			  wpabuf_len(server_outer_tlvs));
+		pos += wpabuf_len(server_outer_tlvs);
+	}
+
+	/* 4. All the Outer TLVs from the first TEAP message sent by the peer to
+	 * the EAP server. */
+	if (peer_outer_tlvs) {
+		os_memcpy(pos, wpabuf_head(peer_outer_tlvs),
+			  wpabuf_len(peer_outer_tlvs));
+		pos += wpabuf_len(peer_outer_tlvs);
+	}
+
+	buffer_len = pos - buffer;
+
+	wpa_hexdump_key(MSG_MSGDUMP,
+			"EAP-TEAP: CMK for Compound MAC calculation",
+			cmk, EAP_TEAP_CMK_LEN);
+	wpa_hexdump(MSG_MSGDUMP,
+		    "EAP-TEAP: BUFFER for Compound MAC calculation",
+		    buffer, buffer_len);
+	res = eap_teap_tls_mac(tls_cs, cmk, EAP_TEAP_CMK_LEN,
+			       buffer, buffer_len,
+			       compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+	os_free(buffer);
+
+	return res;
+}
+
+
+int eap_teap_parse_tlv(struct eap_teap_tlv_parse *tlv,
+		       int tlv_type, u8 *pos, size_t len)
+{
+	switch (tlv_type) {
+	case TEAP_TLV_IDENTITY_TYPE:
+		if (len < 2) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short Identity-Type TLV");
+			tlv->result = TEAP_STATUS_FAILURE;
+			break;
+		}
+		tlv->identity_type = WPA_GET_BE16(pos);
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Identity-Type: %u",
+			   tlv->identity_type);
+		break;
+	case TEAP_TLV_RESULT:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Result TLV", pos, len);
+		if (tlv->result) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one Result TLV in the message");
+			tlv->result = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		if (len < 2) {
+			wpa_printf(MSG_INFO, "EAP-TEAP: Too short Result TLV");
+			tlv->result = TEAP_STATUS_FAILURE;
+			break;
+		}
+		tlv->result = WPA_GET_BE16(pos);
+		if (tlv->result != TEAP_STATUS_SUCCESS &&
+		    tlv->result != TEAP_STATUS_FAILURE) {
+			wpa_printf(MSG_INFO, "EAP-TEAP: Unknown Result %d",
+				   tlv->result);
+			tlv->result = TEAP_STATUS_FAILURE;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Result: %s",
+			   tlv->result == TEAP_STATUS_SUCCESS ?
+			   "Success" : "Failure");
+		break;
+	case TEAP_TLV_NAK:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: NAK TLV", pos, len);
+		if (len < 6) {
+			wpa_printf(MSG_INFO, "EAP-TEAP: Too short NAK TLV");
+			tlv->result = TEAP_STATUS_FAILURE;
+			break;
+		}
+		tlv->nak = pos;
+		tlv->nak_len = len;
+		break;
+	case TEAP_TLV_ERROR:
+		if (len < 4) {
+			wpa_printf(MSG_INFO, "EAP-TEAP: Too short Error TLV");
+			tlv->result = TEAP_STATUS_FAILURE;
+			break;
+		}
+		tlv->error_code = WPA_GET_BE32(pos);
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Error: %u", tlv->error_code);
+		break;
+	case TEAP_TLV_REQUEST_ACTION:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Request-Action TLV",
+			    pos, len);
+		if (tlv->request_action) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one Request-Action TLV in the message");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		if (len < 2) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short Request-Action TLV");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			break;
+		}
+		tlv->request_action_status = pos[0];
+		tlv->request_action = pos[1];
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Request-Action: Status=%u Action=%u",
+			   tlv->request_action_status, tlv->request_action);
+		break;
+	case TEAP_TLV_EAP_PAYLOAD:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EAP-Payload TLV",
+			    pos, len);
+		if (tlv->eap_payload_tlv) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one EAP-Payload TLV in the message");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		tlv->eap_payload_tlv = pos;
+		tlv->eap_payload_tlv_len = len;
+		break;
+	case TEAP_TLV_INTERMEDIATE_RESULT:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Intermediate-Result TLV",
+			    pos, len);
+		if (len < 2) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short Intermediate-Result TLV");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			break;
+		}
+		if (tlv->iresult) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one Intermediate-Result TLV in the message");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		tlv->iresult = WPA_GET_BE16(pos);
+		if (tlv->iresult != TEAP_STATUS_SUCCESS &&
+		    tlv->iresult != TEAP_STATUS_FAILURE) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Unknown Intermediate Result %d",
+				   tlv->iresult);
+			tlv->iresult = TEAP_STATUS_FAILURE;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Intermediate Result: %s",
+			   tlv->iresult == TEAP_STATUS_SUCCESS ?
+			   "Success" : "Failure");
+		break;
+	case TEAP_TLV_PAC:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: PAC TLV", pos, len);
+		if (tlv->pac) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one PAC TLV in the message");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		tlv->pac = pos;
+		tlv->pac_len = len;
+		break;
+	case TEAP_TLV_CRYPTO_BINDING:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Crypto-Binding TLV",
+			    pos, len);
+		if (tlv->crypto_binding) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one Crypto-Binding TLV in the message");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		tlv->crypto_binding_len = sizeof(struct teap_tlv_hdr) + len;
+		if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short Crypto-Binding TLV");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		tlv->crypto_binding = (struct teap_tlv_crypto_binding *)
+			(pos - sizeof(struct teap_tlv_hdr));
+		break;
+	case TEAP_TLV_BASIC_PASSWORD_AUTH_REQ:
+		wpa_hexdump_ascii(MSG_MSGDUMP,
+				  "EAP-TEAP: Basic-Password-Auth-Req TLV",
+				  pos, len);
+		if (tlv->basic_auth_req) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one Basic-Password-Auth-Req TLV in the message");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		tlv->basic_auth_req = pos;
+		tlv->basic_auth_req_len = len;
+		break;
+	case TEAP_TLV_BASIC_PASSWORD_AUTH_RESP:
+		wpa_hexdump_ascii(MSG_MSGDUMP,
+				  "EAP-TEAP: Basic-Password-Auth-Resp TLV",
+				  pos, len);
+		if (tlv->basic_auth_resp) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: More than one Basic-Password-Auth-Resp TLV in the message");
+			tlv->iresult = TEAP_STATUS_FAILURE;
+			return -2;
+		}
+		tlv->basic_auth_resp = pos;
+		tlv->basic_auth_resp_len = len;
+		break;
+	default:
+		/* Unknown TLV */
+		return -1;
+	}
+
+	return 0;
+}
+
+
+const char * eap_teap_tlv_type_str(enum teap_tlv_types type)
+{
+	switch (type) {
+	case TEAP_TLV_AUTHORITY_ID:
+		return "Authority-ID";
+	case TEAP_TLV_IDENTITY_TYPE:
+		return "Identity-Type";
+	case TEAP_TLV_RESULT:
+		return "Result";
+	case TEAP_TLV_NAK:
+		return "NAK";
+	case TEAP_TLV_ERROR:
+		return "Error";
+	case TEAP_TLV_CHANNEL_BINDING:
+		return "Channel-Binding";
+	case TEAP_TLV_VENDOR_SPECIFIC:
+		return "Vendor-Specific";
+	case TEAP_TLV_REQUEST_ACTION:
+		return "Request-Action";
+	case TEAP_TLV_EAP_PAYLOAD:
+		return "EAP-Payload";
+	case TEAP_TLV_INTERMEDIATE_RESULT:
+		return "Intermediate-Result";
+	case TEAP_TLV_PAC:
+		return "PAC";
+	case TEAP_TLV_CRYPTO_BINDING:
+		return "Crypto-Binding";
+	case TEAP_TLV_BASIC_PASSWORD_AUTH_REQ:
+		return "Basic-Password-Auth-Req";
+	case TEAP_TLV_BASIC_PASSWORD_AUTH_RESP:
+		return "Basic-Password-Auth-Resp";
+	case TEAP_TLV_PKCS7:
+		return "PKCS#7";
+	case TEAP_TLV_PKCS10:
+		return "PKCS#10";
+	case TEAP_TLV_TRUSTED_SERVER_ROOT:
+		return "Trusted-Server-Root";
+	}
+
+	return "?";
+}
+
+
+struct wpabuf * eap_teap_tlv_result(int status, int intermediate)
+{
+	struct wpabuf *buf;
+	struct teap_tlv_result *result;
+
+	if (status != TEAP_STATUS_FAILURE && status != TEAP_STATUS_SUCCESS)
+		return NULL;
+
+	buf = wpabuf_alloc(sizeof(*result));
+	if (!buf)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add %sResult TLV(status=%s)",
+		   intermediate ? "Intermediate-" : "",
+		   status == TEAP_STATUS_SUCCESS ? "Success" : "Failure");
+	result = wpabuf_put(buf, sizeof(*result));
+	result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+					(intermediate ?
+					 TEAP_TLV_INTERMEDIATE_RESULT :
+					 TEAP_TLV_RESULT));
+	result->length = host_to_be16(2);
+	result->status = host_to_be16(status);
+	return buf;
+}
+
+
+struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(4 + 4);
+	if (!buf)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Error TLV(Error Code=%d)",
+		   error);
+	wpabuf_put_be16(buf, TEAP_TLV_MANDATORY | TEAP_TLV_ERROR);
+	wpabuf_put_be16(buf, 4);
+	wpabuf_put_be32(buf, error);
+	return buf;
+}
+
+
+struct wpabuf * eap_teap_tlv_identity_type(enum teap_identity_types id)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(4 + 2);
+	if (!buf)
+		return NULL;
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Add Identity-Type TLV(Identity-Type=%d)", id);
+	wpabuf_put_be16(buf, TEAP_TLV_IDENTITY_TYPE);
+	wpabuf_put_be16(buf, 2);
+	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
new file mode 100644
index 0000000..3a25879
--- /dev/null
+++ b/src/eap_common/eap_teap_common.h
@@ -0,0 +1,230 @@
+/*
+ * EAP-TEAP definitions (RFC 7170)
+ * 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_H
+#define EAP_TEAP_H
+
+#define EAP_TEAP_VERSION 1
+#define EAP_TEAP_KEY_LEN 64
+#define EAP_TEAP_IMCK_LEN 60
+#define EAP_TEAP_SIMCK_LEN 40
+#define EAP_TEAP_CMK_LEN 20
+#define EAP_TEAP_COMPOUND_MAC_LEN 20
+#define EAP_TEAP_NONCE_LEN 32
+
+#define TEAP_TLS_EXPORTER_LABEL_SKS "EXPORTER: teap session key seed"
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 7170: Section 4.2.12.1 - Formats for PAC Attributes
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general TLV format (Section 4.2.1).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/* 6 - Reserved */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_attr_hdr {
+	be16 type;
+	be16 len;
+} STRUCT_PACKED;
+
+struct teap_tlv_hdr {
+	be16 tlv_type;
+	be16 length;
+} STRUCT_PACKED;
+
+/* Result TLV and Intermediate-Result TLV */
+struct teap_tlv_result {
+	be16 tlv_type;
+	be16 length;
+	be16 status;
+	/* for Intermediate-Result TLV, followed by optional TLVs */
+} STRUCT_PACKED;
+
+struct teap_tlv_nak {
+	be16 tlv_type;
+	be16 length;
+	be32 vendor_id;
+	be16 nak_type;
+	/* followed by optional TLVs */
+} STRUCT_PACKED;
+
+struct teap_tlv_crypto_binding {
+	be16 tlv_type; /* TLV Type[14b] and M/R flags */
+	be16 length;
+	u8 reserved;
+	u8 version;
+	u8 received_version;
+	u8 subtype; /* Flags[4b] and Sub-Type[4b] */
+	u8 nonce[EAP_TEAP_NONCE_LEN];
+	u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+	u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+} STRUCT_PACKED;
+
+struct teap_tlv_request_action {
+	be16 tlv_type;
+	be16 length;
+	u8 status;
+	u8 action;
+	/* followed by optional TLVs */
+} STRUCT_PACKED;
+
+enum teap_request_action {
+	TEAP_REQUEST_ACTION_PROCESS_TLV = 1,
+	TEAP_REQUEST_ACTION_NEGOTIATE_EAP = 2,
+};
+
+/* PAC TLV with PAC-Acknowledgement TLV attribute */
+struct teap_tlv_pac_ack {
+	be16 tlv_type;
+	be16 length;
+	be16 pac_type;
+	be16 pac_len;
+	be16 result;
+} STRUCT_PACKED;
+
+struct teap_attr_pac_type {
+	be16 type; /* PAC_TYPE_PAC_TYPE */
+	be16 length; /* 2 */
+	be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define TEAP_CRYPTO_BINDING_EMSK_CMAC 1
+#define TEAP_CRYPTO_BINDING_MSK_CMAC 2
+#define TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC 3
+
+
+#define EAP_TEAP_PAC_KEY_LEN 48
+
+/* RFC 7170: 4.2.12.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+
+
+/* RFC 7170, 4.2.1: General TLV Format */
+enum teap_tlv_types {
+	TEAP_TLV_AUTHORITY_ID = 1,
+	TEAP_TLV_IDENTITY_TYPE = 2,
+	TEAP_TLV_RESULT = 3,
+	TEAP_TLV_NAK = 4,
+	TEAP_TLV_ERROR = 5,
+	TEAP_TLV_CHANNEL_BINDING = 6,
+	TEAP_TLV_VENDOR_SPECIFIC = 7,
+	TEAP_TLV_REQUEST_ACTION = 8,
+	TEAP_TLV_EAP_PAYLOAD = 9,
+	TEAP_TLV_INTERMEDIATE_RESULT = 10,
+	TEAP_TLV_PAC = 11,
+	TEAP_TLV_CRYPTO_BINDING = 12,
+	TEAP_TLV_BASIC_PASSWORD_AUTH_REQ = 13,
+	TEAP_TLV_BASIC_PASSWORD_AUTH_RESP = 14,
+	TEAP_TLV_PKCS7 = 15,
+	TEAP_TLV_PKCS10 = 16,
+	TEAP_TLV_TRUSTED_SERVER_ROOT = 17,
+};
+
+enum teap_tlv_result_status {
+	TEAP_STATUS_SUCCESS = 1,
+	TEAP_STATUS_FAILURE = 2
+};
+
+/* Identity-Type values within Identity-Type TLV */
+enum teap_identity_types {
+	TEAP_IDENTITY_TYPE_USER = 1,
+	TEAP_IDENTITY_TYPE_MACHINE = 2,
+};
+
+#define TEAP_TLV_MANDATORY 0x8000
+#define TEAP_TLV_TYPE_MASK 0x3fff
+
+/* RFC 7170, 4.2.6: Error TLV */
+enum teap_error_codes {
+	TEAP_ERROR_INNER_METHOD = 1001,
+	TEAP_ERROR_UNSPEC_AUTH_INFRA_PROBLEM = 1002,
+	TEAP_ERROR_UNSPEC_AUTHENTICATION_FAILURE = 1003,
+	TEAP_ERROR_UNSPEC_AUTHORIZATION_FAILURE = 1004,
+	TEAP_ERROR_USER_ACCOUNT_CRED_UNAVAILABLE = 1005,
+	TEAP_ERROR_USER_ACCOUNT_EXPIRED = 1006,
+	TEAP_ERROR_USER_ACCOUNT_LOCKED_TRY_AGAIN_LATER = 1007,
+	TEAP_ERROR_USER_ACCOUNT_LOCKED_ADMIN_REQ = 1008,
+	TEAP_ERROR_TUNNEL_COMPROMISE_ERROR = 2001,
+	TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED = 2002,
+};
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_teap_tlv_parse {
+	u8 *eap_payload_tlv;
+	size_t eap_payload_tlv_len;
+	struct teap_tlv_crypto_binding *crypto_binding;
+	size_t crypto_binding_len;
+	int iresult;
+	int result;
+	u8 *nak;
+	size_t nak_len;
+	u8 request_action;
+	u8 request_action_status;
+	u8 *pac;
+	size_t pac_len;
+	u8 *basic_auth_req;
+	size_t basic_auth_req_len;
+	u8 *basic_auth_resp;
+	size_t basic_auth_resp_len;
+	u32 error_code;
+	u16 identity_type;
+};
+
+void eap_teap_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_teap_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len);
+void eap_teap_put_tlv_buf(struct wpabuf *buf, u16 type,
+			  const struct wpabuf *data);
+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,
+			 const u8 *msk, size_t msk_len,
+			 const u8 *emsk, size_t emsk_len,
+			 u8 *s_imck_msk, u8 *cmk_msk,
+			 u8 *s_imck_emsk, u8 *cmk_emsk);
+int eap_teap_compound_mac(u16 tls_cs, const struct teap_tlv_crypto_binding *cb,
+			  const struct wpabuf *server_outer_tlvs,
+			  const struct wpabuf *peer_outer_tlvs,
+			  const u8 *cmk, u8 *compound_mac);
+int eap_teap_parse_tlv(struct eap_teap_tlv_parse *tlv,
+		       int tlv_type, u8 *pos, size_t len);
+const char * eap_teap_tlv_type_str(enum teap_tlv_types type);
+struct wpabuf * eap_teap_tlv_result(int status, int intermediate);
+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.c b/src/eap_peer/eap.c
index 974c475..c78b214 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines (RFC 4137)
- * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -32,12 +32,13 @@
 #define STATE_MACHINE_DATA struct eap_sm
 #define STATE_MACHINE_DEBUG_PREFIX "EAP"
 
-#define EAP_MAX_AUTH_ROUNDS 50
+#define EAP_MAX_AUTH_ROUNDS 100
+#define EAP_MAX_AUTH_ROUNDS_SHORT 50
 #define EAP_CLIENT_TIMEOUT_DEFAULT 60
 
 
 static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
-				  EapType method);
+				  enum eap_type method);
 static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id);
 static void eap_sm_processIdentity(struct eap_sm *sm,
 				   const struct wpabuf *req);
@@ -260,10 +261,12 @@
 	 */
 	sm->ignore = 0;
 	sm->num_rounds = 0;
+	sm->num_rounds_short = 0;
 	sm->prev_failure = 0;
 	sm->expected_failure = 0;
 	sm->reauthInit = FALSE;
 	sm->erp_seq = (u32) -1;
+	sm->use_machine_cred = 0;
 }
 
 
@@ -276,6 +279,7 @@
 {
 	SM_ENTRY(EAP, DISABLED);
 	sm->num_rounds = 0;
+	sm->num_rounds_short = 0;
 	/*
 	 * RFC 4137 does not describe clearing of idleWhile here, but doing so
 	 * allows the timer tick to be stopped more quickly when EAP is not in
@@ -309,6 +313,10 @@
 	/* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
 	eap_sm_parseEapReq(sm, eapReqData);
 	sm->num_rounds++;
+	if (!eapReqData || wpabuf_len(eapReqData) < 20)
+		sm->num_rounds_short++;
+	else
+		sm->num_rounds_short = 0;
 }
 
 
@@ -319,7 +327,7 @@
 SM_STATE(EAP, GET_METHOD)
 {
 	int reinit;
-	EapType method;
+	enum eap_type method;
 	const struct eap_method *eap_method;
 
 	SM_ENTRY(EAP, GET_METHOD);
@@ -815,7 +823,8 @@
 	wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)",
 		   erp->keyname_nai, erp->next_seq);
 
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+	msg = eap_msg_alloc(EAP_VENDOR_IETF,
+			    (enum eap_type) EAP_ERP_TYPE_REAUTH,
 			    1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
 			    EAP_CODE_INITIATE, eap_id);
 	if (msg == NULL)
@@ -949,6 +958,8 @@
 	SM_ENTRY(EAP, SEND_RESPONSE);
 	wpabuf_free(sm->lastRespData);
 	if (sm->eapRespData) {
+		if (wpabuf_len(sm->eapRespData) >= 20)
+			sm->num_rounds_short = 0;
 		if (sm->workaround)
 			os_memcpy(sm->last_sha1, sm->req_sha1, 20);
 		sm->lastId = sm->reqId;
@@ -1341,6 +1352,14 @@
 			sm->num_rounds++;
 			SM_ENTER_GLOBAL(EAP, FAILURE);
 		}
+	} else if (sm->num_rounds_short > EAP_MAX_AUTH_ROUNDS_SHORT) {
+		if (sm->num_rounds_short == EAP_MAX_AUTH_ROUNDS_SHORT + 1) {
+			wpa_msg(sm->msg_ctx, MSG_INFO,
+				"EAP: more than %d authentication rounds (short) - abort",
+				EAP_MAX_AUTH_ROUNDS_SHORT);
+			sm->num_rounds_short++;
+			SM_ENTER_GLOBAL(EAP, FAILURE);
+		}
 	} else {
 		/* Local transitions */
 		eap_peer_sm_step_local(sm);
@@ -1349,7 +1368,7 @@
 
 
 static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
-				  EapType method)
+				  enum eap_type method)
 {
 	if (!eap_allowed_method(sm, vendor, method)) {
 		wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: "
@@ -1595,13 +1614,13 @@
 static int eap_sm_set_scard_pin(struct eap_sm *sm,
 				struct eap_peer_config *conf)
 {
-	if (scard_set_pin(sm->scard_ctx, conf->pin)) {
+	if (scard_set_pin(sm->scard_ctx, conf->cert.pin)) {
 		/*
 		 * Make sure the same PIN is not tried again in order to avoid
 		 * blocking SIM.
 		 */
-		os_free(conf->pin);
-		conf->pin = NULL;
+		os_free(conf->cert.pin);
+		conf->cert.pin = NULL;
 
 		wpa_printf(MSG_WARNING, "PIN validation failed");
 		eap_sm_request_pin(sm);
@@ -1657,6 +1676,11 @@
 		identity_len = config->anonymous_identity_len;
 		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
 				  identity, identity_len);
+	} else if (sm->use_machine_cred) {
+		identity = config->machine_identity;
+		identity_len = config->machine_identity_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using machine identity",
+				  identity, identity_len);
 	} else {
 		identity = config->identity;
 		identity_len = config->identity_len;
@@ -2097,12 +2121,8 @@
 			}
 		}
 
-		sm->eapol_cb->notify_cert(sm->eapol_ctx,
-					  data->peer_cert.depth,
-					  data->peer_cert.subject,
-					  data->peer_cert.altsubject,
-					  data->peer_cert.num_altsubject,
-					  hash_hex, data->peer_cert.cert);
+		sm->eapol_cb->notify_cert(sm->eapol_ctx, &data->peer_cert,
+					  hash_hex);
 		break;
 	case TLS_ALERT:
 		if (data->alert.is_local)
@@ -2604,10 +2624,12 @@
 
 static int eap_allowed_phase2_type(int vendor, int type)
 {
+	if (vendor == EAP_VENDOR_HOSTAP)
+		return 1;
 	if (vendor != EAP_VENDOR_IETF)
 		return 0;
 	return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
-		type != EAP_TYPE_FAST;
+		type != EAP_TYPE_FAST && type != EAP_TYPE_TEAP;
 }
 
 
@@ -2666,7 +2688,7 @@
 		if (eap_allowed_phase2_type(vendor, method)) {
 			if (vendor == EAP_VENDOR_IETF &&
 			    method == EAP_TYPE_TLS && config &&
-			    config->private_key2 == NULL)
+			    !config->phase2_cert.private_key)
 				continue;
 			buf[*count].vendor = vendor;
 			buf[*count].method = method;
@@ -2725,8 +2747,15 @@
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
 {
 	struct eap_peer_config *config = eap_get_config(sm);
-	if (config == NULL)
+
+	if (!config)
 		return NULL;
+
+	if (sm->use_machine_cred) {
+		*len = config->machine_identity_len;
+		return config->machine_identity;
+	}
+
 	*len = config->identity_len;
 	return config->identity;
 }
@@ -2736,14 +2765,24 @@
 				struct eap_peer_config *config)
 {
 	char *name;
+	const u8 *password;
+	size_t password_len;
 
-	if (config->password == NULL)
+	if (sm->use_machine_cred) {
+		password = config->machine_password;
+		password_len = config->machine_password_len;
+	} else {
+		password = config->password;
+		password_len = config->password_len;
+	}
+
+	if (!password)
 		return -1;
 
-	name = os_zalloc(config->password_len + 1);
-	if (name == NULL)
+	name = os_zalloc(password_len + 1);
+	if (!name)
 		return -1;
-	os_memcpy(name, config->password, config->password_len);
+	os_memcpy(name, password, password_len);
 
 	ext_password_free(sm->ext_pw_buf);
 	sm->ext_pw_buf = ext_password_get(sm->ext_pw, name);
@@ -2762,16 +2801,25 @@
 const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
 {
 	struct eap_peer_config *config = eap_get_config(sm);
-	if (config == NULL)
+
+	if (!config)
 		return NULL;
 
-	if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+	if ((sm->use_machine_cred &&
+	     (config->flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD)) ||
+	    (!sm->use_machine_cred &&
+	     (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD))) {
 		if (eap_get_ext_password(sm, config) < 0)
 			return NULL;
 		*len = wpabuf_len(sm->ext_pw_buf);
 		return wpabuf_head(sm->ext_pw_buf);
 	}
 
+	if (sm->use_machine_cred) {
+		*len = config->machine_password_len;
+		return config->machine_password;
+	}
+
 	*len = config->password_len;
 	return config->password;
 }
@@ -2789,10 +2837,14 @@
 const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
 {
 	struct eap_peer_config *config = eap_get_config(sm);
-	if (config == NULL)
+
+	if (!config)
 		return NULL;
 
-	if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+	if ((sm->use_machine_cred &&
+	     (config->flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD)) ||
+	    (!sm->use_machine_cred &&
+	     (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD))) {
 		if (eap_get_ext_password(sm, config) < 0)
 			return NULL;
 		if (hash)
@@ -2801,6 +2853,14 @@
 		return wpabuf_head(sm->ext_pw_buf);
 	}
 
+	if (sm->use_machine_cred) {
+		*len = config->machine_password_len;
+		if (hash)
+			*hash = !!(config->flags &
+				   EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH);
+		return config->machine_password;
+	}
+
 	*len = config->password_len;
 	if (hash)
 		*hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index d0837e3..acd70d0 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -16,6 +16,7 @@
 struct eap_sm;
 struct wpa_config_blob;
 struct wpabuf;
+struct tls_cert_data;
 
 struct eap_method_type {
 	int vendor;
@@ -226,16 +227,11 @@
 	/**
 	 * notify_cert - Notification of a peer certificate
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
-	 * @depth: Depth in certificate chain (0 = server)
-	 * @subject: Subject of the peer certificate
-	 * @altsubject: Select fields from AltSubject of the peer certificate
-	 * @num_altsubject: Number of altsubject values
+	 * @cert: Certificate information
 	 * @cert_hash: SHA-256 hash of the certificate
-	 * @cert: Peer certificate
 	 */
-	void (*notify_cert)(void *ctx, int depth, const char *subject,
-			    const char *altsubject[], int num_altsubject,
-			    const char *cert_hash, const struct wpabuf *cert);
+	void (*notify_cert)(void *ctx, struct tls_cert_data *cert,
+			    const char *cert_hash);
 
 	/**
 	 * notify_status - Notification of the current EAP state
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index ff88cf8..d50bc61 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -31,6 +31,7 @@
 	u8 emsk[EAP_EMSK_LEN];
 	u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
 	u8 auts[EAP_AKA_AUTS_LEN];
+	u8 reauth_mac[EAP_SIM_MAC_LEN];
 
 	int num_id_req, num_notification;
 	u8 *pseudonym;
@@ -931,8 +932,13 @@
 				     attr->checkcode_len)) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
 			   "message");
+#ifdef TEST_FUZZ
+		wpa_printf(MSG_INFO,
+			   "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing");
+#else /* TEST_FUZZ */
 		return eap_aka_client_error(data, id,
 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+#endif /* TEST_FUZZ */
 	}
 
 #ifdef EAP_AKA_PRIME
@@ -1064,8 +1070,13 @@
 	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
 			   "used invalid AT_MAC");
+#ifdef TEST_FUZZ
+		wpa_printf(MSG_INFO,
+			   "TEST: Ignore AT_MAC mismatch for fuzz testing");
+#else /* TEST_FUZZ */
 		return eap_aka_client_error(data, id,
 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+#endif /* TEST_FUZZ */
 	}
 
 	/* Old reauthentication identity must not be used anymore. In
@@ -1214,8 +1225,13 @@
 	if (attr->checkcode &&
 	    eap_aka_verify_checkcode(data, attr->checkcode,
 				     attr->checkcode_len)) {
+#ifdef TEST_FUZZ
+		wpa_printf(MSG_INFO,
+			   "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing");
+#else /* TEST_FUZZ */
 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
 			   "message");
+#endif /* TEST_FUZZ */
 		return eap_aka_client_error(data, id,
 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 	}
@@ -1235,6 +1251,14 @@
 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 	}
 
+	/* At this stage the received MAC has been verified. Use this MAC for
+	 * reauth Session-Id calculation if all other checks pass.
+	 * The peer does not use the local MAC but the received MAC in deriving
+	 * Session-Id. */
+	os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Server MAC",
+		    data->reauth_mac, EAP_SIM_MAC_LEN);
+
 	if (attr->encr_data == NULL || attr->iv == NULL) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
 			   "message did not include encrypted data");
@@ -1506,14 +1530,24 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	if (!data->reauth)
+		*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	else
+		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
 	id = os_malloc(*len);
 	if (id == NULL)
 		return NULL;
 
 	id[0] = data->eap_method;
-	os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
-	os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+	if (!data->reauth) {
+		os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+		os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn,
+			  EAP_AKA_AUTN_LEN);
+	} else {
+		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
+		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
+			  EAP_SIM_MAC_LEN);
+	}
 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
 
 	return id;
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 3a88f2a..3238f74 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -1,6 +1,6 @@
 /*
  * EAP peer configuration data
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,68 +10,9 @@
 #define EAP_CONFIG_H
 
 /**
- * struct eap_peer_config - EAP peer configuration/credentials
+ * struct eap_peer_cert_config - EAP peer certificate configuration/credential
  */
-struct eap_peer_config {
-	/**
-	 * identity - EAP Identity
-	 *
-	 * This field is used to set the real user identity or NAI (for
-	 * EAP-PSK/PAX/SAKE/GPSK).
-	 */
-	u8 *identity;
-
-	/**
-	 * identity_len - EAP Identity length
-	 */
-	size_t identity_len;
-
-	/**
-	 * anonymous_identity -  Anonymous EAP Identity
-	 *
-	 * This field is used for unencrypted use with EAP types that support
-	 * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
-	 * real identity (identity field) only to the authentication server.
-	 *
-	 * If not set, the identity field will be used for both unencrypted and
-	 * protected fields.
-	 *
-	 * This field can also be used with EAP-SIM/AKA/AKA' to store the
-	 * pseudonym identity.
-	 */
-	u8 *anonymous_identity;
-
-	/**
-	 * anonymous_identity_len - Length of anonymous_identity
-	 */
-	size_t anonymous_identity_len;
-
-	u8 *imsi_identity;
-	size_t imsi_identity_len;
-
-	/**
-	 * password - Password string for EAP
-	 *
-	 * This field can include either the plaintext password (default
-	 * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
-	 * presentation of the password) if flags field has
-	 * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
-	 * only be used with authentication mechanism that use this hash as the
-	 * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
-	 * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
-	 *
-	 * In addition, this field is used to configure a pre-shared key for
-	 * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
-	 * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
-	 * PSK.
-	 */
-	u8 *password;
-
-	/**
-	 * password_len - Length of password field
-	 */
-	size_t password_len;
-
+struct eap_peer_cert_config {
 	/**
 	 * ca_cert - File path to CA certificate file (PEM/DER)
 	 *
@@ -231,14 +172,6 @@
 	char *check_cert_subject;
 
 	/**
-	 * check_cert_subject2 - Constraint for server certificate subject fields
-	 *
-	 * This field is like check_cert_subject, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
-	 */
-	char *check_cert_subject2;
-
-	/**
 	 * altsubject_match - Constraint for server certificate alt. subject
 	 *
 	 * Semicolon separated string of entries to be matched against the
@@ -299,115 +232,181 @@
 	char *domain_match;
 
 	/**
-	 * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
+	 * pin - PIN for USIM, GSM SIM, and smartcards
 	 *
-	 * This file can have one or more trusted CA certificates. If ca_cert2
-	 * and ca_path2 are not included, server certificate will not be
-	 * verified. This is insecure and a trusted CA certificate should
-	 * always be configured. Full path to the file should be used since
-	 * working directory may change when wpa_supplicant is run in the
-	 * background.
+	 * This field is used to configure PIN for SIM and smartcards for
+	 * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+	 * smartcard is used for private key operations.
 	 *
-	 * This field is like ca_cert, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
-	 *
-	 * Alternatively, a named configuration blob can be used by setting
-	 * this to blob://blob_name.
+	 * If left out, this will be asked through control interface.
 	 */
-	char *ca_cert2;
+	char *pin;
 
 	/**
-	 * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+	 * engine - Enable OpenSSL engine (e.g., for smartcard access)
 	 *
-	 * This path may contain multiple CA certificates in OpenSSL format.
-	 * Common use for this is to point to system trusted CA list which is
-	 * often installed into directory like /etc/ssl/certs. If configured,
-	 * these certificates are added to the list of trusted CAs. ca_cert
-	 * may also be included in that case, but it is not required.
-	 *
-	 * This field is like ca_path, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
 	 */
-	char *ca_path2;
+	int engine;
 
 	/**
-	 * client_cert2 - File path to client certificate file
+	 * engine_id - Engine ID for OpenSSL engine
 	 *
-	 * This field is like client_cert, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
-	 * file should be used since working directory may change when
-	 * wpa_supplicant is run in the background.
+	 * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+	 * engine.
 	 *
-	 * Alternatively, a named configuration blob can be used by setting
-	 * this to blob://blob_name.
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
 	 */
-	char *client_cert2;
+	char *engine_id;
+
 
 	/**
-	 * private_key2 - File path to client private key file
+	 * key_id - Key ID for OpenSSL engine
 	 *
-	 * This field is like private_key, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
-	 * file should be used since working directory may change when
-	 * wpa_supplicant is run in the background.
-	 *
-	 * Alternatively, a named configuration blob can be used by setting
-	 * this to blob://blob_name.
+	 * This is used if private key operations for EAP-TLS are performed
+	 * using a smartcard.
 	 */
-	char *private_key2;
+	char *key_id;
 
 	/**
-	 * private_key2_passwd -  Password for private key file
+	 * cert_id - Cert ID for OpenSSL engine
 	 *
-	 * This field is like private_key_passwd, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 * This is used if the certificate operations for EAP-TLS are performed
+	 * using a smartcard.
 	 */
-	char *private_key2_passwd;
+	char *cert_id;
 
 	/**
-	 * dh_file2 - File path to DH/DSA parameters file (in PEM format)
+	 * ca_cert_id - CA Cert ID for OpenSSL engine
 	 *
-	 * This field is like dh_file, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
-	 * file should be used since working directory may change when
-	 * wpa_supplicant is run in the background.
-	 *
-	 * Alternatively, a named configuration blob can be used by setting
-	 * this to blob://blob_name.
+	 * This is used if the CA certificate for EAP-TLS is on a smartcard.
 	 */
-	char *dh_file2;
+	char *ca_cert_id;
 
 	/**
-	 * subject_match2 - Constraint for server certificate subject
+	 * ocsp - Whether to use/require OCSP to check server certificate
 	 *
-	 * This field is like subject_match, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 * 0 = do not use OCSP stapling (TLS certificate status extension)
+	 * 1 = try to use OCSP stapling, but not require response
+	 * 2 = require valid OCSP stapling response
 	 */
-	char *subject_match2;
+	int ocsp;
+};
+
+/**
+ * struct eap_peer_config - EAP peer configuration/credentials
+ */
+struct eap_peer_config {
+	/**
+	 * identity - EAP Identity
+	 *
+	 * This field is used to set the real user identity or NAI (for
+	 * EAP-PSK/PAX/SAKE/GPSK).
+	 */
+	u8 *identity;
 
 	/**
-	 * altsubject_match2 - Constraint for server certificate alt. subject
-	 *
-	 * This field is like altsubject_match, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 * identity_len - EAP Identity length
 	 */
-	char *altsubject_match2;
+	size_t identity_len;
 
 	/**
-	 * domain_suffix_match2 - Constraint for server domain name
+	 * anonymous_identity -  Anonymous EAP Identity
 	 *
-	 * This field is like domain_suffix_match, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 * This field is used for unencrypted use with EAP types that support
+	 * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+	 * real identity (identity field) only to the authentication server.
+	 *
+	 * If not set, the identity field will be used for both unencrypted and
+	 * protected fields.
+	 *
+	 * This field can also be used with EAP-SIM/AKA/AKA' to store the
+	 * pseudonym identity.
 	 */
-	char *domain_suffix_match2;
+	u8 *anonymous_identity;
 
 	/**
-	 * domain_match2 - Constraint for server domain name
-	 *
-	 * This field is like domain_match, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 * anonymous_identity_len - Length of anonymous_identity
 	 */
-	char *domain_match2;
+	size_t anonymous_identity_len;
+
+	u8 *imsi_identity;
+	size_t imsi_identity_len;
+
+	/**
+	 * machine_identity - EAP Identity for machine credential
+	 *
+	 * This field is used to set the machine identity or NAI for cases where
+	 * and explicit machine credential (instead of or in addition to a user
+	 * credential (from %identity) is needed.
+	 */
+	u8 *machine_identity;
+
+	/**
+	 * machine_identity_len - EAP Identity length for machine credential
+	 */
+	size_t machine_identity_len;
+
+	/**
+	 * password - Password string for EAP
+	 *
+	 * This field can include either the plaintext password (default
+	 * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
+	 * presentation of the password) if flags field has
+	 * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
+	 * only be used with authentication mechanism that use this hash as the
+	 * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
+	 * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+	 *
+	 * In addition, this field is used to configure a pre-shared key for
+	 * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
+	 * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
+	 * PSK.
+	 */
+	u8 *password;
+
+	/**
+	 * password_len - Length of password field
+	 */
+	size_t password_len;
+
+	/**
+	 * machine_password - Password string for EAP machine credential
+	 *
+	 * This field is used when machine credential based on username/password
+	 * is needed instead of a user credential (from %password). See
+	 * %password for more details on the format.
+	 */
+	u8 *machine_password;
+
+	/**
+	 * machine_password_len - Length of machine credential password field
+	 */
+	size_t machine_password_len;
+
+	/**
+	 * cert - Certificate parameters for Phase 1
+	 */
+	struct eap_peer_cert_config cert;
+
+	/**
+	 * phase2_cert - Certificate parameters for Phase 2
+	 *
+	 * This is like cert, but used for Phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST/TEAP tunnel) authentication.
+	 */
+	struct eap_peer_cert_config phase2_cert;
+
+	/**
+	 * machine_cert - Certificate parameters for Phase 2 machine credential
+	 *
+	 * This is like cert, but used for Phase 2 (inside EAP-TEAP tunnel)
+	 * authentication with machine credentials (while phase2_cert is used
+	 * for user credentials).
+	 */
+	struct eap_peer_cert_config machine_cert;
 
 	/**
 	 * eap_methods - Allowed EAP methods
@@ -496,6 +495,13 @@
 	char *phase2;
 
 	/**
+	 * machine_phase2 - Phase2 parameters for machine credentials
+	 *
+	 * See phase2 for more details.
+	 */
+	char *machine_phase2;
+
+	/**
 	 * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
 	 *
 	 * This field is used to configure PC/SC smartcard interface.
@@ -507,123 +513,6 @@
 	char *pcsc;
 
 	/**
-	 * pin - PIN for USIM, GSM SIM, and smartcards
-	 *
-	 * This field is used to configure PIN for SIM and smartcards for
-	 * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
-	 * smartcard is used for private key operations.
-	 *
-	 * If left out, this will be asked through control interface.
-	 */
-	char *pin;
-
-	/**
-	 * engine - Enable OpenSSL engine (e.g., for smartcard access)
-	 *
-	 * This is used if private key operations for EAP-TLS are performed
-	 * using a smartcard.
-	 */
-	int engine;
-
-	/**
-	 * engine_id - Engine ID for OpenSSL engine
-	 *
-	 * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
-	 * engine.
-	 *
-	 * This is used if private key operations for EAP-TLS are performed
-	 * using a smartcard.
-	 */
-	char *engine_id;
-
-	/**
-	 * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2)
-	 *
-	 * This is used if private key operations for EAP-TLS are performed
-	 * using a smartcard.
-	 *
-	 * This field is like engine, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
-	 */
-	int engine2;
-
-
-	/**
-	 * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2)
-	 *
-	 * This field is used to configure PIN for SIM and smartcards for
-	 * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
-	 * smartcard is used for private key operations.
-	 *
-	 * This field is like pin2, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
-	 *
-	 * If left out, this will be asked through control interface.
-	 */
-	char *pin2;
-
-	/**
-	 * engine2_id - Engine ID for OpenSSL engine (Phase 2)
-	 *
-	 * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
-	 * engine.
-	 *
-	 * This is used if private key operations for EAP-TLS are performed
-	 * using a smartcard.
-	 *
-	 * This field is like engine_id, but used for phase 2 (inside
-	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
-	 */
-	char *engine2_id;
-
-
-	/**
-	 * key_id - Key ID for OpenSSL engine
-	 *
-	 * This is used if private key operations for EAP-TLS are performed
-	 * using a smartcard.
-	 */
-	char *key_id;
-
-	/**
-	 * cert_id - Cert ID for OpenSSL engine
-	 *
-	 * This is used if the certificate operations for EAP-TLS are performed
-	 * using a smartcard.
-	 */
-	char *cert_id;
-
-	/**
-	 * ca_cert_id - CA Cert ID for OpenSSL engine
-	 *
-	 * This is used if the CA certificate for EAP-TLS is on a smartcard.
-	 */
-	char *ca_cert_id;
-
-	/**
-	 * key2_id - Key ID for OpenSSL engine (phase2)
-	 *
-	 * This is used if private key operations for EAP-TLS are performed
-	 * using a smartcard.
-	 */
-	char *key2_id;
-
-	/**
-	 * cert2_id - Cert ID for OpenSSL engine (phase2)
-	 *
-	 * This is used if the certificate operations for EAP-TLS are performed
-	 * using a smartcard.
-	 */
-	char *cert2_id;
-
-	/**
-	 * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2)
-	 *
-	 * This is used if the CA certificate for EAP-TLS is on a smartcard.
-	 */
-	char *ca_cert2_id;
-
-	/**
 	 * otp - One-time-password
 	 *
 	 * This field should not be set in configuration step. It is only used
@@ -751,6 +640,8 @@
 
 #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
 #define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1)
+#define EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH BIT(2)
+#define EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD BIT(3)
 	/**
 	 * flags - Network configuration flags (bitfield)
 	 *
@@ -760,19 +651,14 @@
 	 *         instead of plaintext password
 	 * bit 1 = password is stored in external storage; the value in the
 	 *         password field is the name of that external entry
+	 * bit 2 = machine password is represented as a 16-byte NtPasswordHash
+	 *         value instead of plaintext password
+	 * bit 3 = machine password is stored in external storage; the value in
+	 *         the password field is the name of that external entry
 	 */
 	u32 flags;
 
 	/**
-	 * ocsp - Whether to use/require OCSP to check server certificate
-	 *
-	 * 0 = do not use OCSP stapling (TLS certificate status extension)
-	 * 1 = try to use OCSP stapling, but not require response
-	 * 2 = require valid OCSP stapling response
-	 */
-	int ocsp;
-
-	/**
 	 * external_sim_resp - Response from external SIM processing
 	 *
 	 * This field should not be set in configuration step. It is only used
@@ -816,6 +702,8 @@
 		EXT_CERT_CHECK_GOOD,
 		EXT_CERT_CHECK_BAD,
 	} pending_ext_cert_check;
+
+	int teap_anon_dh;
 };
 
 
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index 0de7d6c..534af26 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -414,7 +414,7 @@
 	 */
 	if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
-		os_memset(key, 0, sizeof(key));
+		forced_memzero(key, sizeof(key));
 		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
@@ -422,7 +422,7 @@
 	if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0)
 	{
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
-		os_memset(key, 0, sizeof(key));
+		forced_memzero(key, sizeof(key));
 		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
@@ -431,7 +431,7 @@
 				 data->serverid, data->serverid_len,
 				 data->peerid, data->peerid_len) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
-		os_memset(key, 0, sizeof(key));
+		forced_memzero(key, sizeof(key));
 		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
@@ -442,7 +442,7 @@
 				 data->sess.dhcomp_len + data->sess.pnonce_len,
 				 EAP_EKE_COMMIT);
 	if (resp == NULL) {
-		os_memset(key, 0, sizeof(key));
+		forced_memzero(key, sizeof(key));
 		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
@@ -452,11 +452,11 @@
 	if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
 		wpabuf_free(resp);
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
-		os_memset(key, 0, sizeof(key));
+		forced_memzero(key, sizeof(key));
 		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
-	os_memset(key, 0, sizeof(key));
+	forced_memzero(key, sizeof(key));
 
 	wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
 		    rpos, data->sess.dhcomp_len);
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 94ce57d..0ed4a2b 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -162,7 +162,7 @@
 
 	if (eap_peer_select_phase2_methods(config, "auth=",
 					   &data->phase2_types,
-					   &data->num_phase2_types) < 0) {
+					   &data->num_phase2_types, 0) < 0) {
 		eap_fast_deinit(sm, data);
 		return NULL;
 	}
@@ -364,22 +364,24 @@
 }
 
 
-static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type)
+static int eap_fast_select_phase2_method(struct eap_fast_data *data,
+					 int vendor, enum eap_type type)
 {
 	size_t i;
 
 	/* TODO: TNC with anonymous provisioning; need to require both
 	 * completed MSCHAPv2 and TNC */
 
-	if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) {
-		wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed "
-			   "during unauthenticated provisioning; reject phase2"
-			   " type %d", type);
+	if (data->anon_provisioning &&
+	    (vendor != EAP_VENDOR_IETF || type != EAP_TYPE_MSCHAPV2)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-FAST: Only EAP-MSCHAPv2 is allowed during unauthenticated provisioning; reject phase2 type %u:%u",
+			   vendor, type);
 		return -1;
 	}
 
 #ifdef EAP_TNC
-	if (type == EAP_TYPE_TNC) {
+	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_TNC) {
 		data->phase2_type.vendor = EAP_VENDOR_IETF;
 		data->phase2_type.method = EAP_TYPE_TNC;
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
@@ -391,7 +393,7 @@
 #endif /* EAP_TNC */
 
 	for (i = 0; i < data->num_phase2_types; i++) {
-		if (data->phase2_types[i].vendor != EAP_VENDOR_IETF ||
+		if (data->phase2_types[i].vendor != vendor ||
 		    data->phase2_types[i].method != type)
 			continue;
 
@@ -404,7 +406,9 @@
 		break;
 	}
 
-	if (type != data->phase2_type.method || type == EAP_TYPE_NONE)
+	if (vendor != data->phase2_type.vendor ||
+	    type != data->phase2_type.method ||
+	    (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_NONE))
 		return -1;
 
 	return 0;
@@ -422,6 +426,8 @@
 	struct eap_method_ret iret;
 	struct eap_peer_config *config = eap_get_config(sm);
 	struct wpabuf msg;
+	int vendor = EAP_VENDOR_IETF;
+	enum eap_type method;
 
 	if (len <= sizeof(struct eap_hdr)) {
 		wpa_printf(MSG_INFO, "EAP-FAST: too short "
@@ -429,14 +435,27 @@
 		return -1;
 	}
 	pos = (u8 *) (hdr + 1);
-	wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
-	if (*pos == EAP_TYPE_IDENTITY) {
+	method = *pos;
+	if (method == EAP_TYPE_EXPANDED) {
+		if (len < sizeof(struct eap_hdr) + 8) {
+			wpa_printf(MSG_INFO,
+				   "EAP-FAST: Too short Phase 2 request (expanded header) (len=%lu)",
+				   (unsigned long) len);
+			return -1;
+		}
+		vendor = WPA_GET_BE24(pos + 1);
+		method = WPA_GET_BE32(pos + 4);
+	}
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%u:%u",
+		   vendor, method);
+	if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_IDENTITY) {
 		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
 		return 0;
 	}
 
 	if (data->phase2_priv && data->phase2_method &&
-	    *pos != data->phase2_type.method) {
+	    (vendor != data->phase2_type.vendor ||
+	     method != data->phase2_type.method)) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - "
 			   "deinitialize previous method");
 		data->phase2_method->deinit(sm, data->phase2_priv);
@@ -448,7 +467,7 @@
 
 	if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
 	    data->phase2_type.method == EAP_TYPE_NONE &&
-	    eap_fast_select_phase2_method(data, *pos) < 0) {
+	    eap_fast_select_phase2_method(data, vendor, method) < 0) {
 		if (eap_peer_tls_phase2_nak(data->phase2_types,
 					    data->num_phase2_types,
 					    hdr, resp))
@@ -459,8 +478,9 @@
 	if ((data->phase2_priv == NULL &&
 	     eap_fast_init_phase2_method(sm, data) < 0) ||
 	    data->phase2_method == NULL) {
-		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
-			   "Phase 2 EAP method %d", *pos);
+		wpa_printf(MSG_INFO,
+			   "EAP-FAST: Failed to initialize Phase 2 EAP method %u:%u",
+			   vendor, method);
 		ret->methodState = METHOD_DONE;
 		ret->decision = DECISION_FAIL;
 		return -1;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 096f0f2..8f29d4a 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -72,7 +72,7 @@
 	/**
 	 * method - EAP type number (EAP_TYPE_*)
 	 */
-	EapType method;
+	enum eap_type method;
 
 	/**
 	 * name - Name of the method (e.g., "TLS")
@@ -312,7 +312,7 @@
 		EAP_FAILURE
 	} EAP_state;
 	/* Long-term local variables */
-	EapType selectedMethod;
+	enum eap_type selectedMethod;
 	EapMethodState methodState;
 	int lastId;
 	struct wpabuf *lastRespData;
@@ -322,7 +322,7 @@
 	Boolean rxSuccess;
 	Boolean rxFailure;
 	int reqId;
-	EapType reqMethod;
+	enum eap_type reqMethod;
 	int reqVendor;
 	u32 reqVendorMethod;
 	Boolean ignore;
@@ -366,6 +366,7 @@
 	u8 *peer_challenge, *auth_challenge;
 
 	int num_rounds;
+	int num_rounds_short;
 	int force_disabled;
 
 	struct wps_context *wps;
@@ -381,6 +382,7 @@
 	unsigned int expected_failure:1;
 	unsigned int ext_cert_check:1;
 	unsigned int waiting_ext_cert_check:1;
+	unsigned int use_machine_cred:1;
 
 	struct dl_list erp_keys; /* struct eap_erp_key */
 };
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
index 233b9ee..34758e0 100644
--- a/src/eap_peer/eap_leap.c
+++ b/src/eap_peer/eap_leap.c
@@ -390,8 +390,8 @@
 	wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
 	*len = LEAP_KEY_LEN;
 
-	os_memset(pw_hash, 0, sizeof(pw_hash));
-	os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash));
+	forced_memzero(pw_hash, sizeof(pw_hash));
+	forced_memzero(pw_hash_hash, sizeof(pw_hash_hash));
 
 	return key;
 }
diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
index 9747954..f2d2947 100644
--- a/src/eap_peer/eap_methods.c
+++ b/src/eap_peer/eap_methods.c
@@ -27,7 +27,8 @@
  * @method: EAP type number
  * Returns: Pointer to EAP method or %NULL if not found
  */
-const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
+const struct eap_method * eap_peer_get_eap_method(int vendor,
+						  enum eap_type method)
 {
 	struct eap_method *m;
 	for (m = eap_methods; m; m = m->next) {
@@ -47,7 +48,7 @@
  * This function maps EAP type names into EAP type numbers based on the list of
  * EAP methods included in the build.
  */
-EapType eap_peer_get_type(const char *name, int *vendor)
+enum eap_type eap_peer_get_type(const char *name, int *vendor)
 {
 	struct eap_method *m;
 	for (m = eap_methods; m; m = m->next) {
@@ -70,7 +71,7 @@
  * This function maps EAP type numbers into EAP type names based on the list of
  * EAP methods included in the build.
  */
-const char * eap_get_name(int vendor, EapType type)
+const char * eap_get_name(int vendor, enum eap_type type)
 {
 	struct eap_method *m;
 	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
@@ -169,7 +170,7 @@
 
 	for (m = eap_methods; m; m = m->next)
 		c++;
-	
+
 	*count = c;
 	return eap_methods;
 }
@@ -279,7 +280,8 @@
  * is not needed anymore.
  */
 struct eap_method * eap_peer_method_alloc(int version, int vendor,
-					  EapType method, const char *name)
+					  enum eap_type method,
+					  const char *name)
 {
 	struct eap_method *eap;
 	eap = os_zalloc(sizeof(*eap));
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
index b96b211..e94f3d7 100644
--- a/src/eap_peer/eap_methods.h
+++ b/src/eap_peer/eap_methods.h
@@ -11,31 +11,33 @@
 
 #include "eap_common/eap_defs.h"
 
-const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_eap_method(int vendor,
+						  enum eap_type method);
 const struct eap_method * eap_peer_get_methods(size_t *count);
 
 struct eap_method * eap_peer_method_alloc(int version, int vendor,
-					  EapType method, const char *name);
+					  enum eap_type method,
+					  const char *name);
 int eap_peer_method_register(struct eap_method *method);
 
 
 #ifdef IEEE8021X_EAPOL
 
-EapType eap_peer_get_type(const char *name, int *vendor);
-const char * eap_get_name(int vendor, EapType type);
+enum eap_type eap_peer_get_type(const char *name, int *vendor);
+const char * eap_get_name(int vendor, enum eap_type type);
 size_t eap_get_names(char *buf, size_t buflen);
 char ** eap_get_names_as_string_array(size_t *num);
 void eap_peer_unregister_methods(void);
 
 #else /* IEEE8021X_EAPOL */
 
-static inline EapType eap_peer_get_type(const char *name, int *vendor)
+static inline enum eap_type eap_peer_get_type(const char *name, int *vendor)
 {
 	*vendor = EAP_VENDOR_IETF;
 	return EAP_TYPE_NONE;
 }
 
-static inline const char * eap_get_name(int vendor, EapType type)
+static inline const char * eap_get_name(int vendor, enum eap_type type)
 {
 	return NULL;
 }
@@ -97,6 +99,7 @@
 int eap_peer_aka_register(void);
 int eap_peer_aka_prime_register(void);
 int eap_peer_fast_register(void);
+int eap_peer_teap_register(void);
 int eap_peer_pax_register(void);
 int eap_peer_sake_register(void);
 int eap_peer_gpsk_register(void);
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 8dcf7cc..92b15ec 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -148,7 +148,7 @@
 
 	if (eap_peer_select_phase2_methods(config, "auth=",
 					   &data->phase2_types,
-					   &data->num_phase2_types) < 0) {
+					   &data->num_phase2_types, 0) < 0) {
 		eap_peap_deinit(sm, data);
 		return NULL;
 	}
@@ -295,7 +295,7 @@
 	res = peap_prfplus(data->peap_version, tk, 40,
 			   "Inner Methods Compound Keys",
 			   isk, sizeof(isk), imck, sizeof(imck));
-	os_memset(isk, 0, sizeof(isk));
+	forced_memzero(isk, sizeof(isk));
 	if (res < 0)
 		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
@@ -305,7 +305,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
 	os_memcpy(data->cmk, imck + 40, 20);
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
-	os_memset(imck, 0, sizeof(imck));
+	forced_memzero(imck, sizeof(imck));
 
 	return 0;
 }
@@ -603,6 +603,8 @@
 	u8 *pos;
 	struct eap_method_ret iret;
 	struct eap_peer_config *config = eap_get_config(sm);
+	int vendor;
+	enum eap_type method;
 
 	if (len <= sizeof(struct eap_hdr)) {
 		wpa_printf(MSG_INFO, "EAP-PEAP: too short "
@@ -666,13 +668,26 @@
 #endif /* EAP_TNC */
 		/* fall through */
 	default:
+		vendor = EAP_VENDOR_IETF;
+		method = *pos;
+
+		if (method == EAP_TYPE_EXPANDED) {
+			if (len < sizeof(struct eap_hdr) + 8) {
+				wpa_printf(MSG_INFO,
+					   "EAP-PEAP: Too short Phase 2 request (expanded header) (len=%lu)",
+					   (unsigned long) len);
+				return -1;
+			}
+			vendor = WPA_GET_BE24(pos + 1);
+			method = WPA_GET_BE32(pos + 4);
+		}
+
 		if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
 		    data->phase2_type.method == EAP_TYPE_NONE) {
 			size_t i;
 			for (i = 0; i < data->num_phase2_types; i++) {
-				if (data->phase2_types[i].vendor !=
-				    EAP_VENDOR_IETF ||
-				    data->phase2_types[i].method != *pos)
+				if (data->phase2_types[i].vendor != vendor ||
+				    data->phase2_types[i].method != method)
 					continue;
 
 				data->phase2_type.vendor =
@@ -686,8 +701,9 @@
 				break;
 			}
 		}
-		if (*pos != data->phase2_type.method ||
-		    *pos == EAP_TYPE_NONE) {
+		if (vendor != data->phase2_type.vendor ||
+		    method != data->phase2_type.method ||
+		    (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE)) {
 			if (eap_peer_tls_phase2_nak(data->phase2_types,
 						    data->num_phase2_types,
 						    hdr, resp))
@@ -1267,7 +1283,7 @@
 		os_memcpy(key, csk, EAP_TLS_KEY_LEN);
 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
 			    key, EAP_TLS_KEY_LEN);
-		os_memset(csk, 0, sizeof(csk));
+		forced_memzero(csk, sizeof(csk));
 	} else
 		os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 76fcad4..54f102a 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -30,6 +30,7 @@
 	u8 *password;
 	size_t password_len;
 	int password_hash;
+	struct wpa_freq_range_list allowed_groups;
 	u16 group_num;
 	u8 prep;
 	u8 token[4];
@@ -54,6 +55,9 @@
 };
 
 
+static void eap_pwd_deinit(struct eap_sm *sm, void *priv);
+
+
 #ifndef CONFIG_NO_STDOUT_DEBUG
 static const char * eap_pwd_state_txt(int state)
 {
@@ -92,6 +96,7 @@
 	size_t identity_len, password_len;
 	int fragment_size;
 	int pwhash;
+	const char *phase1;
 
 	password = eap_get_config_password2(sm, &password_len, &pwhash);
 	if (password == NULL) {
@@ -129,6 +134,30 @@
 	data->password_len = password_len;
 	data->password_hash = pwhash;
 
+	phase1 = eap_get_config_phase1(sm);
+	if (phase1) {
+		const char *pos, *end;
+		char *copy = NULL;
+		int res;
+
+		pos = os_strstr(phase1, "eap_pwd_groups=");
+		if (pos) {
+			pos += 15;
+			end = os_strchr(pos, ' ');
+			if (end) {
+				copy = os_zalloc(end - pos + 1);
+				if (!copy)
+					goto fail;
+				os_memcpy(copy, pos, end - pos);
+				pos = copy;
+			}
+			res = freq_range_list_parse(&data->allowed_groups, pos);
+			os_free(copy);
+			if (res)
+				goto fail;
+		}
+	}
+
 	data->out_frag_pos = data->in_frag_pos = 0;
 	data->inbuf = data->outbuf = NULL;
 	fragment_size = eap_get_config_fragment_size(sm);
@@ -140,6 +169,9 @@
 	data->state = PWD_ID_Req;
 
 	return data;
+fail:
+	eap_pwd_deinit(sm, data);
+	return NULL;
 }
 
 
@@ -163,6 +195,7 @@
 	}
 	wpabuf_free(data->inbuf);
 	wpabuf_free(data->outbuf);
+	os_free(data->allowed_groups.range);
 	bin_clear_free(data, sizeof(*data));
 }
 
@@ -203,6 +236,18 @@
 }
 
 
+static int eap_pwd_allowed_group(struct eap_pwd_data *data, u16 group)
+{
+	if (!data->allowed_groups.range) {
+		/* By default, allow the groups using NIST curves P-256, P-384,
+		 * and P-521. */
+		return group == 19 || group == 20 || group == 21;
+	}
+
+	return freq_range_list_includes(&data->allowed_groups, group);
+}
+
+
 static void
 eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 			    struct eap_method_ret *ret,
@@ -228,9 +273,11 @@
 	wpa_printf(MSG_DEBUG,
 		   "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u",
 		   data->group_num, id->random_function, id->prf, id->prep);
-	if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
-	    (id->prf != EAP_PWD_DEFAULT_PRF)) {
-		ret->ignore = TRUE;
+	if (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC ||
+	    id->prf != EAP_PWD_DEFAULT_PRF ||
+	    !eap_pwd_allowed_group(data, data->group_num)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unsupported or disabled proposal");
 		eap_pwd_state(data, FAILURE);
 		return;
 	}
@@ -362,7 +409,7 @@
 					       data->password_len, pwhash);
 			if (res == 0)
 				res = hash_nt_password_hash(pwhash, pwhashhash);
-			os_memset(pwhash, 0, sizeof(pwhash));
+			forced_memzero(pwhash, sizeof(pwhash));
 		}
 
 		if (res) {
@@ -514,8 +561,8 @@
 				       data->id_server, data->id_server_len,
 				       data->id_peer, data->id_peer_len,
 				       data->token);
-	os_memset(pwhashhash, 0, sizeof(pwhashhash));
-	os_memset(salthashpwd, 0, sizeof(salthashpwd));
+	forced_memzero(pwhashhash, sizeof(pwhashhash));
+	forced_memzero(salthashpwd, sizeof(salthashpwd));
 	if (res) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
 		eap_pwd_state(data, FAILURE);
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 0a6ce25..255241f 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-SAKE (RFC 4763)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -235,9 +235,13 @@
 		data->serverid_len = attr.serverid_len;
 	}
 
-	eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
-			     data->rand_s, data->rand_p,
-			     (u8 *) &data->tek, data->msk, data->emsk);
+	if (eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
+				 data->rand_s, data->rand_p,
+				 (u8 *) &data->tek, data->msk,
+				 data->emsk) < 0) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
+		return NULL;
+	}
 
 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
 
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index b5811a8..dd9848e 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -32,6 +32,7 @@
 	u8 msk[EAP_SIM_KEYING_DATA_LEN];
 	u8 emsk[EAP_EMSK_LEN];
 	u8 rand[3][GSM_RAND_LEN];
+	u8 reauth_mac[EAP_SIM_MAC_LEN];
 
 	int num_id_req, num_notification;
 	u8 *pseudonym;
@@ -43,7 +44,7 @@
 	u8 *last_eap_identity;
 	size_t last_eap_identity_len;
 	enum {
-		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
+		CONTINUE, START_DONE, RESULT_SUCCESS, SUCCESS, FAILURE
 	} state;
 	int result_ind, use_result_ind;
 	int use_pseudonym;
@@ -57,6 +58,8 @@
 	switch (state) {
 	case CONTINUE:
 		return "CONTINUE";
+	case START_DONE:
+		return "START_DONE";
 	case RESULT_SUCCESS:
 		return "RESULT_SUCCESS";
 	case SUCCESS:
@@ -485,6 +488,7 @@
 	const u8 *identity = NULL;
 	size_t identity_len = 0;
 	struct eap_sim_msg *msg;
+	struct wpabuf *resp;
 
 	data->reauth = 0;
 	if (id_req == ANY_ID && data->reauth_id) {
@@ -534,7 +538,10 @@
 				identity, identity_len);
 	}
 
-	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
+	resp = eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
+	if (resp)
+		eap_sim_state(data, START_DONE);
+	return resp;
 }
 
 
@@ -720,6 +727,13 @@
 	int res;
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
+	if (data->state != START_DONE) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-SIM: Unexpected Challenge in state %s",
+			   eap_sim_state_txt(data->state));
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
 	data->reauth = 0;
 	if (!attr->mac || !attr->rand) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
@@ -803,8 +817,13 @@
 			       EAP_SIM_NONCE_MT_LEN)) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
 			   "used invalid AT_MAC");
+#ifdef TEST_FUZZ
+		wpa_printf(MSG_INFO,
+			   "TEST: Ignore AT_MAC mismatch for fuzz testing");
+#else /* TEST_FUZZ */
 		return eap_sim_client_error(data, id,
 					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+#endif /* TEST_FUZZ */
 	}
 
 	/* Old reauthentication identity must not be used anymore. In
@@ -963,10 +982,30 @@
 	{
 		wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
 			   "did not have valid AT_MAC");
+#ifdef TEST_FUZZ
+		wpa_printf(MSG_INFO,
+			   "TEST: Ignore AT_MAC mismatch for fuzz testing");
+#else /* TEST_FUZZ */
 		return eap_sim_client_error(data, id,
 					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+#endif /* TEST_FUZZ */
 	}
 
+	/* At this stage the received MAC has been verified. Use this MAC for
+	 * reauth Session-Id calculation if all other checks pass.
+	 * The peer does not use the local MAC but the received MAC in deriving
+	 * Session-Id. */
+#ifdef TEST_FUZZ
+	if (attr->mac)
+		os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN);
+	else
+		os_memset(data->reauth_mac, 0x12, EAP_SIM_MAC_LEN);
+#else /* TEST_FUZZ */
+	os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN);
+#endif /* TEST_FUZZ */
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: Server MAC",
+		    data->reauth_mac, EAP_SIM_MAC_LEN);
+
 	if (attr->encr_data == NULL || attr->iv == NULL) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
 			   "message did not include encrypted data");
@@ -1225,15 +1264,24 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	if (!data->reauth)
+		*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	else
+		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
 	id = os_malloc(*len);
 	if (id == NULL)
 		return NULL;
 
 	id[0] = EAP_TYPE_SIM;
-	os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
-	os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
-		  EAP_SIM_NONCE_MT_LEN);
+	if (!data->reauth) {
+		os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+		os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN,
+			  data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+	} else {
+		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
+		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
+			  EAP_SIM_MAC_LEN);
+	}
 	wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
 
 	return id;
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
new file mode 100644
index 0000000..f751fbe
--- /dev/null
+++ b/src/eap_peer/eap_teap.c
@@ -0,0 +1,2137 @@
+/*
+ * EAP peer method: EAP-TEAP (RFC 7170)
+ * 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 "crypto/tls.h"
+#include "eap_common/eap_teap_common.h"
+#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);
+
+
+struct eap_teap_data {
+	struct eap_ssl_data ssl;
+
+	u8 teap_version; /* Negotiated version */
+	u8 received_version; /* Version number received during negotiation */
+	u16 tls_cs;
+
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int phase2_success;
+	int inner_method_done;
+	int iresult_verified;
+	int result_success_done;
+	int on_tx_completion;
+
+	struct eap_method_type phase2_type;
+	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];
+	u8 *session_id;
+	size_t id_len;
+	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_msk[EAP_TEAP_SIMCK_LEN];
+	u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
+	int simck_idx;
+	int cmk_emsk_available;
+
+	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
+	struct wpabuf *server_outer_tlvs;
+	struct wpabuf *peer_outer_tlvs;
+};
+
+
+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 */
+}
+
+
+static void * eap_teap_init(struct eap_sm *sm)
+{
+	struct eap_teap_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	if (!config)
+		return NULL;
+
+	data = os_zalloc(sizeof(*data));
+	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) {
+		eap_teap_deinit(sm, data);
+		return NULL;
+	}
+
+	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;
+}
+
+
+static void eap_teap_clear(struct eap_teap_data *data)
+{
+	forced_memzero(data->key_data, EAP_TEAP_KEY_LEN);
+	forced_memzero(data->emsk, EAP_EMSK_LEN);
+	os_free(data->session_id);
+	data->session_id = NULL;
+	wpabuf_free(data->pending_phase2_req);
+	data->pending_phase2_req = NULL;
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
+	wpabuf_free(data->server_outer_tlvs);
+	data->server_outer_tlvs = NULL;
+	wpabuf_free(data->peer_outer_tlvs);
+	data->peer_outer_tlvs = NULL;
+	forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+}
+
+
+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;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->deinit(sm, data->phase2_priv);
+	eap_teap_clear(data);
+	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,
+				    data->key_data) < 0 ||
+	    eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk,
+				     data->emsk) < 0)
+		return -1;
+	data->success = 1;
+	return 0;
+}
+
+
+static int eap_teap_derive_key_auth(struct eap_sm *sm,
+				    struct eap_teap_data *data)
+{
+	int res;
+
+	/* 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);
+	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_idx = 0;
+	return 0;
+}
+
+
+static int eap_teap_init_phase2_method(struct eap_sm *sm,
+				       struct eap_teap_data *data)
+{
+	data->inner_method_done = 0;
+	data->iresult_verified = 0;
+	data->phase2_method =
+		eap_peer_get_eap_method(data->phase2_type.vendor,
+					data->phase2_type.method);
+	if (!data->phase2_method)
+		return -1;
+
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+
+	return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static int eap_teap_select_phase2_method(struct eap_teap_data *data,
+					 int vendor, enum eap_type type)
+{
+	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;
+		data->phase2_type.method = EAP_TYPE_TNC;
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Selected Phase 2 EAP vendor %d method %d for TNC",
+			   data->phase2_type.vendor,
+			   data->phase2_type.method);
+		return 0;
+	}
+#endif /* EAP_TNC */
+
+	for (i = 0; i < data->num_phase2_types; i++) {
+		if (data->phase2_types[i].vendor != vendor ||
+		    data->phase2_types[i].method != type)
+			continue;
+
+		data->phase2_type.vendor = data->phase2_types[i].vendor;
+		data->phase2_type.method = data->phase2_types[i].method;
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Selected Phase 2 EAP vendor %d method %d",
+			   data->phase2_type.vendor,
+			   data->phase2_type.method);
+		break;
+	}
+
+	if (vendor != data->phase2_type.vendor ||
+	    type != data->phase2_type.method ||
+	    (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_NONE))
+		return -1;
+
+	return 0;
+}
+
+
+static void eap_teap_deinit_inner_eap(struct eap_sm *sm,
+				      struct eap_teap_data *data)
+{
+	if (!data->phase2_priv || !data->phase2_method)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Phase 2 EAP sequence - deinitialize previous method");
+	data->phase2_method->deinit(sm, data->phase2_priv);
+	data->phase2_method = NULL;
+	data->phase2_priv = NULL;
+	data->phase2_type.vendor = EAP_VENDOR_IETF;
+	data->phase2_type.method = EAP_TYPE_NONE;
+}
+
+
+static int eap_teap_phase2_request(struct eap_sm *sm,
+				   struct eap_teap_data *data,
+				   struct eap_method_ret *ret,
+				   struct eap_hdr *hdr,
+				   struct wpabuf **resp)
+{
+	size_t len = be_to_host16(hdr->length);
+	u8 *pos;
+	struct eap_method_ret iret;
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct wpabuf msg;
+	int vendor = EAP_VENDOR_IETF;
+	enum eap_type method;
+
+	if (len <= sizeof(struct eap_hdr)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: too short Phase 2 request (len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+	pos = (u8 *) (hdr + 1);
+	method = *pos;
+	if (method == EAP_TYPE_EXPANDED) {
+		if (len < sizeof(struct eap_hdr) + 8) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short Phase 2 request (expanded header) (len=%lu)",
+				   (unsigned long) len);
+			return -1;
+		}
+		vendor = WPA_GET_BE24(pos + 1);
+		method = WPA_GET_BE32(pos + 4);
+	}
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 Request: type=%u:%u",
+		   vendor, method);
+	if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_IDENTITY) {
+		eap_teap_deinit_inner_eap(sm, data);
+		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+		return 0;
+	}
+
+	if (data->phase2_priv && data->phase2_method &&
+	    (vendor != data->phase2_type.vendor ||
+	     method != data->phase2_type.method))
+		eap_teap_deinit_inner_eap(sm, data);
+
+	if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+	    data->phase2_type.method == EAP_TYPE_NONE &&
+	    eap_teap_select_phase2_method(data, vendor, method) < 0) {
+		if (eap_peer_tls_phase2_nak(data->phase2_types,
+					    data->num_phase2_types,
+					    hdr, resp))
+			return -1;
+		return 0;
+	}
+
+	if ((!data->phase2_priv && eap_teap_init_phase2_method(sm, data) < 0) ||
+	    !data->phase2_method) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Failed to initialize Phase 2 EAP method %u:%u",
+			   vendor, method);
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return -1;
+	}
+
+	os_memset(&iret, 0, sizeof(iret));
+	wpabuf_set(&msg, hdr, len);
+	*resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+					     &msg);
+	if (iret.methodState == METHOD_DONE)
+		data->inner_method_done = 1;
+	if (!(*resp) ||
+	    (iret.methodState == METHOD_DONE &&
+	     iret.decision == DECISION_FAIL)) {
+		/* Wait for protected indication of failure */
+		ret->methodState = METHOD_MAY_CONT;
+		ret->decision = DECISION_FAIL;
+	} else if ((iret.methodState == METHOD_DONE ||
+		    iret.methodState == METHOD_MAY_CONT) &&
+		   (iret.decision == DECISION_UNCOND_SUCC ||
+		    iret.decision == DECISION_COND_SUCC)) {
+		data->phase2_success = 1;
+	}
+
+	if (!(*resp) && config &&
+	    (config->pending_req_identity || config->pending_req_password ||
+	     config->pending_req_otp || config->pending_req_new_password ||
+	     config->pending_req_sim)) {
+		wpabuf_free(data->pending_phase2_req);
+		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+	} else if (!(*resp))
+		return -1;
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_teap_tlv_nak(int vendor_id, int tlv_type)
+{
+	struct wpabuf *buf;
+	struct teap_tlv_nak *nak;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Add NAK TLV (Vendor-Id %u NAK-Type %u)",
+		   vendor_id, tlv_type);
+	buf = wpabuf_alloc(sizeof(*nak));
+	if (!buf)
+		return NULL;
+	nak = wpabuf_put(buf, sizeof(*nak));
+	nak->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_NAK);
+	nak->length = host_to_be16(6);
+	nak->vendor_id = host_to_be32(vendor_id);
+	nak->nak_type = host_to_be16(tlv_type);
+	return buf;
+}
+
+
+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)
+{
+	struct wpabuf *tlv;
+
+	tlv = eap_teap_tlv_identity_type(sm->use_machine_cred ?
+					 TEAP_IDENTITY_TYPE_MACHINE :
+					 TEAP_IDENTITY_TYPE_USER);
+	return wpabuf_concat(msg, tlv);
+}
+
+
+static struct wpabuf * eap_teap_process_eap_payload_tlv(
+	struct eap_sm *sm, struct eap_teap_data *data,
+	struct eap_method_ret *ret,
+	u8 *eap_payload_tlv, size_t eap_payload_tlv_len,
+	enum teap_identity_types req_id_type)
+{
+	struct eap_hdr *hdr;
+	struct wpabuf *resp = NULL;
+
+	if (eap_payload_tlv_len < sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: too short EAP Payload TLV (len=%lu)",
+			   (unsigned long) eap_payload_tlv_len);
+		return NULL;
+	}
+
+	hdr = (struct eap_hdr *) eap_payload_tlv;
+	if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: EAP packet overflow in EAP Payload TLV");
+		return NULL;
+	}
+
+	if (hdr->code != EAP_CODE_REQUEST) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header",
+			   hdr->code);
+		return NULL;
+	}
+
+	if (eap_teap_phase2_request(sm, data, ret, hdr, &resp)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Phase 2 Request processing failed");
+		return NULL;
+	}
+
+	resp = eap_teap_tlv_eap_payload(resp);
+	if (req_id_type)
+		resp = eap_teap_add_identity_type(sm, resp);
+
+	return resp;
+}
+
+
+static struct wpabuf * eap_teap_process_basic_auth_req(
+	struct eap_sm *sm, struct eap_teap_data *data,
+	u8 *basic_auth_req, size_t basic_auth_req_len,
+	enum teap_identity_types req_id_type)
+{
+	const u8 *identity, *password;
+	size_t identity_len, password_len, plen;
+	struct wpabuf *resp;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Req prompt",
+			  basic_auth_req, basic_auth_req_len);
+	/* TODO: send over control interface */
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password(sm, &password_len);
+	if (!identity || !password ||
+	    identity_len > 255 || password_len > 255) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No username/password suitable for Basic-Password-Auth");
+		return eap_teap_tlv_nak(0, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ);
+	}
+
+	plen = 1 + identity_len + 1 + password_len;
+	resp = wpabuf_alloc(sizeof(struct teap_tlv_hdr) + plen);
+	if (!resp)
+		return NULL;
+	eap_teap_put_tlv_hdr(resp, TEAP_TLV_BASIC_PASSWORD_AUTH_RESP, plen);
+	wpabuf_put_u8(resp, identity_len);
+	wpabuf_put_data(resp, identity, identity_len);
+	wpabuf_put_u8(resp, password_len);
+	wpabuf_put_data(resp, password, password_len);
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Resp",
+			    resp);
+	if (req_id_type)
+		resp = eap_teap_add_identity_type(sm, resp);
+
+	/* Assume this succeeds so that Result TLV(Success) from the server can
+	 * be used to terminate TEAP. */
+	data->phase2_success = 1;
+
+	return resp;
+}
+
+
+static int
+eap_teap_validate_crypto_binding(struct eap_teap_data *data,
+				 const struct teap_tlv_crypto_binding *cb)
+{
+	u8 flags, subtype;
+
+	subtype = cb->subtype & 0x0f;
+	flags = cb->subtype >> 4;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+		   cb->version, cb->received_version, flags, subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+		    cb->nonce, sizeof(cb->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+		    cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+		    cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+	if (cb->version != EAP_TEAP_VERSION ||
+	    cb->received_version != data->received_version ||
+	    subtype != TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST ||
+	    flags < 1 || flags > 3) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Invalid Version/Flags/Sub-Type in Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+			   cb->version, cb->received_version, flags, subtype);
+		return -1;
+	}
+
+	if (cb->nonce[EAP_TEAP_NONCE_LEN - 1] & 0x01) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Invalid Crypto-Binding TLV Nonce in request");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_teap_write_crypto_binding(
+	struct eap_teap_data *data,
+	struct teap_tlv_crypto_binding *rbind,
+	const struct teap_tlv_crypto_binding *cb,
+	const u8 *cmk_msk, const u8 *cmk_emsk)
+{
+	u8 subtype, flags;
+
+	rbind->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+				       TEAP_TLV_CRYPTO_BINDING);
+	rbind->length = host_to_be16(sizeof(*rbind) -
+				     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;
+	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,
+				  data->peer_outer_tlvs, cmk_msk,
+				  rbind->msk_compound_mac) < 0)
+		return -1;
+	if (cmk_emsk &&
+	    eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
+				  data->peer_outer_tlvs, cmk_emsk,
+				  rbind->emsk_compound_mac) < 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u SubType %u",
+		   rbind->version, rbind->received_version, flags, subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+		    rbind->nonce, sizeof(rbind->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+		    rbind->emsk_compound_mac, sizeof(rbind->emsk_compound_mac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+		    rbind->msk_compound_mac, sizeof(rbind->msk_compound_mac));
+
+	return 0;
+}
+
+
+static int eap_teap_get_cmk(struct eap_sm *sm, struct eap_teap_data *data,
+			    u8 *cmk_msk, u8 *cmk_emsk)
+{
+	u8 *msk = NULL, *emsk = NULL;
+	size_t msk_len = 0, emsk_len = 0;
+	int res;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Determining CMK[%d] for Compound MAC calculation",
+		   data->simck_idx + 1);
+
+	if (!data->phase2_method)
+		return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
+							 data->simck_msk,
+							 cmk_msk);
+
+	if (!data->phase2_method || !data->phase2_priv) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
+		return -1;
+	}
+
+	if (data->phase2_method->isKeyAvailable &&
+	    !data->phase2_method->isKeyAvailable(sm, data->phase2_priv)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Phase 2 key material not available");
+		return -1;
+	}
+
+	if (data->phase2_method->isKeyAvailable &&
+	    data->phase2_method->getKey) {
+		msk = data->phase2_method->getKey(sm, data->phase2_priv,
+						  &msk_len);
+		if (!msk) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Could not fetch Phase 2 MSK");
+			return -1;
+		}
+	}
+
+	if (data->phase2_method->isKeyAvailable &&
+	    data->phase2_method->get_emsk) {
+		emsk = data->phase2_method->get_emsk(sm, data->phase2_priv,
+						     &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);
+	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;
+	}
+	return res;
+}
+
+
+static int eap_teap_session_id(struct eap_teap_data *data)
+{
+	const size_t max_id_len = 100;
+	int res;
+
+	os_free(data->session_id);
+	data->session_id = os_malloc(max_id_len);
+	if (!data->session_id)
+		return -1;
+
+	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) {
+		os_free(data->session_id);
+		data->session_id = NULL;
+		wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id");
+		return -1;
+	}
+
+	data->id_len = 1 + res;
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id",
+		    data->session_id, data->id_len);
+	return 0;
+}
+
+
+static struct wpabuf * eap_teap_process_crypto_binding(
+	struct eap_sm *sm, struct eap_teap_data *data,
+	struct eap_method_ret *ret,
+	const struct teap_tlv_crypto_binding *cb, size_t bind_len)
+{
+	struct wpabuf *resp;
+	u8 *pos;
+	u8 cmk_msk[EAP_TEAP_CMK_LEN];
+	u8 cmk_emsk[EAP_TEAP_CMK_LEN];
+	const u8 *cmk_emsk_ptr = NULL;
+	int res;
+	size_t len;
+	u8 flags;
+
+	if (eap_teap_validate_crypto_binding(data, cb) < 0 ||
+	    eap_teap_get_cmk(sm, data, cmk_msk, cmk_emsk) < 0)
+		return NULL;
+
+	/* Validate received MSK/EMSK Compound MAC */
+	flags = cb->subtype >> 4;
+
+	if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+	    flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+		u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+		if (eap_teap_compound_mac(data->tls_cs, cb,
+					  data->server_outer_tlvs,
+					  data->peer_outer_tlvs, cmk_msk,
+					  msk_compound_mac) < 0)
+			return NULL;
+		res = os_memcmp_const(msk_compound_mac, cb->msk_compound_mac,
+				      EAP_TEAP_COMPOUND_MAC_LEN);
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received MSK Compound MAC",
+			    cb->msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+		wpa_hexdump(MSG_MSGDUMP,
+			    "EAP-TEAP: Calculated MSK Compound MAC",
+			    msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+		if (res != 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: MSK Compound MAC did not match");
+			return NULL;
+		}
+	}
+
+	if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+	     flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
+	    data->cmk_emsk_available) {
+		u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+		if (eap_teap_compound_mac(data->tls_cs, cb,
+					  data->server_outer_tlvs,
+					  data->peer_outer_tlvs, cmk_emsk,
+					  emsk_compound_mac) < 0)
+			return NULL;
+		res = os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac,
+				      EAP_TEAP_COMPOUND_MAC_LEN);
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received EMSK Compound MAC",
+			    cb->emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+		wpa_hexdump(MSG_MSGDUMP,
+			    "EAP-TEAP: Calculated EMSK Compound MAC",
+			    emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+		if (res != 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: EMSK Compound MAC did not match");
+			return NULL;
+		}
+
+		cmk_emsk_ptr = cmk_emsk;
+	}
+
+	if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
+	    !data->cmk_emsk_available) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Server included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this");
+		return NULL;
+	}
+
+	/*
+	 * Compound MAC was valid, so authentication succeeded. Reply with
+	 * crypto binding to allow server to complete authentication.
+	 */
+
+	len = sizeof(struct teap_tlv_crypto_binding);
+	resp = wpabuf_alloc(len);
+	if (!resp)
+		return NULL;
+
+	if (data->phase2_success && eap_teap_derive_msk(data) < 0) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to generate MSK");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		data->phase2_success = 0;
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	if (data->phase2_success && eap_teap_session_id(data) < 0) {
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	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) {
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	return resp;
+}
+
+
+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)
+{
+	u16 tlv_type;
+	int mandatory, res;
+	size_t len;
+	u8 *pos, *end;
+
+	os_memset(tlv, 0, sizeof(*tlv));
+
+	/* Parse TLVs from the decrypted Phase 2 data */
+	pos = wpabuf_mhead(decrypted);
+	end = pos + wpabuf_len(decrypted);
+	while (end - pos >= 4) {
+		mandatory = pos[0] & 0x80;
+		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > (size_t) (end - pos)) {
+			wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s",
+			   tlv_type, eap_teap_tlv_type_str(tlv_type),
+			   (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
+
+		res = eap_teap_parse_tlv(tlv, tlv_type, pos, len);
+		if (res == -2)
+			break;
+		if (res < 0) {
+			if (mandatory) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: NAK unknown mandatory TLV type %u",
+					   tlv_type);
+				*resp = eap_teap_tlv_nak(0, tlv_type);
+				break;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Ignore unknown optional TLV type %u",
+				   tlv_type);
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+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,
+				      u8 identifier,
+				      struct wpabuf *decrypted,
+				      struct wpabuf **out_data)
+{
+	struct wpabuf *resp = NULL, *tmp;
+	struct eap_teap_tlv_parse tlv;
+	int failed = 0;
+	enum teap_error_codes error = 0;
+	int iresult_added = 0;
+
+	if (eap_teap_parse_decrypted(decrypted, &tlv, &resp) < 0) {
+		/* Parsing failed - no response available */
+		return 0;
+	}
+
+	if (resp) {
+		/* Parsing rejected the message - send out an error response */
+		goto send_resp;
+	}
+
+	if (tlv.result == TEAP_STATUS_FAILURE) {
+		/* Server indicated failure - respond similarly per
+		 * RFC 7170, 3.6.3. This authentication exchange cannot succeed
+		 * and will be terminated with a cleartext EAP Failure. */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Server rejected authentication");
+		resp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		goto send_resp;
+	}
+
+	if (tlv.iresult == TEAP_STATUS_SUCCESS && !tlv.crypto_binding) {
+		/* Intermediate-Result TLV indicating success, but no
+		 * Crypto-Binding TLV */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Intermediate-Result TLV indicating success, but no Crypto-Binding TLV");
+		failed = 1;
+		error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+		goto done;
+	}
+
+	if (!data->iresult_verified && !data->result_success_done &&
+	    tlv.result == TEAP_STATUS_SUCCESS && !tlv.crypto_binding) {
+		/* Result TLV indicating success, but no Crypto-Binding TLV */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Result TLV indicating success, but no Crypto-Binding TLV");
+		failed = 1;
+		error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+		goto done;
+	}
+
+	if (tlv.iresult != TEAP_STATUS_SUCCESS &&
+	    tlv.iresult != TEAP_STATUS_FAILURE &&
+	    data->inner_method_done) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Inner EAP method exchange completed, but no Intermediate-Result TLV included");
+		failed = 1;
+		error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+		goto done;
+	}
+
+	if (tlv.identity_type == TEAP_IDENTITY_TYPE_MACHINE) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
+		sm->use_machine_cred = config && config->machine_identity &&
+			config->machine_identity_len;
+	} else if (tlv.identity_type) {
+		sm->use_machine_cred = 0;
+	}
+	if (tlv.identity_type) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
+		os_free(data->phase2_types);
+		data->phase2_types = NULL;
+		data->num_phase2_types = 0;
+		if (config &&
+		    eap_peer_select_phase2_methods(config, "auth=",
+						   &data->phase2_types,
+						   &data->num_phase2_types,
+						   sm->use_machine_cred) < 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Failed to update Phase 2 EAP types");
+			failed = 1;
+			goto done;
+		}
+	}
+
+	if (tlv.basic_auth_req) {
+		tmp = eap_teap_process_basic_auth_req(sm, data,
+						      tlv.basic_auth_req,
+						      tlv.basic_auth_req_len,
+						      tlv.identity_type);
+		if (!tmp)
+			failed = 1;
+		resp = wpabuf_concat(resp, tmp);
+	} else if (tlv.eap_payload_tlv) {
+		tmp = eap_teap_process_eap_payload_tlv(sm, data, ret,
+						       tlv.eap_payload_tlv,
+						       tlv.eap_payload_tlv_len,
+						       tlv.identity_type);
+		if (!tmp)
+			failed = 1;
+		resp = wpabuf_concat(resp, tmp);
+
+		if (tlv.iresult == TEAP_STATUS_SUCCESS ||
+		    tlv.iresult == TEAP_STATUS_FAILURE) {
+			tmp = eap_teap_tlv_result(failed ?
+						  TEAP_STATUS_FAILURE :
+						  TEAP_STATUS_SUCCESS, 1);
+			resp = wpabuf_concat(resp, tmp);
+			if (tlv.iresult == TEAP_STATUS_FAILURE)
+				failed = 1;
+			iresult_added = 1;
+		}
+	}
+
+	if (tlv.crypto_binding) {
+		if (tlv.iresult != TEAP_STATUS_SUCCESS &&
+		    tlv.result != TEAP_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
+			failed = 1;
+			error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
+			goto done;
+		}
+
+		tmp = eap_teap_process_crypto_binding(sm, data, ret,
+						      tlv.crypto_binding,
+						      tlv.crypto_binding_len);
+		if (!tmp) {
+			failed = 1;
+			error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+		} else {
+			resp = wpabuf_concat(resp, tmp);
+			if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
+				data->result_success_done = 1;
+			if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
+				data->inner_method_done = 0;
+				data->iresult_verified = 1;
+			}
+		}
+	}
+
+	if (data->result_success_done && data->session_ticket_used &&
+	    eap_teap_derive_msk(data) == 0) {
+		/* Assume the server might accept authentication without going
+		 * 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;
+	}
+
+	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);
+		resp = wpabuf_concat(tmp, resp);
+
+		if (error != 0) {
+			tmp = eap_teap_tlv_error(error);
+			resp = wpabuf_concat(tmp, resp);
+		}
+
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+	} else if (tlv.result == TEAP_STATUS_SUCCESS) {
+		tmp = eap_teap_tlv_result(TEAP_STATUS_SUCCESS, 0);
+		resp = wpabuf_concat(tmp, resp);
+	}
+	if ((tlv.iresult == TEAP_STATUS_SUCCESS ||
+	     tlv.iresult == TEAP_STATUS_FAILURE) && !iresult_added) {
+		tmp = eap_teap_tlv_result((!failed && data->phase2_success) ?
+					  TEAP_STATUS_SUCCESS :
+					  TEAP_STATUS_FAILURE, 1);
+		resp = wpabuf_concat(tmp, resp);
+	}
+
+	if (resp && tlv.result == TEAP_STATUS_SUCCESS && !failed &&
+	    (tlv.crypto_binding || data->iresult_verified) &&
+	    data->phase2_success) {
+		/* Successfully completed Phase 2 */
+		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;
+		ret->decision = DECISION_UNCOND_SUCC;
+	}
+
+	if (!resp) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No recognized TLVs - send empty response packet");
+		resp = wpabuf_alloc(1);
+	}
+
+send_resp:
+	if (!resp)
+		return 0;
+
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 data", resp);
+	if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TEAP,
+				 data->teap_version, identifier,
+				 resp, out_data)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Failed to encrypt a Phase 2 frame");
+	}
+	wpabuf_free(resp);
+
+	return 0;
+}
+
+
+static int eap_teap_decrypt(struct eap_sm *sm, struct eap_teap_data *data,
+			    struct eap_method_ret *ret, u8 identifier,
+			    const struct wpabuf *in_data,
+			    struct wpabuf **out_data)
+{
+	struct wpabuf *in_decrypted;
+	int res;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Received %lu bytes encrypted data for Phase 2",
+		   (unsigned long) wpabuf_len(in_data));
+
+	if (data->pending_phase2_req) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Pending Phase 2 request - skip decryption and use old data");
+		/* Clear TLS reassembly state. */
+		eap_peer_tls_reset_input(&data->ssl);
+
+		in_decrypted = data->pending_phase2_req;
+		data->pending_phase2_req = NULL;
+		goto continue_req;
+	}
+
+	if (wpabuf_len(in_data) == 0) {
+		/* Received TLS ACK - requesting more fragments */
+		res = eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TEAP,
+					   data->teap_version,
+					   identifier, NULL, out_data);
+		if (res == 0 && !data->ssl.tls_out &&
+		    data->on_tx_completion) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Mark authentication completed at full TX of fragments");
+			ret->methodState = data->on_tx_completion;
+			data->on_tx_completion = 0;
+			ret->decision = DECISION_UNCOND_SUCC;
+		}
+		return res;
+	}
+
+	res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+	if (res)
+		return res;
+
+continue_req:
+	wpa_hexdump_buf(MSG_MSGDUMP, "EAP-TEAP: Decrypted Phase 2 TLV(s)",
+			in_decrypted);
+
+	if (wpabuf_len(in_decrypted) < 4) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Too short Phase 2 TLV frame (len=%lu)",
+			   (unsigned long) wpabuf_len(in_decrypted));
+		wpabuf_free(in_decrypted);
+		return -1;
+	}
+
+	res = eap_teap_process_decrypted(sm, data, ret, identifier,
+					 in_decrypted, out_data);
+
+	wpabuf_free(in_decrypted);
+
+	return res;
+}
+
+
+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 */
+
+	/* EAP-TEAP version negotiation (RFC 7170, Section 3.2) */
+	data->received_version = flags & EAP_TLS_VERSION_MASK;
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Start (server ver=%u, own ver=%u)",
+		   data->received_version, data->teap_version);
+	if (data->received_version < 1) {
+		/* Version 1 was the first defined version, so reject 0 */
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Server used unknown TEAP version %u",
+			   data->received_version);
+		return -1;
+	}
+	if (data->received_version < data->teap_version)
+		data->teap_version = data->received_version;
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Using TEAP version %d",
+		   data->teap_version);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message payload", pos, left);
+
+	/* Parse Authority-ID TLV from Outer TLVs, if present */
+	if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
+		const u8 *outer_pos, *outer_end;
+		u32 outer_tlv_len;
+
+		if (left < 4) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Not enough room for the Outer TLV Length field");
+			return -1;
+		}
+
+		outer_tlv_len = WPA_GET_BE32(pos);
+		pos += 4;
+		left -= 4;
+
+		if (outer_tlv_len > left) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Truncated Outer TLVs field (Outer TLV Length: %u; remaining buffer: %u)",
+				   outer_tlv_len, (unsigned int) left);
+			return -1;
+		}
+
+		outer_pos = pos + left - outer_tlv_len;
+		outer_end = outer_pos + outer_tlv_len;
+		wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message Outer TLVs",
+			    outer_pos, outer_tlv_len);
+		wpabuf_free(data->server_outer_tlvs);
+		data->server_outer_tlvs = wpabuf_alloc_copy(outer_pos,
+							    outer_tlv_len);
+		if (!data->server_outer_tlvs)
+			return -1;
+		left -= outer_tlv_len;
+		if (left > 0) {
+			wpa_hexdump(MSG_INFO,
+				    "EAP-TEAP: Unexpected TLS Data in Start message",
+				    pos, left);
+			return -1;
+		}
+
+		while (outer_pos < outer_end) {
+			u16 tlv_type, tlv_len;
+
+			if (outer_end - outer_pos < 4) {
+				wpa_printf(MSG_INFO,
+					   "EAP-TEAP: Truncated Outer TLV header");
+				return -1;
+			}
+			tlv_type = WPA_GET_BE16(outer_pos);
+			outer_pos += 2;
+			tlv_len = WPA_GET_BE16(outer_pos);
+			outer_pos += 2;
+			/* Outer TLVs are required to be optional, so no need to
+			 * check the M flag */
+			tlv_type &= TEAP_TLV_TYPE_MASK;
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Outer TLV: Type=%u Length=%u",
+				   tlv_type, tlv_len);
+			if (outer_end - outer_pos < tlv_len) {
+				wpa_printf(MSG_INFO,
+					   "EAP-TEAP: Truncated Outer TLV (Type %u)",
+					   tlv_type);
+				return -1;
+			}
+			if (tlv_type == TEAP_TLV_AUTHORITY_ID) {
+				wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Authority-ID",
+					    outer_pos, tlv_len);
+				if (a_id) {
+					wpa_printf(MSG_INFO,
+						   "EAP-TEAP: Multiple Authority-ID TLVs in TEAP/Start");
+					return -1;
+				}
+				a_id = outer_pos;
+				a_id_len = tlv_len;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: Ignore unknown Outer TLV (Type %u)",
+					   tlv_type);
+			}
+			outer_pos += tlv_len;
+		}
+	} else if (left > 0) {
+		wpa_hexdump(MSG_INFO,
+			    "EAP-TEAP: Unexpected TLS Data in Start message",
+			    pos, left);
+		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;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static struct wpabuf * eap_teap_add_dummy_outer_tlvs(struct eap_teap_data *data,
+						     struct wpabuf *resp)
+{
+	struct wpabuf *resp2;
+	u16 len;
+	const u8 *pos;
+	u8 flags;
+
+	wpabuf_free(data->peer_outer_tlvs);
+	data->peer_outer_tlvs = wpabuf_alloc(4 + 4);
+	if (!data->peer_outer_tlvs) {
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	/* Outer TLVs (dummy Vendor-Specific TLV for testing) */
+	wpabuf_put_be16(data->peer_outer_tlvs, TEAP_TLV_VENDOR_SPECIFIC);
+	wpabuf_put_be16(data->peer_outer_tlvs, 4);
+	wpabuf_put_be32(data->peer_outer_tlvs, EAP_VENDOR_HOSTAP);
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: TESTING - Add dummy Outer TLVs",
+			data->peer_outer_tlvs);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"EAP-TEAP: TEAP/Start response before modification",
+			resp);
+	resp2 = wpabuf_alloc(wpabuf_len(resp) + 4 +
+			     wpabuf_len(data->peer_outer_tlvs));
+	if (!resp2) {
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	pos = wpabuf_head(resp);
+	wpabuf_put_u8(resp2, *pos++); /* Code */
+	wpabuf_put_u8(resp2, *pos++); /* Identifier */
+	len = WPA_GET_BE16(pos);
+	pos += 2;
+	wpabuf_put_be16(resp2, len + 4 + wpabuf_len(data->peer_outer_tlvs));
+	wpabuf_put_u8(resp2, *pos++); /* Type */
+	/* Flags | Ver (with Outer TLV length included flag set to 1) */
+	flags = *pos++;
+	if (flags & (EAP_TEAP_FLAGS_OUTER_TLV_LEN |
+		     EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Cannot add Outer TLVs for testing");
+		wpabuf_free(resp);
+		wpabuf_free(resp2);
+		return NULL;
+	}
+	flags |= EAP_TEAP_FLAGS_OUTER_TLV_LEN;
+	wpabuf_put_u8(resp2, flags);
+	/* Outer TLV Length */
+	wpabuf_put_be32(resp2, wpabuf_len(data->peer_outer_tlvs));
+	/* TLS Data */
+	wpabuf_put_data(resp2, pos, wpabuf_len(resp) - 6);
+	wpabuf_put_buf(resp2, data->peer_outer_tlvs); /* Outer TLVs */
+
+	wpabuf_free(resp);
+	wpa_hexdump_buf(MSG_DEBUG,
+			"EAP-TEAP: TEAP/Start response after modification",
+			resp2);
+	return resp2;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static struct wpabuf * eap_teap_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	const struct eap_hdr *req;
+	size_t left;
+	int res;
+	u8 flags, id;
+	struct wpabuf *resp;
+	const u8 *pos;
+	struct eap_teap_data *data = priv;
+	struct wpabuf msg;
+
+	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TEAP, ret,
+					reqData, &left, &flags);
+	if (!pos)
+		return NULL;
+
+	req = wpabuf_head(reqData);
+	id = req->identifier;
+
+	if (flags & EAP_TLS_FLAGS_START) {
+		if (eap_teap_process_start(sm, data, flags, pos, left) < 0)
+			return NULL;
+
+		/* Outer TLVs are not used in further packet processing and
+		 * there cannot be TLS Data in this TEAP/Start message, so
+		 * enforce that by ignoring whatever data might remain in the
+		 * buffer. */
+		left = 0;
+	} else if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
+		/* TODO: RFC 7170, Section 4.3.1 indicates that the unexpected
+		 * Outer TLVs MUST be ignored instead of ignoring the full
+		 * message. */
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Outer TLVs present in non-Start message -> ignore message");
+		return NULL;
+	}
+
+	wpabuf_set(&msg, pos, left);
+
+	resp = NULL;
+	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+	    !data->resuming) {
+		/* Process tunneled (encrypted) phase 2 data. */
+		res = eap_teap_decrypt(sm, data, ret, id, &msg, &resp);
+		if (res < 0) {
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			/*
+			 * Ack possible Alert that may have caused failure in
+			 * decryption.
+			 */
+			res = 1;
+		}
+	} else {
+		if (sm->waiting_ext_cert_check && data->pending_resp) {
+			struct eap_peer_config *config = eap_get_config(sm);
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_GOOD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: External certificate check succeeded - continue handshake");
+				resp = data->pending_resp;
+				data->pending_resp = NULL;
+				sm->waiting_ext_cert_check = 0;
+				return resp;
+			}
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_BAD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: External certificate check failed - force authentication failure");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				sm->waiting_ext_cert_check = 0;
+				return NULL;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Continuing to wait external server certificate validation");
+			return NULL;
+		}
+
+		/* Continue processing TLS handshake (phase 1). */
+		res = eap_peer_tls_process_helper(sm, &data->ssl,
+						  EAP_TYPE_TEAP,
+						  data->teap_version, id, &msg,
+						  &resp);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: TLS processing failed");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return resp;
+		}
+
+		if (sm->waiting_ext_cert_check) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Waiting external server certificate validation");
+			wpabuf_free(data->pending_resp);
+			data->pending_resp = resp;
+			return NULL;
+		}
+
+		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 =
+				tls_connection_get_cipher_suite(data->ssl.conn);
+			wpa_printf(MSG_DEBUG,
+				   "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,
+					   "EAP-TEAP: Could not derive keys");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				wpabuf_free(resp);
+				return NULL;
+			}
+		}
+
+		if (res == 2) {
+			/*
+			 * Application data included in the handshake message.
+			 */
+			wpabuf_free(data->pending_phase2_req);
+			data->pending_phase2_req = resp;
+			resp = NULL;
+			res = eap_teap_decrypt(sm, data, ret, id, &msg, &resp);
+		}
+	}
+
+	if (res == 1) {
+		wpabuf_free(resp);
+		return eap_peer_tls_build_ack(id, EAP_TYPE_TEAP,
+					      data->teap_version);
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (data->test_outer_tlvs && res == 0 && resp &&
+	    (flags & EAP_TLS_FLAGS_START) && wpabuf_len(resp) >= 6)
+		resp = eap_teap_add_dummy_outer_tlvs(data, resp);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return resp;
+}
+
+
+#if 0 /* TODO */
+static Boolean eap_teap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_teap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->deinit_for_reauth)
+		data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
+	eap_teap_clear(data);
+}
+
+
+static void * eap_teap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+		eap_teap_deinit(sm, data);
+		return NULL;
+	}
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->init_for_reauth)
+		data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+	data->phase2_success = 0;
+	data->inner_method_done = 0;
+	data->result_success_done = 0;
+	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;
+}
+#endif
+
+
+static int eap_teap_get_status(struct eap_sm *sm, void *priv, char *buf,
+			       size_t buflen, int verbose)
+{
+	struct eap_teap_data *data = priv;
+	int len, ret;
+
+	len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+	if (data->phase2_method) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "EAP-TEAP Phase 2 method=%s\n",
+				  data->phase2_method->name);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+	return len;
+}
+
+
+static Boolean eap_teap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	return data->success;
+}
+
+
+static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	u8 *key;
+
+	if (!data->success)
+		return NULL;
+
+	key = os_memdup(data->key_data, EAP_TEAP_KEY_LEN);
+	if (!key)
+		return NULL;
+
+	*len = EAP_TEAP_KEY_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	u8 *id;
+
+	if (!data->success || !data->session_id)
+		return NULL;
+
+	id = os_memdup(data->session_id, data->id_len);
+	if (!id)
+		return NULL;
+
+	*len = data->id_len;
+
+	return id;
+}
+
+
+static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	u8 *key;
+
+	if (!data->success)
+		return NULL;
+
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
+	if (!key)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+int eap_peer_teap_register(void)
+{
+	struct eap_method *eap;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP");
+	if (!eap)
+		return -1;
+
+	eap->init = eap_teap_init;
+	eap->deinit = eap_teap_deinit;
+	eap->process = eap_teap_process;
+	eap->isKeyAvailable = eap_teap_isKeyAvailable;
+	eap->getKey = eap_teap_getKey;
+	eap->getSessionId = eap_teap_get_session_id;
+	eap->get_status = eap_teap_get_status;
+#if 0 /* TODO */
+	eap->has_reauth_data = eap_teap_has_reauth_data;
+	eap->deinit_for_reauth = eap_teap_deinit_for_reauth;
+	eap->init_for_reauth = eap_teap_init_for_reauth;
+#endif
+	eap->get_emsk = eap_teap_get_emsk;
+
+	return eap_peer_method_register(eap);
+}
diff --git a/src/eap_peer/eap_teap_pac.c b/src/eap_peer/eap_teap_pac.c
new file mode 100644
index 0000000..34a2743
--- /dev/null
+++ b/src/eap_peer/eap_teap_pac.c
@@ -0,0 +1,931 @@
+/*
+ * 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
new file mode 100644
index 0000000..edf4c57
--- /dev/null
+++ b/src/eap_peer/eap_teap_pac.h
@@ -0,0 +1,50 @@
+/*
+ * 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 ffea9d2..d9771f6 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -33,10 +33,17 @@
 {
 	struct eap_tls_data *data;
 	struct eap_peer_config *config = eap_get_config(sm);
-	if (config == NULL ||
-	    ((sm->init_phase2 ? config->private_key2 : config->private_key)
-	     == NULL &&
-	     (sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
+	struct eap_peer_cert_config *cert;
+
+	if (!config)
+		return NULL;
+	if (!sm->init_phase2)
+		cert = &config->cert;
+	else if (sm->use_machine_cred)
+		cert = &config->machine_cert;
+	else
+		cert = &config->phase2_cert;
+	if (!cert->private_key && cert->engine == 0) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
 		return NULL;
 	}
@@ -51,13 +58,12 @@
 	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 		eap_tls_deinit(sm, data);
-		if (config->engine) {
+		if (cert->engine) {
 			wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
 				   "PIN");
 			eap_sm_request_pin(sm);
 			sm->ignore = TRUE;
-		} else if (config->private_key && !config->private_key_passwd)
-		{
+		} else if (cert->private_key && !cert->private_key_passwd) {
 			wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
 				   "key passphrase");
 			eap_sm_request_passphrase(sm);
@@ -174,6 +180,9 @@
 			    struct eap_method_ret *ret)
 {
 	const char *label;
+	const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
+	const u8 *context = NULL;
+	size_t context_len = 0;
 
 	wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
 
@@ -184,6 +193,8 @@
 
 	if (data->ssl.tls_v13) {
 		label = "EXPORTER_EAP_TLS_Key_Material";
+		context = eap_tls13_context;
+		context_len = 1;
 
 		/* A possible NewSessionTicket may be received before
 		 * EAP-Success, so need to allow it to be received. */
@@ -198,7 +209,7 @@
 
 	eap_tls_free_key(data);
 	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label,
-						 NULL, 0,
+						 context, context_len,
 						 EAP_TLS_KEY_LEN +
 						 EAP_EMSK_LEN);
 	if (data->key_data) {
@@ -291,6 +302,18 @@
 		return NULL;
 	}
 
+	if (res == 2) {
+		/* Application data included in the handshake message (used by
+		 * EAP-TLS 1.3 to indicate conclusion of the exchange). */
+		wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Received Application Data",
+				resp);
+		wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Remaining tls_out data",
+				data->ssl.tls_out);
+		eap_peer_tls_reset_output(&data->ssl);
+		/* Send an ACK to allow the server to complete exchange */
+		res = 1;
+	}
+
 	if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
 		eap_tls_success(sm, data, ret);
 
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index cb94c45..80e2d71 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -16,7 +16,7 @@
 #include "eap_config.h"
 
 
-static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+static struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len,
 					 u8 code, u8 identifier)
 {
 	if (type == EAP_UNAUTH_TLS_TYPE)
@@ -105,8 +105,8 @@
 }
 
 
-static void eap_tls_params_from_conf1(struct tls_connection_params *params,
-				      struct eap_peer_config *config)
+static void eap_tls_cert_params_from_conf(struct tls_connection_params *params,
+					  struct eap_peer_cert_config *config)
 {
 	params->ca_cert = config->ca_cert;
 	params->ca_path = config->ca_path;
@@ -125,6 +125,19 @@
 	params->key_id = config->key_id;
 	params->cert_id = config->cert_id;
 	params->ca_cert_id = config->ca_cert_id;
+	if (config->ocsp)
+		params->flags |= TLS_CONN_REQUEST_OCSP;
+	if (config->ocsp >= 2)
+		params->flags |= TLS_CONN_REQUIRE_OCSP;
+	if (config->ocsp == 3)
+		params->flags |= TLS_CONN_REQUIRE_OCSP_ALL;
+}
+
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+				      struct eap_peer_config *config)
+{
+	eap_tls_cert_params_from_conf(params, &config->cert);
 	eap_tls_params_flags(params, config->phase1);
 }
 
@@ -132,34 +145,27 @@
 static void eap_tls_params_from_conf2(struct tls_connection_params *params,
 				      struct eap_peer_config *config)
 {
-	params->ca_cert = config->ca_cert2;
-	params->ca_path = config->ca_path2;
-	params->client_cert = config->client_cert2;
-	params->private_key = config->private_key2;
-	params->private_key_passwd = config->private_key2_passwd;
-	params->dh_file = config->dh_file2;
-	params->subject_match = config->subject_match2;
-	params->altsubject_match = config->altsubject_match2;
-	params->check_cert_subject = config->check_cert_subject2;
-	params->suffix_match = config->domain_suffix_match2;
-	params->domain_match = config->domain_match2;
-	params->engine = config->engine2;
-	params->engine_id = config->engine2_id;
-	params->pin = config->pin2;
-	params->key_id = config->key2_id;
-	params->cert_id = config->cert2_id;
-	params->ca_cert_id = config->ca_cert2_id;
+	eap_tls_cert_params_from_conf(params, &config->phase2_cert);
 	eap_tls_params_flags(params, config->phase2);
 }
 
 
+static void eap_tls_params_from_conf2m(struct tls_connection_params *params,
+				       struct eap_peer_config *config)
+{
+	eap_tls_cert_params_from_conf(params, &config->machine_cert);
+	eap_tls_params_flags(params, config->machine_phase2);
+}
+
+
 static int eap_tls_params_from_conf(struct eap_sm *sm,
 				    struct eap_ssl_data *data,
 				    struct tls_connection_params *params,
 				    struct eap_peer_config *config, int phase2)
 {
 	os_memset(params, 0, sizeof(*params));
-	if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
+	if (sm->workaround && data->eap_type != EAP_TYPE_FAST &&
+	    data->eap_type != EAP_TYPE_TEAP) {
 		/*
 		 * Some deployed authentication servers seem to be unable to
 		 * handle the TLS Session Ticket extension (they are supposed
@@ -171,7 +177,15 @@
 		 */
 		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
 	}
+	if (data->eap_type == EAP_TYPE_TEAP) {
+		/* 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 ||
 	    data->eap_type == EAP_TYPE_TTLS ||
 	    data->eap_type == EAP_TYPE_PEAP) {
 		/* The current EAP peer implementation is not yet ready for the
@@ -190,7 +204,10 @@
 		 */
 		params->flags |= TLS_CONN_DISABLE_TLSv1_3;
 	}
-	if (phase2) {
+	if (phase2 && sm->use_machine_cred) {
+		wpa_printf(MSG_DEBUG, "TLS: using machine config options");
+		eap_tls_params_from_conf2m(params, config);
+	} else if (phase2) {
 		wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
 		eap_tls_params_from_conf2(params, config);
 	} else {
@@ -233,12 +250,6 @@
 {
 	int res;
 
-	if (config->ocsp)
-		params->flags |= TLS_CONN_REQUEST_OCSP;
-	if (config->ocsp >= 2)
-		params->flags |= TLS_CONN_REQUIRE_OCSP;
-	if (config->ocsp == 3)
-		params->flags |= TLS_CONN_REQUIRE_OCSP_ALL;
 	data->conn = tls_connection_init(data->ssl_ctx);
 	if (data->conn == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
@@ -255,8 +266,8 @@
 		 */
 		wpa_printf(MSG_INFO,
 			   "TLS: Bad PIN provided, requesting a new one");
-		os_free(config->pin);
-		config->pin = NULL;
+		os_free(config->cert.pin);
+		config->cert.pin = NULL;
 		eap_sm_request_pin(sm);
 		sm->ignore = TRUE;
 	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
@@ -404,17 +415,18 @@
 
 	if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
 		u8 *id, *method_id;
+		const u8 context[] = { EAP_TYPE_TLS };
 
 		/* Session-Id = <EAP-Type> || Method-Id
 		 * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id",
-		 *                          "", 64)
+		 *                          Type-Code, 64)
 		 */
 		*len = 1 + 64;
 		id = os_malloc(*len);
 		if (!id)
 			return NULL;
 		method_id = eap_peer_tls_derive_key(
-			sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64);
+			sm, data, "EXPORTER_EAP_TLS_Method-Id", context, 1, 64);
 		if (!method_id) {
 			os_free(id);
 			return NULL;
@@ -609,7 +621,8 @@
  * @out_data: Buffer for returning the allocated output buffer
  * Returns: ret (0 or 1) on success, -1 on failure
  */
-static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+static int eap_tls_process_output(struct eap_ssl_data *data,
+				  enum eap_type eap_type,
 				  int peap_version, u8 id, int ret,
 				  struct wpabuf **out_data)
 {
@@ -707,7 +720,7 @@
  * the tunneled data is used.
  */
 int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
-				EapType eap_type, int peap_version,
+				enum eap_type eap_type, int peap_version,
 				u8 id, const struct wpabuf *in_data,
 				struct wpabuf **out_data)
 {
@@ -799,7 +812,7 @@
  * @peap_version: Version number for EAP-PEAP/TTLS
  * Returns: Pointer to the allocated ACK frame or %NULL on failure
  */
-struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+struct wpabuf * eap_peer_tls_build_ack(u8 id, enum eap_type eap_type,
 				       int peap_version)
 {
 	struct wpabuf *resp;
@@ -889,7 +902,7 @@
  */
 const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
 				     struct eap_ssl_data *data,
-				     EapType eap_type,
+				     enum eap_type eap_type,
 				     struct eap_method_ret *ret,
 				     const struct wpabuf *reqData,
 				     size_t *len, u8 *flags)
@@ -1046,7 +1059,7 @@
  * Returns: 0 on success, -1 on failure
  */
 int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
-			 EapType eap_type, int peap_version, u8 id,
+			 enum eap_type eap_type, int peap_version, u8 id,
 			 const struct wpabuf *in_data,
 			 struct wpabuf **out_data)
 {
@@ -1082,17 +1095,21 @@
 int eap_peer_select_phase2_methods(struct eap_peer_config *config,
 				   const char *prefix,
 				   struct eap_method_type **types,
-				   size_t *num_types)
+				   size_t *num_types, int use_machine_cred)
 {
 	char *start, *pos, *buf;
 	struct eap_method_type *methods = NULL, *_methods;
 	u32 method;
 	size_t num_methods = 0, prefix_len;
+	const char *phase2;
 
-	if (config == NULL || config->phase2 == NULL)
+	if (!config)
+		goto get_defaults;
+	phase2 = use_machine_cred ? config->machine_phase2 : config->phase2;
+	if (!phase2)
 		goto get_defaults;
 
-	start = buf = os_strdup(config->phase2);
+	start = buf = os_strdup(phase2);
 	if (buf == NULL)
 		return -1;
 
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 5f82529..183b7de 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -70,7 +70,8 @@
 	void *ssl_ctx;
 
 	/**
-	 * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+	 * eap_type - EAP method used in Phase 1
+	 * (EAP_TYPE_TLS/PEAP/TTLS/FAST/TEAP)
 	 */
 	u8 eap_type;
 
@@ -85,6 +86,7 @@
 #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
 #define EAP_TLS_FLAGS_START 0x20
+#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10
 #define EAP_TLS_VERSION_MASK 0x07
 
  /* could be up to 128 bytes, but only the first 64 bytes are used */
@@ -105,17 +107,17 @@
 				    struct eap_ssl_data *data, u8 eap_type,
 				    size_t *len);
 int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
-				EapType eap_type, int peap_version,
+				enum eap_type eap_type, int peap_version,
 				u8 id, const struct wpabuf *in_data,
 				struct wpabuf **out_data);
-struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+struct wpabuf * eap_peer_tls_build_ack(u8 id, enum eap_type eap_type,
 				       int peap_version);
 int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
 int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
 			char *buf, size_t buflen, int verbose);
 const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
 				     struct eap_ssl_data *data,
-				     EapType eap_type,
+				     enum eap_type eap_type,
 				     struct eap_method_ret *ret,
 				     const struct wpabuf *reqData,
 				     size_t *len, u8 *flags);
@@ -125,13 +127,13 @@
 			 const struct wpabuf *in_data,
 			 struct wpabuf **in_decrypted);
 int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
-			 EapType eap_type, int peap_version, u8 id,
+			 enum eap_type eap_type, int peap_version, u8 id,
 			 const struct wpabuf *in_data,
 			 struct wpabuf **out_data);
 int eap_peer_select_phase2_methods(struct eap_peer_config *config,
 				   const char *prefix,
 				   struct eap_method_type **types,
-				   size_t *num_types);
+				   size_t *num_types, int use_machine_cred);
 int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
 			    struct eap_hdr *hdr, struct wpabuf **resp);
 
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 1c8dbe2..662676f 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -146,8 +146,8 @@
 	if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
 		if (eap_peer_select_phase2_methods(config, "autheap=",
 						   &data->phase2_eap_types,
-						   &data->num_phase2_eap_types)
-		    < 0) {
+						   &data->num_phase2_eap_types,
+						   0) < 0) {
 			eap_ttls_deinit(sm, data);
 			return NULL;
 		}
@@ -311,11 +311,11 @@
 
 
 static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
-					      u8 method)
+					      int vendor, enum eap_type method)
 {
 	size_t i;
 	for (i = 0; i < data->num_phase2_eap_types; i++) {
-		if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+		if (data->phase2_eap_types[i].vendor != vendor ||
 		    data->phase2_eap_types[i].method != method)
 			continue;
 
@@ -362,17 +362,19 @@
 					      struct eap_ttls_data *data,
 					      struct eap_method_ret *ret,
 					      struct eap_hdr *hdr, size_t len,
-					      u8 method, struct wpabuf **resp)
+					      int vendor, enum eap_type method,
+					      struct wpabuf **resp)
 {
 #ifdef EAP_TNC
 	if (data->tnc_started && data->phase2_method &&
-	    data->phase2_priv && method == EAP_TYPE_TNC &&
+	    data->phase2_priv &&
+	    vendor == EAP_VENDOR_IETF && method == EAP_TYPE_TNC &&
 	    data->phase2_eap_type.method == EAP_TYPE_TNC)
 		return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
 						   resp);
 
 	if (data->ready_for_tnc && !data->tnc_started &&
-	    method == EAP_TYPE_TNC) {
+	    vendor == EAP_VENDOR_IETF && method == EAP_TYPE_TNC) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
 			   "EAP method");
 		data->tnc_started = 1;
@@ -386,7 +388,7 @@
 			return -1;
 		}
 
-		data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_eap_type.vendor = vendor;
 		data->phase2_eap_type.method = method;
 		wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
 			   "Phase 2 EAP vendor %d method %d (TNC)",
@@ -400,10 +402,11 @@
 
 	if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
 	    data->phase2_eap_type.method == EAP_TYPE_NONE)
-		eap_ttls_phase2_select_eap_method(data, method);
+		eap_ttls_phase2_select_eap_method(data, vendor, method);
 
-	if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
-	{
+	if (vendor != data->phase2_eap_type.vendor ||
+	    method != data->phase2_eap_type.method ||
+	    (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE)) {
 		if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
 					    data->num_phase2_eap_types,
 					    hdr, resp))
@@ -412,8 +415,7 @@
 	}
 
 	if (data->phase2_priv == NULL) {
-		data->phase2_method = eap_peer_get_eap_method(
-			EAP_VENDOR_IETF, method);
+		data->phase2_method = eap_peer_get_eap_method(vendor, method);
 		if (data->phase2_method) {
 			sm->init_phase2 = 1;
 			data->phase2_priv = data->phase2_method->init(sm);
@@ -421,8 +423,9 @@
 		}
 	}
 	if (data->phase2_priv == NULL || data->phase2_method == NULL) {
-		wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
-			   "Phase 2 EAP method %d", method);
+		wpa_printf(MSG_INFO,
+			   "EAP-TTLS: failed to initialize Phase 2 EAP method %u:%u",
+			   vendor, method);
 		return -1;
 	}
 
@@ -451,9 +454,23 @@
 	case EAP_TYPE_IDENTITY:
 		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
 		break;
+	case EAP_TYPE_EXPANDED:
+		if (len < sizeof(struct eap_hdr) + 8) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TTLS: Too short Phase 2 request (expanded header) (len=%lu)",
+				   (unsigned long) len);
+			return -1;
+		}
+		if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+						       WPA_GET_BE24(pos + 1),
+						       WPA_GET_BE32(pos + 4),
+						       resp) < 0)
+			return -1;
+		break;
 	default:
 		if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
-						       *pos, resp) < 0)
+						       EAP_VENDOR_IETF, *pos,
+						       resp) < 0)
 			return -1;
 		break;
 	}
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index a9bafe2..c460980 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -144,7 +144,7 @@
 	TNC_MessageType messageType)
 {
 	struct tnc_if_imc *imc;
-	unsigned char *b64;
+	char *b64;
 	size_t b64len;
 
 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
@@ -629,8 +629,7 @@
 		return NULL;
 	*pos2 = '\0';
 
-	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
-				decoded_len);
+	decoded = base64_decode(pos, os_strlen(pos), decoded_len);
 	*pos2 = '<';
 	if (decoded == NULL) {
 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index b130368..540b4e7 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -108,35 +108,162 @@
 };
 
 struct eap_config {
+	/**
+	 * ssl_ctx - TLS context
+	 *
+	 * This is passed to the EAP server implementation as a callback
+	 * context for TLS operations.
+	 */
 	void *ssl_ctx;
 	void *msg_ctx;
+
+	/**
+	 * eap_sim_db_priv - EAP-SIM/AKA database context
+	 *
+	 * This is passed to the EAP-SIM/AKA server implementation as a
+	 * callback context.
+	 */
 	void *eap_sim_db_priv;
 	Boolean backend_auth;
 	int eap_server;
+
+	/**
+	 * pwd_group - The D-H group assigned for EAP-pwd
+	 *
+	 * If EAP-pwd is not used it can be set to zero.
+	 */
 	u16 pwd_group;
+
+	/**
+	 * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
+	 *
+	 * This parameter is used to set a key for EAP-FAST to encrypt the
+	 * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
+	 * set, must point to a 16-octet key.
+	 */
 	u8 *pac_opaque_encr_key;
+
+	/**
+	 * eap_fast_a_id - EAP-FAST authority identity (A-ID)
+	 *
+	 * If EAP-FAST is not used, this can be set to %NULL. In theory, this
+	 * is a variable length field, but due to some existing implementations
+	 * requiring A-ID to be 16 octets in length, it is recommended to use
+	 * that length for the field to provide interoperability with deployed
+	 * peer implementations.
+	 */
 	u8 *eap_fast_a_id;
+
+	/**
+	 * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
+	 */
 	size_t eap_fast_a_id_len;
+	/**
+	 * eap_fast_a_id_info - EAP-FAST authority identifier information
+	 *
+	 * This A-ID-Info contains a user-friendly name for the A-ID. For
+	 * example, this could be the enterprise and server names in
+	 * human-readable format. This field is encoded as UTF-8. If EAP-FAST
+	 * is not used, this can be set to %NULL.
+	 */
 	char *eap_fast_a_id_info;
-	int eap_fast_prov;
+
+	/**
+	 * eap_fast_prov - EAP-FAST provisioning modes
+	 *
+	 * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
+	 * 2 = only authenticated provisioning allowed, 3 = both provisioning
+	 * modes allowed.
+	 */
+	enum {
+		NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
+	} eap_fast_prov;
+
+	/**
+	 * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
+	 *
+	 * This is the hard limit on how long a provisioned PAC-Key can be
+	 * used.
+	 */
 	int pac_key_lifetime;
+
+	/**
+	 * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
+	 *
+	 * This is a soft limit on the PAC-Key. The server will automatically
+	 * generate a new PAC-Key when this number of seconds (or fewer) of the
+	 * lifetime remains.
+	 */
 	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,
+		EAP_TEAP_ID_REQUIRE_USER = 1,
+		EAP_TEAP_ID_REQUIRE_MACHINE = 2,
+		EAP_TEAP_ID_REQUEST_USER_ACCEPT_MACHINE = 3,
+		EAP_TEAP_ID_REQUEST_MACHINE_ACCEPT_USER = 4,
+		EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE = 5,
+	} eap_teap_id;
+
+	/**
+	 * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
+	 *
+	 * This controls whether the protected success/failure indication
+	 * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
+	 */
 	int eap_sim_aka_result_ind;
+	int eap_sim_id;
+
+	/**
+	 * tnc - Trusted Network Connect (TNC)
+	 *
+	 * This controls whether TNC is enabled and will be required before the
+	 * peer is allowed to connect. Note: This is only used with EAP-TTLS
+	 * and EAP-FAST. If any other EAP method is enabled, the peer will be
+	 * allowed to connect without TNC.
+	 */
 	int tnc;
+
+	/**
+	 * wps - Wi-Fi Protected Setup context
+	 *
+	 * If WPS is used with an external RADIUS server (which is quite
+	 * unlikely configuration), this is used to provide a pointer to WPS
+	 * context data. Normally, this can be set to %NULL.
+	 */
 	struct wps_context *wps;
-	const struct wpabuf *assoc_wps_ie;
-	const struct wpabuf *assoc_p2p_ie;
-	const u8 *peer_addr;
 	int fragment_size;
 
 	int pbc_in_m1;
 
-	const u8 *server_id;
+	/**
+	 * server_id - Server identity
+	 */
+	u8 *server_id;
 	size_t server_id_len;
+
+	/**
+	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+	 *
+	 * This controls whether the authentication server derives ERP key
+	 * hierarchy (rRK and rIK) from full EAP authentication and allows
+	 * these keys to be used to perform ERP to derive rMSK instead of full
+	 * EAP authentication to derive MSK.
+	 */
 	int erp;
 	unsigned int tls_session_lifetime;
 	unsigned int tls_flags;
 
+	unsigned int max_auth_rounds;
+	unsigned int max_auth_rounds_short;
+};
+
+struct eap_session_data {
+	const struct wpabuf *assoc_wps_ie;
+	const struct wpabuf *assoc_p2p_ie;
+	const u8 *peer_addr;
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -145,7 +272,8 @@
 
 struct eap_sm * eap_server_sm_init(void *eapol_ctx,
 				   const struct eapol_callbacks *eapol_cb,
-				   struct eap_config *eap_conf);
+				   const struct eap_config *conf,
+				   const struct eap_session_data *sess);
 void eap_server_sm_deinit(struct eap_sm *sm);
 int eap_server_sm_step(struct eap_sm *sm);
 void eap_sm_notify_cached(struct eap_sm *sm);
@@ -162,5 +290,6 @@
 				   const u8 *challenge, const u8 *response);
 void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len);
 void eap_user_free(struct eap_user *user);
+void eap_server_config_free(struct eap_config *cfg);
 
 #endif /* EAP_H */
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 1cade10..44896a6 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -23,7 +23,7 @@
  */
 struct eap_method {
 	int vendor;
-	EapType method;
+	enum eap_type method;
 	const char *name;
 
 	void * (*init)(struct eap_sm *sm);
@@ -128,7 +128,7 @@
 	/* Full authenticator state machine local variables */
 
 	/* Long-term (maintained between packets) */
-	EapType currentMethod;
+	enum eap_type currentMethod;
 	int currentId;
 	enum {
 		METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
@@ -141,7 +141,7 @@
 	Boolean rxResp;
 	Boolean rxInitiate;
 	int respId;
-	EapType respMethod;
+	enum eap_type respMethod;
 	int respVendor;
 	u32 respVendorMethod;
 	Boolean ignore;
@@ -154,7 +154,7 @@
 	const struct eap_method *m; /* selected EAP method */
 	/* not defined in RFC 4137 */
 	Boolean changed;
-	void *eapol_ctx, *msg_ctx;
+	void *eapol_ctx;
 	const struct eapol_callbacks *eapol_cb;
 	void *eap_method_priv;
 	u8 *identity;
@@ -167,13 +167,12 @@
 	struct eap_user *user;
 	int user_eap_method_index;
 	int init_phase2;
-	void *ssl_ctx;
-	struct eap_sim_db_data *eap_sim_db_priv;
-	Boolean backend_auth;
+	const struct eap_config *cfg;
+	struct eap_config cfg_buf;
 	Boolean update_user;
-	int eap_server;
 
-	int num_rounds;
+	unsigned int num_rounds;
+	unsigned int num_rounds_short;
 	enum {
 		METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
 	} method_pending;
@@ -181,19 +180,6 @@
 	u8 *auth_challenge;
 	u8 *peer_challenge;
 
-	u8 *pac_opaque_encr_key;
-	u8 *eap_fast_a_id;
-	size_t eap_fast_a_id_len;
-	char *eap_fast_a_id_info;
-	enum {
-		NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
-	} eap_fast_prov;
-	int pac_key_lifetime;
-	int pac_key_refresh_time;
-	int eap_sim_aka_result_ind;
-	int tnc;
-	u16 pwd_group;
-	struct wps_context *wps;
 	struct wpabuf *assoc_wps_ie;
 	struct wpabuf *assoc_p2p_ie;
 
@@ -201,19 +187,8 @@
 
 	u8 peer_addr[ETH_ALEN];
 
-	/* Fragmentation size for EAP method init() handler */
-	int fragment_size;
-
-	int pbc_in_m1;
-
-	const u8 *server_id;
-	size_t server_id_len;
-
 	Boolean initiate_reauth_start_sent;
 	Boolean try_initiate_reauth;
-	int erp;
-	unsigned int tls_session_lifetime;
-	unsigned int tls_flags;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
index 3bf1495..ad60700 100644
--- a/src/eap_server/eap_methods.h
+++ b/src/eap_server/eap_methods.h
@@ -12,14 +12,15 @@
 #include "eap_common/eap_defs.h"
 
 const struct eap_method * eap_server_get_eap_method(int vendor,
-						    EapType method);
+						    enum eap_type method);
 struct eap_method * eap_server_method_alloc(int version, int vendor,
-					    EapType method, const char *name);
+					    enum eap_type method,
+					    const char *name);
 int eap_server_method_register(struct eap_method *method);
 
-EapType eap_server_get_type(const char *name, int *vendor);
+enum eap_type eap_server_get_type(const char *name, int *vendor);
 void eap_server_unregister_methods(void);
-const char * eap_server_get_name(int vendor, EapType type);
+const char * eap_server_get_name(int vendor, enum eap_type type);
 
 /* EAP server method registration calls for statically linked in methods */
 int eap_server_identity_register(void);
@@ -41,6 +42,7 @@
 int eap_server_gpsk_register(void);
 int eap_server_vendor_test_register(void);
 int eap_server_fast_register(void);
+int eap_server_teap_register(void);
 int eap_server_wsc_register(void);
 int eap_server_ikev2_register(void);
 int eap_server_tnc_register(void);
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index e8b36e1..34ce239 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -23,8 +23,6 @@
 #define STATE_MACHINE_DATA struct eap_sm
 #define STATE_MACHINE_DEBUG_PREFIX "EAP"
 
-#define EAP_MAX_AUTH_ROUNDS 50
-
 /* EAP state machines are described in RFC 4137 */
 
 static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
@@ -37,9 +35,10 @@
 static int eap_sm_nextId(struct eap_sm *sm, int id);
 static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
 				 size_t len);
-static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
+static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm,
+						 int *vendor);
 static int eap_sm_Policy_getDecision(struct eap_sm *sm);
-static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method);
 
 
 static int eap_get_erp_send_reauth_start(struct eap_sm *sm)
@@ -94,7 +93,7 @@
 	}
 
 	msg = eap_msg_alloc(EAP_VENDOR_IETF,
-			    (EapType) EAP_ERP_TYPE_REAUTH_START, plen,
+			    (enum eap_type) EAP_ERP_TYPE_REAUTH_START, plen,
 			    EAP_CODE_INITIATE, id);
 	if (msg == NULL)
 		return NULL;
@@ -215,6 +214,7 @@
 {
 	SM_ENTRY(EAP, DISABLED);
 	sm->num_rounds = 0;
+	sm->num_rounds_short = 0;
 }
 
 
@@ -222,7 +222,7 @@
 {
 	SM_ENTRY(EAP, INITIALIZE);
 
-	if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
+	if (sm->eap_if.eapRestart && !sm->cfg->eap_server && sm->identity) {
 		/*
 		 * Need to allow internal Identity method to be used instead
 		 * of passthrough at the beginning of reauthentication.
@@ -256,7 +256,7 @@
 	sm->m = NULL;
 	sm->user_eap_method_index = 0;
 
-	if (sm->backend_auth) {
+	if (sm->cfg->backend_auth) {
 		sm->currentMethod = EAP_TYPE_NONE;
 		/* parse rxResp, respId, respMethod */
 		eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
@@ -265,9 +265,10 @@
 		}
 	}
 	sm->num_rounds = 0;
+	sm->num_rounds_short = 0;
 	sm->method_pending = METHOD_PENDING_NONE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
 		MACSTR, MAC2STR(sm->peer_addr));
 }
 
@@ -299,7 +300,7 @@
 		}
 	}
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 		"method=%u", sm->currentMethod);
 }
 
@@ -324,7 +325,7 @@
 			sm->eap_if.eapReq = TRUE;
 	}
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR,
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR,
 		MAC2STR(sm->peer_addr));
 }
 
@@ -336,6 +337,10 @@
 	/* parse rxResp, respId, respMethod */
 	eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
 	sm->num_rounds++;
+	if (!sm->eap_if.eapRespData || wpabuf_len(sm->eap_if.eapRespData) < 20)
+		sm->num_rounds_short++;
+	else
+		sm->num_rounds_short = 0;
 }
 
 
@@ -353,6 +358,8 @@
 
 	sm->retransCount = 0;
 	if (sm->eap_if.eapReqData) {
+		if (wpabuf_len(sm->eap_if.eapReqData) >= 20)
+			sm->num_rounds_short = 0;
 		if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
 		{
 			sm->eap_if.eapResp = FALSE;
@@ -529,7 +536,7 @@
 				    sm->eap_if.eapSessionId,
 				    sm->eap_if.eapSessionIdLen);
 		}
-		if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
+		if (sm->cfg->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
 			eap_server_erp_init(sm);
 		sm->methodState = METHOD_END;
 	} else {
@@ -541,7 +548,7 @@
 SM_STATE(EAP, PROPOSE_METHOD)
 {
 	int vendor;
-	EapType type;
+	enum eap_type type;
 
 	SM_ENTRY(EAP, PROPOSE_METHOD);
 
@@ -579,7 +586,7 @@
 	else
 		sm->methodState = METHOD_PROPOSED;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 		"vendor=%u method=%u", vendor, sm->currentMethod);
 	eap_log_msg(sm, "Propose EAP method vendor=%u method=%u",
 		    vendor, sm->currentMethod);
@@ -635,8 +642,8 @@
 
 	sm->eap_if.eapTimeout = TRUE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR,
-		MAC2STR(sm->peer_addr));
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO,
+		WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR, MAC2STR(sm->peer_addr));
 }
 
 
@@ -650,7 +657,7 @@
 	sm->lastReqData = NULL;
 	sm->eap_if.eapFail = TRUE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
 		MACSTR, MAC2STR(sm->peer_addr));
 }
 
@@ -667,7 +674,7 @@
 		sm->eap_if.eapKeyAvailable = TRUE;
 	sm->eap_if.eapSuccess = TRUE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
 		MACSTR, MAC2STR(sm->peer_addr));
 }
 
@@ -720,7 +727,8 @@
 	plen = 1 + 2 + 2 + os_strlen(nai);
 	if (hash_len)
 		plen += 1 + hash_len;
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+	msg = eap_msg_alloc(EAP_VENDOR_IETF,
+			    (enum eap_type) EAP_ERP_TYPE_REAUTH,
 			    plen, EAP_CODE_FINISH, id);
 	if (msg == NULL)
 		return;
@@ -753,7 +761,7 @@
 
 	if ((flags & 0x80) || !erp) {
 		sm->eap_if.eapFail = TRUE;
-		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+		wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
 			MACSTR, MAC2STR(sm->peer_addr));
 		return;
 	}
@@ -781,7 +789,7 @@
 			sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
 	sm->eap_if.eapSuccess = TRUE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
 		MACSTR, MAC2STR(sm->peer_addr));
 }
 
@@ -805,7 +813,8 @@
 
 	sm->rxInitiate = FALSE;
 
-	pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+	pos = eap_hdr_validate(EAP_VENDOR_IETF,
+			       (enum eap_type) EAP_ERP_TYPE_REAUTH,
 			       sm->eap_if.eapRespData, &len);
 	if (pos == NULL) {
 		wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
@@ -852,7 +861,7 @@
 	os_memcpy(nai, parse.keyname, parse.keyname_len);
 	nai[parse.keyname_len] = '\0';
 
-	if (!sm->eap_server) {
+	if (!sm->cfg->eap_server) {
 		/*
 		 * In passthrough case, EAP-Initiate/Re-auth replaces
 		 * EAP Identity exchange. Use keyName-NAI as the user identity
@@ -1015,7 +1024,7 @@
 			sm->eap_if.eapReq = TRUE;
 	}
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR,
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR,
 		MAC2STR(sm->peer_addr));
 }
 
@@ -1108,8 +1117,8 @@
 
 	sm->eap_if.eapTimeout = TRUE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR,
-		MAC2STR(sm->peer_addr));
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO,
+		WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR, MAC2STR(sm->peer_addr));
 }
 
 
@@ -1120,7 +1129,7 @@
 	eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
 	sm->eap_if.eapFail = TRUE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR,
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR,
 		MAC2STR(sm->peer_addr));
 }
 
@@ -1149,7 +1158,7 @@
 	 */
 	sm->start_reauth = TRUE;
 
-	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR,
+	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR,
 		MAC2STR(sm->peer_addr));
 }
 
@@ -1160,17 +1169,26 @@
 		SM_ENTER_GLOBAL(EAP, INITIALIZE);
 	else if (!sm->eap_if.portEnabled)
 		SM_ENTER_GLOBAL(EAP, DISABLED);
-	else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
-		if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+	else if (sm->num_rounds > sm->cfg->max_auth_rounds) {
+		if (sm->num_rounds == sm->cfg->max_auth_rounds + 1) {
 			wpa_printf(MSG_DEBUG, "EAP: more than %d "
 				   "authentication rounds - abort",
-				   EAP_MAX_AUTH_ROUNDS);
+				   sm->cfg->max_auth_rounds);
 			sm->num_rounds++;
 			SM_ENTER_GLOBAL(EAP, FAILURE);
 		}
+	} else if (sm->num_rounds_short > sm->cfg->max_auth_rounds_short) {
+		if (sm->num_rounds_short ==
+		    sm->cfg->max_auth_rounds_short + 1) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: more than %d authentication rounds (short) - abort",
+				   sm->cfg->max_auth_rounds_short);
+			sm->num_rounds_short++;
+			SM_ENTER_GLOBAL(EAP, FAILURE);
+		}
 	} else switch (sm->EAP_state) {
 	case EAP_INITIALIZE:
-		if (sm->backend_auth) {
+		if (sm->cfg->backend_auth) {
 			if (!sm->rxResp)
 				SM_ENTER(EAP, SELECT_ACTION);
 			else if (sm->rxResp &&
@@ -1333,7 +1351,7 @@
 		else if (sm->decision == DECISION_INITIATE_REAUTH_START)
 			SM_ENTER(EAP, INITIATE_REAUTH_START);
 #ifdef CONFIG_ERP
-		else if (sm->eap_server && sm->erp && sm->rxInitiate)
+		else if (sm->cfg->eap_server && sm->cfg->erp && sm->rxInitiate)
 			SM_ENTER(EAP, INITIATE_RECEIVED);
 #endif /* CONFIG_ERP */
 		else
@@ -1343,7 +1361,7 @@
 		SM_ENTER(EAP, SEND_REQUEST);
 		break;
 	case EAP_INITIATE_RECEIVED:
-		if (!sm->eap_server)
+		if (!sm->cfg->eap_server)
 			SM_ENTER(EAP, SELECT_ACTION);
 		break;
 	case EAP_TIMEOUT_FAILURE:
@@ -1669,9 +1687,9 @@
 }
 
 
-static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
+static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
 {
-	EapType next;
+	enum eap_type next;
 	int idx = sm->user_eap_method_index;
 
 	/* In theory, there should be no problems with starting
@@ -1703,7 +1721,7 @@
 
 static int eap_sm_Policy_getDecision(struct eap_sm *sm)
 {
-	if (!sm->eap_server && sm->identity && !sm->start_reauth) {
+	if (!sm->cfg->eap_server && sm->identity && !sm->start_reauth) {
 		wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
 		return DECISION_PASSTHROUGH;
 	}
@@ -1783,7 +1801,7 @@
 }
 
 
-static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method)
 {
 	return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
 }
@@ -1834,7 +1852,8 @@
  */
 struct eap_sm * eap_server_sm_init(void *eapol_ctx,
 				   const struct eapol_callbacks *eapol_cb,
-				   struct eap_config *conf)
+				   const struct eap_config *conf,
+				   const struct eap_session_data *sess)
 {
 	struct eap_sm *sm;
 
@@ -1844,51 +1863,15 @@
 	sm->eapol_ctx = eapol_ctx;
 	sm->eapol_cb = eapol_cb;
 	sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
-	sm->ssl_ctx = conf->ssl_ctx;
-	sm->msg_ctx = conf->msg_ctx;
-	sm->eap_sim_db_priv = conf->eap_sim_db_priv;
-	sm->backend_auth = conf->backend_auth;
-	sm->eap_server = conf->eap_server;
-	if (conf->pac_opaque_encr_key) {
-		sm->pac_opaque_encr_key = os_malloc(16);
-		if (sm->pac_opaque_encr_key) {
-			os_memcpy(sm->pac_opaque_encr_key,
-				  conf->pac_opaque_encr_key, 16);
-		}
-	}
-	if (conf->eap_fast_a_id) {
-		sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
-		if (sm->eap_fast_a_id) {
-			os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
-				  conf->eap_fast_a_id_len);
-			sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
-		}
-	}
-	if (conf->eap_fast_a_id_info)
-		sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
-	sm->eap_fast_prov = conf->eap_fast_prov;
-	sm->pac_key_lifetime = conf->pac_key_lifetime;
-	sm->pac_key_refresh_time = conf->pac_key_refresh_time;
-	sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
-	sm->tnc = conf->tnc;
-	sm->wps = conf->wps;
-	if (conf->assoc_wps_ie)
-		sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
-	if (conf->assoc_p2p_ie)
-		sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
-	if (conf->peer_addr)
-		os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
-	sm->fragment_size = conf->fragment_size;
-	sm->pwd_group = conf->pwd_group;
-	sm->pbc_in_m1 = conf->pbc_in_m1;
-	sm->server_id = conf->server_id;
-	sm->server_id_len = conf->server_id_len;
-	sm->erp = conf->erp;
-	sm->tls_session_lifetime = conf->tls_session_lifetime;
-	sm->tls_flags = conf->tls_flags;
-
+	sm->cfg = conf;
+	if (sess->assoc_wps_ie)
+		sm->assoc_wps_ie = wpabuf_dup(sess->assoc_wps_ie);
+	if (sess->assoc_p2p_ie)
+		sm->assoc_p2p_ie = wpabuf_dup(sess->assoc_p2p_ie);
+	if (sess->peer_addr)
+		os_memcpy(sm->peer_addr, sess->peer_addr, ETH_ALEN);
 #ifdef CONFIG_TESTING_OPTIONS
-	sm->tls_test_flags = conf->tls_test_flags;
+	sm->tls_test_flags = sess->tls_test_flags;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
@@ -1918,9 +1901,6 @@
 	wpabuf_free(sm->eap_if.eapRespData);
 	os_free(sm->identity);
 	os_free(sm->serial_num);
-	os_free(sm->pac_opaque_encr_key);
-	os_free(sm->eap_fast_a_id);
-	os_free(sm->eap_fast_a_id_info);
 	wpabuf_free(sm->eap_if.aaaEapReqData);
 	wpabuf_free(sm->eap_if.aaaEapRespData);
 	bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen);
@@ -2110,3 +2090,15 @@
 		   source, user, hex_challenge, hex_response);
 }
 #endif /* CONFIG_TESTING_OPTIONS */
+
+
+void eap_server_config_free(struct eap_config *cfg)
+{
+	if (!cfg)
+		return;
+	os_free(cfg->pac_opaque_encr_key);
+	os_free(cfg->eap_fast_a_id);
+	os_free(cfg->eap_fast_a_id_info);
+	os_free(cfg->server_id);
+	os_free(cfg);
+}
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 1bea706..22dd965 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -30,6 +30,7 @@
 	u8 ck[EAP_AKA_CK_LEN];
 	u8 ik[EAP_AKA_IK_LEN];
 	u8 res[EAP_AKA_RES_MAX_LEN];
+	u8 reauth_mac[EAP_SIM_MAC_LEN];
 	size_t res_len;
 	enum {
 		IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
@@ -99,7 +100,7 @@
 		return 0;
 
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username);
-	data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv,
+	data->reauth = eap_sim_db_get_reauth_entry(sm->cfg->eap_sim_db_priv,
 						   username);
 	if (data->reauth == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - "
@@ -156,7 +157,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
 			   username);
 		permanent = eap_sim_db_get_permanent(
-			sm->eap_sim_db_priv, username);
+			sm->cfg->eap_sim_db_priv, username);
 		if (permanent == NULL) {
 			os_free(username);
 			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
@@ -181,7 +182,7 @@
 {
 	struct eap_aka_data *data;
 
-	if (sm->eap_sim_db_priv == NULL) {
+	if (!sm->cfg->eap_sim_db_priv) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
 		return NULL;
 	}
@@ -207,7 +208,7 @@
 	/* TODO: make ANID configurable; see 3GPP TS 24.302 */
 	char *network_name = "WLAN";
 
-	if (sm->eap_sim_db_priv == NULL) {
+	if (sm->cfg->eap_sim_db_priv == NULL) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
 		return NULL;
 	}
@@ -392,10 +393,13 @@
 			      const u8 *nonce_s)
 {
 	os_free(data->next_pseudonym);
-	if (nonce_s == NULL) {
+	if (!(sm->cfg->eap_sim_id & 0x01)) {
+		/* Use of pseudonyms disabled in configuration */
+		data->next_pseudonym = NULL;
+	} else if (!nonce_s) {
 		data->next_pseudonym =
 			eap_sim_db_get_next_pseudonym(
-				sm->eap_sim_db_priv,
+				sm->cfg->eap_sim_db_priv,
 				data->eap_method == EAP_TYPE_AKA_PRIME ?
 				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
 	} else {
@@ -403,10 +407,13 @@
 		data->next_pseudonym = NULL;
 	}
 	os_free(data->next_reauth_id);
-	if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
+	if (!(sm->cfg->eap_sim_id & 0x02)) {
+		/* Use of fast reauth disabled in configuration */
+		data->next_reauth_id = NULL;
+	} else if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
 		data->next_reauth_id =
 			eap_sim_db_get_next_reauth_id(
-				sm->eap_sim_db_priv,
+				sm->cfg->eap_sim_db_priv,
 				data->eap_method == EAP_TYPE_AKA_PRIME ?
 				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
 	} else {
@@ -498,7 +505,7 @@
 
 	eap_aka_add_checkcode(data, msg);
 
-	if (sm->eap_sim_aka_result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind) {
 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 	}
@@ -542,6 +549,7 @@
 					    struct eap_aka_data *data, u8 id)
 {
 	struct eap_sim_msg *msg;
+	struct wpabuf *buf;
 
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
 
@@ -574,14 +582,23 @@
 
 	eap_aka_add_checkcode(data, msg);
 
-	if (sm->eap_sim_aka_result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind) {
 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 	}
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
+	buf = eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
+
+	/* Remember this MAC before sending it to the peer. This MAC is used for
+	 * Session-Id calculation after receiving response from the peer and
+	 * after all other checks pass. */
+	os_memcpy(data->reauth_mac,
+		  wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN,
+		  EAP_SIM_MAC_LEN);
+
+	return buf;
 }
 
 
@@ -750,7 +767,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
 			   username);
 		permanent = eap_sim_db_get_permanent(
-			sm->eap_sim_db_priv, username);
+			sm->cfg->eap_sim_db_priv, username);
 		os_free(username);
 		if (permanent == NULL) {
 			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
@@ -786,7 +803,7 @@
 	size_t identity_len;
 	int res;
 
-	res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent,
+	res = eap_sim_db_get_aka_auth(sm->cfg->eap_sim_db_priv, data->permanent,
 				      data->rand, data->autn, data->ik,
 				      data->ck, data->res, &data->res_len, sm);
 	if (res == EAP_SIM_DB_PENDING) {
@@ -981,7 +998,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the "
 		   "correct AT_MAC");
-	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
 		data->use_result_ind = 1;
 		data->notification = EAP_SIM_SUCCESS;
 		eap_aka_state(data, NOTIFICATION);
@@ -989,14 +1006,15 @@
 		eap_aka_state(data, SUCCESS);
 
 	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
+		eap_sim_db_add_pseudonym(sm->cfg->eap_sim_db_priv,
+					 data->permanent,
 					 data->next_pseudonym);
 		data->next_pseudonym = NULL;
 	}
 	if (data->next_reauth_id) {
 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
-			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+			eap_sim_db_add_reauth_prime(sm->cfg->eap_sim_db_priv,
 						    data->permanent,
 						    data->next_reauth_id,
 						    data->counter + 1,
@@ -1004,7 +1022,7 @@
 						    data->k_re);
 #endif /* EAP_SERVER_AKA_PRIME */
 		} else {
-			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+			eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv,
 					      data->permanent,
 					      data->next_reauth_id,
 					      data->counter + 1,
@@ -1034,7 +1052,7 @@
 	 * maintaining a local flag stating whether this AUTS has already been
 	 * reported. */
 	if (!data->auts_reported &&
-	    eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent,
+	    eap_sim_db_resynchronize(sm->cfg->eap_sim_db_priv, data->permanent,
 				     attr->auts, data->rand)) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
@@ -1101,7 +1119,7 @@
 		return;
 	}
 
-	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
 		data->use_result_ind = 1;
 		data->notification = EAP_SIM_SUCCESS;
 		eap_aka_state(data, NOTIFICATION);
@@ -1111,7 +1129,7 @@
 	if (data->next_reauth_id) {
 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
-			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+			eap_sim_db_add_reauth_prime(sm->cfg->eap_sim_db_priv,
 						    data->permanent,
 						    data->next_reauth_id,
 						    data->counter + 1,
@@ -1119,7 +1137,7 @@
 						    data->k_re);
 #endif /* EAP_SERVER_AKA_PRIME */
 		} else {
-			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+			eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv,
 					      data->permanent,
 					      data->next_reauth_id,
 					      data->counter + 1,
@@ -1127,7 +1145,8 @@
 		}
 		data->next_reauth_id = NULL;
 	} else {
-		eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+		eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv,
+					 data->reauth);
 		data->reauth = NULL;
 	}
 
@@ -1136,7 +1155,7 @@
 fail:
 	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
 	eap_aka_state(data, NOTIFICATION);
-	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+	eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv, data->reauth);
 	data->reauth = NULL;
 	os_free(decrypted);
 }
@@ -1304,14 +1323,24 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	if (!data->reauth)
+		*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	else
+		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
 	id = os_malloc(*len);
 	if (id == NULL)
 		return NULL;
 
 	id[0] = data->eap_method;
-	os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
-	os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+	if (!data->reauth) {
+		os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+		os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn,
+			  EAP_AKA_AUTN_LEN);
+	} else {
+		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
+		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
+			  EAP_SIM_MAC_LEN);
+	}
 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
 
 	return id;
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index 71580bf..71fab96 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -84,11 +84,11 @@
 	eap_eke_state(data, IDENTITY);
 
 	data->serverid_type = EAP_EKE_ID_OPAQUE;
-	for (i = 0; i < sm->server_id_len; i++) {
-		if (sm->server_id[i] == '.' &&
+	for (i = 0; i < sm->cfg->server_id_len; i++) {
+		if (sm->cfg->server_id[i] == '.' &&
 		    data->serverid_type == EAP_EKE_ID_OPAQUE)
 			data->serverid_type = EAP_EKE_ID_FQDN;
-		if (sm->server_id[i] == '@')
+		if (sm->cfg->server_id[i] == '@')
 			data->serverid_type = EAP_EKE_ID_NAI;
 	}
 
@@ -186,7 +186,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity");
 
-	plen = 2 + 4 * 4 + 1 + sm->server_id_len;
+	plen = 2 + 4 * 4 + 1 + sm->cfg->server_id_len;
 	msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID);
 	if (msg == NULL)
 		return NULL;
@@ -223,7 +223,7 @@
 
 	/* Server IDType + Identity */
 	wpabuf_put_u8(msg, data->serverid_type);
-	wpabuf_put_data(msg, sm->server_id, sm->server_id_len);
+	wpabuf_put_data(msg, sm->cfg->server_id, sm->cfg->server_id_len);
 
 	wpabuf_free(data->msgs);
 	data->msgs = wpabuf_dup(msg);
@@ -252,7 +252,7 @@
 
 	if (eap_eke_derive_key(&data->sess, sm->user->password,
 			       sm->user->password_len,
-			       sm->server_id, sm->server_id_len,
+			       sm->cfg->server_id, sm->cfg->server_id_len,
 			       data->peerid, data->peerid_len, data->key) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
 		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
@@ -338,7 +338,7 @@
 	wpabuf_put(msg, prot_len);
 
 	if (eap_eke_derive_ka(&data->sess,
-			      sm->server_id, sm->server_id_len,
+			      sm->cfg->server_id, sm->cfg->server_id_len,
 			      data->peerid, data->peerid_len,
 			      data->nonce_p, data->nonce_s) < 0) {
 		wpabuf_free(msg);
@@ -552,7 +552,7 @@
 	}
 
 	if (eap_eke_derive_ke_ki(&data->sess,
-				 sm->server_id, sm->server_id_len,
+				 sm->cfg->server_id, sm->cfg->server_id_len,
 				 data->peerid, data->peerid_len) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
 		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
@@ -641,7 +641,8 @@
 		return;
 	}
 
-	if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len,
+	if (eap_eke_derive_msk(&data->sess, sm->cfg->server_id,
+			       sm->cfg->server_id_len,
 			       data->peerid, data->peerid_len,
 			       data->nonce_s, data->nonce_p,
 			       data->msk, data->emsk) < 0) {
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index a63f820..0270821 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -108,8 +108,8 @@
 }
 
 
-static EapType eap_fast_req_failure(struct eap_sm *sm,
-				    struct eap_fast_data *data)
+static enum eap_type eap_fast_req_failure(struct eap_sm *sm,
+					  struct eap_fast_data *data)
 {
 	/* TODO: send Result TLV(FAILURE) */
 	eap_fast_state(data, FAILURE);
@@ -278,7 +278,7 @@
 	 * Extra key material after TLS key_block: session_key_seed[40]
 	 */
 
-	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+	sks = eap_fast_derive_key(sm->cfg->ssl_ctx, data->ssl.conn,
 				  EAP_FAST_SKS_LEN);
 	if (sks == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
@@ -304,7 +304,7 @@
 {
 	os_free(data->key_block_p);
 	data->key_block_p = (struct eap_fast_key_block_provisioning *)
-		eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+		eap_fast_derive_key(sm->cfg->ssl_ctx, data->ssl.conn,
 				    sizeof(*data->key_block_p));
 	if (data->key_block_p == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
@@ -440,7 +440,7 @@
 		return NULL;
 	}
 
-	if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+	if (tls_connection_set_cipher_list(sm->cfg->ssl_ctx, data->ssl.conn,
 					   ciphers) < 0) {
 		wpa_printf(MSG_INFO, "EAP-FAST: Failed to set TLS cipher "
 			   "suites");
@@ -448,7 +448,8 @@
 		return NULL;
 	}
 
-	if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+	if (tls_connection_set_session_ticket_cb(sm->cfg->ssl_ctx,
+						 data->ssl.conn,
 						 eap_fast_session_ticket_cb,
 						 data) < 0) {
 		wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
@@ -457,47 +458,48 @@
 		return NULL;
 	}
 
-	if (sm->pac_opaque_encr_key == NULL) {
+	if (sm->cfg->pac_opaque_encr_key == NULL) {
 		wpa_printf(MSG_INFO, "EAP-FAST: No PAC-Opaque encryption key "
 			   "configured");
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key,
+	os_memcpy(data->pac_opaque_encr, sm->cfg->pac_opaque_encr_key,
 		  sizeof(data->pac_opaque_encr));
 
-	if (sm->eap_fast_a_id == NULL) {
+	if (sm->cfg->eap_fast_a_id == NULL) {
 		wpa_printf(MSG_INFO, "EAP-FAST: No A-ID configured");
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	data->srv_id = os_memdup(sm->eap_fast_a_id, sm->eap_fast_a_id_len);
+	data->srv_id = os_memdup(sm->cfg->eap_fast_a_id,
+				 sm->cfg->eap_fast_a_id_len);
 	if (data->srv_id == NULL) {
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	data->srv_id_len = sm->eap_fast_a_id_len;
+	data->srv_id_len = sm->cfg->eap_fast_a_id_len;
 
-	if (sm->eap_fast_a_id_info == NULL) {
+	if (sm->cfg->eap_fast_a_id_info == NULL) {
 		wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured");
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
-	data->srv_id_info = os_strdup(sm->eap_fast_a_id_info);
+	data->srv_id_info = os_strdup(sm->cfg->eap_fast_a_id_info);
 	if (data->srv_id_info == NULL) {
 		eap_fast_reset(sm, data);
 		return NULL;
 	}
 
 	/* PAC-Key lifetime in seconds (hard limit) */
-	data->pac_key_lifetime = sm->pac_key_lifetime;
+	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->pac_key_refresh_time;
+	data->pac_key_refresh_time = sm->cfg->pac_key_refresh_time;
 
 	return data;
 }
@@ -552,8 +554,8 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2");
 
-	if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher))
-	    < 0) {
+	if (tls_get_cipher(sm->cfg->ssl_ctx, data->ssl.conn,
+			   cipher, sizeof(cipher)) < 0) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher "
 			   "information");
 		eap_fast_state(data, FAILURE);
@@ -872,7 +874,8 @@
 	case START:
 		return eap_fast_build_start(sm, data, id);
 	case PHASE1:
-		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+		if (tls_connection_established(sm->cfg->ssl_ctx,
+					       data->ssl.conn)) {
 			if (eap_fast_phase1_done(sm, data) < 0)
 				return NULL;
 			if (data->state == PHASE2_START) {
@@ -943,15 +946,14 @@
 
 
 static int eap_fast_phase2_init(struct eap_sm *sm, struct eap_fast_data *data,
-				EapType eap_type)
+				int vendor, enum eap_type eap_type)
 {
 	if (data->phase2_priv && data->phase2_method) {
 		data->phase2_method->reset(sm, data->phase2_priv);
 		data->phase2_method = NULL;
 		data->phase2_priv = NULL;
 	}
-	data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
-							eap_type);
+	data->phase2_method = eap_server_get_eap_method(vendor, eap_type);
 	if (!data->phase2_method)
 		return -1;
 
@@ -973,7 +975,8 @@
 					     struct eap_fast_data *data,
 					     u8 *in_data, size_t in_len)
 {
-	u8 next_type = EAP_TYPE_NONE;
+	int next_vendor = EAP_VENDOR_IETF;
+	enum eap_type next_type = EAP_TYPE_NONE;
 	struct eap_hdr *hdr;
 	u8 *pos;
 	size_t left;
@@ -999,8 +1002,9 @@
 		    m->method == EAP_TYPE_TNC) {
 			wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required "
 				   "TNC negotiation");
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = eap_fast_req_failure(sm, data);
-			eap_fast_phase2_init(sm, data, next_type);
+			eap_fast_phase2_init(sm, data, next_vendor, next_type);
 			return;
 		}
 #endif /* EAP_SERVER_TNC */
@@ -1008,14 +1012,17 @@
 		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
 		    sm->user->methods[sm->user_eap_method_index].method !=
 		    EAP_TYPE_NONE) {
+			next_vendor = sm->user->methods[
+				sm->user_eap_method_index].vendor;
 			next_type = sm->user->methods[
 				sm->user_eap_method_index++].method;
-			wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d",
-				   next_type);
+			wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %u:%u",
+				   next_vendor, next_type);
 		} else {
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = eap_fast_req_failure(sm, data);
 		}
-		eap_fast_phase2_init(sm, data, next_type);
+		eap_fast_phase2_init(sm, data, next_vendor, next_type);
 		return;
 	}
 
@@ -1035,8 +1042,9 @@
 
 	if (!m->isSuccess(sm, priv)) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed");
+		next_vendor = EAP_VENDOR_IETF;
 		next_type = eap_fast_req_failure(sm, data);
-		eap_fast_phase2_init(sm, data, next_type);
+		eap_fast_phase2_init(sm, data, next_vendor, next_type);
 		return;
 	}
 
@@ -1047,6 +1055,7 @@
 					  "Identity not found in the user "
 					  "database",
 					  sm->identity, sm->identity_len);
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = eap_fast_req_failure(sm, data);
 			break;
 		}
@@ -1057,23 +1066,28 @@
 			 * Only EAP-MSCHAPv2 is allowed for anonymous
 			 * provisioning.
 			 */
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_MSCHAPV2;
 			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;
 		}
-		wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type);
+		wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %u:%u",
+			   next_vendor, next_type);
 		break;
 	case PHASE2_METHOD:
 	case CRYPTO_BINDING:
 		eap_fast_update_icmk(sm, data);
 		eap_fast_state(data, CRYPTO_BINDING);
 		data->eap_seq++;
+		next_vendor = EAP_VENDOR_IETF;
 		next_type = EAP_TYPE_NONE;
 #ifdef EAP_SERVER_TNC
-		if (sm->tnc && !data->tnc_started) {
+		if (sm->cfg->tnc && !data->tnc_started) {
 			wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC");
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_TNC;
 			data->tnc_started = 1;
 		}
@@ -1087,7 +1101,7 @@
 		break;
 	}
 
-	eap_fast_phase2_init(sm, data, next_type);
+	eap_fast_phase2_init(sm, data, next_vendor, next_type);
 }
 
 
@@ -1335,8 +1349,8 @@
 		}
 
 		if (data->anon_provisioning &&
-		    sm->eap_fast_prov != ANON_PROV &&
-		    sm->eap_fast_prov != BOTH_PROV) {
+		    sm->cfg->eap_fast_prov != ANON_PROV &&
+		    sm->cfg->eap_fast_prov != BOTH_PROV) {
 			wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
 				   "use unauthenticated provisioning which is "
 				   "disabled");
@@ -1344,8 +1358,8 @@
 			return;
 		}
 
-		if (sm->eap_fast_prov != AUTH_PROV &&
-		    sm->eap_fast_prov != BOTH_PROV &&
+		if (sm->cfg->eap_fast_prov != AUTH_PROV &&
+		    sm->cfg->eap_fast_prov != BOTH_PROV &&
 		    tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
 		    eap_fast_pac_type(tlv.pac, tlv.pac_len,
 				      PAC_TYPE_TUNNEL_PAC)) {
@@ -1397,7 +1411,7 @@
 		return;
 	}
 
-	in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+	in_decrypted = tls_connection_decrypt(sm->cfg->ssl_ctx, data->ssl.conn,
 					      in_buf);
 	if (in_decrypted == NULL) {
 		wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 "
@@ -1457,7 +1471,7 @@
 		return -1;
 	}
 
-	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	if (!tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) ||
 	    wpabuf_len(data->ssl.tls_out) > 0)
 		return 1;
 
@@ -1474,7 +1488,8 @@
 static int eap_fast_process_phase2_start(struct eap_sm *sm,
 					 struct eap_fast_data *data)
 {
-	u8 next_type;
+	int next_vendor;
+	enum eap_type next_type;
 
 	if (data->identity) {
 		os_free(sm->identity);
@@ -1488,10 +1503,12 @@
 					  "Phase2 Identity not found "
 					  "in the user database",
 					  sm->identity, sm->identity_len);
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = eap_fast_req_failure(sm, data);
 		} else {
 			wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already "
 				   "known - skip Phase 2 Identity Request");
+			next_vendor = sm->user->methods[0].vendor;
 			next_type = sm->user->methods[0].method;
 			sm->user_eap_method_index = 1;
 		}
@@ -1499,10 +1516,11 @@
 		eap_fast_state(data, PHASE2_METHOD);
 	} else {
 		eap_fast_state(data, PHASE2_ID);
+		next_vendor = EAP_VENDOR_IETF;
 		next_type = EAP_TYPE_IDENTITY;
 	}
 
-	return eap_fast_phase2_init(sm, data, next_type);
+	return eap_fast_phase2_init(sm, data, next_vendor, next_type);
 }
 
 
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index fb3d117..a774275 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -117,7 +117,7 @@
 	wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server",
 		    data->rand_server, EAP_GPSK_RAND_LEN);
 
-	len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 +
+	len = 1 + 2 + sm->cfg->server_id_len + EAP_GPSK_RAND_LEN + 2 +
 		data->csuite_count * sizeof(struct eap_gpsk_csuite);
 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
 			    EAP_CODE_REQUEST, id);
@@ -129,8 +129,8 @@
 	}
 
 	wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1);
-	wpabuf_put_be16(req, sm->server_id_len);
-	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+	wpabuf_put_be16(req, sm->cfg->server_id_len);
+	wpabuf_put_data(req, sm->cfg->server_id, sm->cfg->server_id_len);
 	wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
 	wpabuf_put_be16(req,
 			data->csuite_count * sizeof(struct eap_gpsk_csuite));
@@ -152,7 +152,7 @@
 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3");
 
 	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
-	len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len +
+	len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->cfg->server_id_len +
 		sizeof(struct eap_gpsk_csuite) + 2 + miclen;
 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
 			    EAP_CODE_REQUEST, id);
@@ -168,8 +168,8 @@
 
 	wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN);
 	wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
-	wpabuf_put_be16(req, sm->server_id_len);
-	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+	wpabuf_put_be16(req, sm->cfg->server_id_len);
+	wpabuf_put_data(req, sm->cfg->server_id, sm->cfg->server_id_len);
 	csuite = wpabuf_put(req, sizeof(*csuite));
 	WPA_PUT_BE32(csuite->vendor, data->vendor);
 	WPA_PUT_BE16(csuite->specifier, data->specifier);
@@ -181,7 +181,7 @@
 	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
 				 data->specifier, start, pos - start, pos) < 0)
 	{
-		os_free(req);
+		wpabuf_free(req);
 		eap_gpsk_state(data, FAILURE);
 		return NULL;
 	}
@@ -294,8 +294,8 @@
 		eap_gpsk_state(data, FAILURE);
 		return;
 	}
-	if (alen != sm->server_id_len ||
-	    os_memcmp(pos, sm->server_id, alen) != 0) {
+	if (alen != sm->cfg->server_id_len ||
+	    os_memcmp(pos, sm->cfg->server_id, alen) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and "
 			   "GPSK-2 did not match");
 		eap_gpsk_state(data, FAILURE);
@@ -379,7 +379,7 @@
 	data->specifier = WPA_GET_BE16(csuite->specifier);
 	wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
 		   data->vendor, data->specifier);
-	pos += sizeof(*csuite);	
+	pos += sizeof(*csuite);
 
 	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
@@ -409,7 +409,7 @@
 				 data->vendor, data->specifier,
 				 data->rand_peer, data->rand_server,
 				 data->id_peer, data->id_peer_len,
-				 sm->server_id, sm->server_id_len,
+				 sm->cfg->server_id, sm->cfg->server_id_len,
 				 data->msk, data->emsk,
 				 data->sk, &data->sk_len,
 				 data->pk, &data->pk_len) < 0) {
@@ -423,7 +423,8 @@
 				       data->vendor, data->specifier,
 				       data->rand_peer, data->rand_server,
 				       data->id_peer, data->id_peer_len,
-				       sm->server_id, sm->server_id_len,
+				       sm->cfg->server_id,
+				       sm->cfg->server_id_len,
 				       EAP_TYPE_GPSK,
 				       data->session_id, &data->id_len) < 0) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 32e6872..897637e 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -87,8 +87,8 @@
 	if (data == NULL)
 		return NULL;
 	data->state = MSG;
-	data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
-		IKEV2_FRAGMENT_SIZE;
+	data->fragment_size = sm->cfg->fragment_size > 0 ?
+		sm->cfg->fragment_size : IKEV2_FRAGMENT_SIZE;
 	data->ikev2.state = SA_INIT;
 	data->ikev2.peer_auth = PEER_AUTH_SECRET;
 	data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
@@ -103,10 +103,10 @@
 	data->ikev2.proposal.encr = ENCR_AES_CBC;
 	data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
 
-	data->ikev2.IDi = os_memdup(sm->server_id, sm->server_id_len);
+	data->ikev2.IDi = os_memdup(sm->cfg->server_id, sm->cfg->server_id_len);
 	if (data->ikev2.IDi == NULL)
 		goto failed;
-	data->ikev2.IDi_len = sm->server_id_len;
+	data->ikev2.IDi_len = sm->cfg->server_id_len;
 
 	data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
 	data->ikev2.cb_ctx = sm;
@@ -414,7 +414,7 @@
 		eap_ikev2_state(data, FAIL);
 		return;
 	}
-		
+
 	if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
 		if (eap_ikev2_process_fragment(data, flags, message_length,
 					       pos, end - pos) < 0)
diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c
index 79ed344..f37c9c3 100644
--- a/src/eap_server/eap_server_methods.c
+++ b/src/eap_server/eap_server_methods.c
@@ -22,7 +22,8 @@
  * @method: EAP type number
  * Returns: Pointer to EAP method or %NULL if not found
  */
-const struct eap_method * eap_server_get_eap_method(int vendor, EapType method)
+const struct eap_method * eap_server_get_eap_method(int vendor,
+						    enum eap_type method)
 {
 	struct eap_method *m;
 	for (m = eap_methods; m; m = m->next) {
@@ -42,7 +43,7 @@
  * This function maps EAP type names into EAP type numbers based on the list of
  * EAP methods included in the build.
  */
-EapType eap_server_get_type(const char *name, int *vendor)
+enum eap_type eap_server_get_type(const char *name, int *vendor)
 {
 	struct eap_method *m;
 	for (m = eap_methods; m; m = m->next) {
@@ -69,7 +70,8 @@
  * is not needed anymore.
  */
 struct eap_method * eap_server_method_alloc(int version, int vendor,
-					    EapType method, const char *name)
+					    enum eap_type method,
+					    const char *name)
 {
 	struct eap_method *eap;
 	eap = os_zalloc(sizeof(*eap));
@@ -163,7 +165,7 @@
  * This function maps EAP type numbers into EAP type names based on the list of
  * EAP methods included in the build.
  */
-const char * eap_server_get_name(int vendor, EapType type)
+const char * eap_server_get_name(int vendor, enum eap_type type)
 {
 	struct eap_method *m;
 	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index e9e03b0..8a1621a 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -109,7 +109,7 @@
 		return NULL;
 	}
 
-	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
+	ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->cfg->server_id_len;
 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
 			    EAP_CODE_REQUEST, id);
 	if (req == NULL) {
@@ -131,7 +131,7 @@
 		wpabuf_put(req, CHALLENGE_LEN);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
 		    data->auth_challenge, CHALLENGE_LEN);
-	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+	wpabuf_put_data(req, sm->cfg->server_id, sm->cfg->server_id_len);
 
 	return req;
 }
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 3257789..5ed29ef 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -107,9 +107,14 @@
 		    data->rand.r.x, EAP_PAX_RAND_LEN);
 
 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
-	eap_pax_mac(data->mac_id, (u8 *) "", 0,
-		    wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
-		    NULL, 0, NULL, 0, pos);
+	if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
+			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+			NULL, 0, NULL, 0, pos) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
+		data->state = FAILURE;
+		wpabuf_free(req);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
 
 	return req;
@@ -144,18 +149,28 @@
 
 	wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
-	eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
-		    data->rand.r.y, EAP_PAX_RAND_LEN,
-		    (u8 *) data->cid, data->cid_len, NULL, 0, pos);
+	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+			data->rand.r.y, EAP_PAX_RAND_LEN,
+			(u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
+		data->state = FAILURE;
+		wpabuf_free(req);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
 		    pos, EAP_PAX_MAC_LEN);
 
 	/* Optional ADE could be added here, if needed */
 
 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
-	eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
-		    wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
-		    NULL, 0, NULL, 0, pos);
+	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+			NULL, 0, NULL, 0, pos) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
+		data->state = FAILURE;
+		wpabuf_free(req);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
 
 	return req;
@@ -190,7 +205,7 @@
 	u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
-	if (pos == NULL || len < sizeof(*resp)) {
+	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
 		return TRUE;
 	}
@@ -264,10 +279,15 @@
 		}
 		icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
-		eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
-			    wpabuf_mhead(respData),
-			    wpabuf_len(respData) - EAP_PAX_ICV_LEN,
-			    NULL, 0, NULL, 0, icvbuf);
+		if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+				wpabuf_mhead(respData),
+				wpabuf_len(respData) - EAP_PAX_ICV_LEN,
+				NULL, 0, NULL, 0, icvbuf) < 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-PAX: Failed to calculate ICV");
+			return TRUE;
+		}
+
 		if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
 			wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
 			wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
@@ -395,10 +415,15 @@
 	}
 	data->keys_set = 1;
 
-	eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
-		    data->rand.r.x, EAP_PAX_RAND_LEN,
-		    data->rand.r.y, EAP_PAX_RAND_LEN,
-		    (u8 *) data->cid, data->cid_len, mac);
+	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+			data->rand.r.x, EAP_PAX_RAND_LEN,
+			data->rand.r.y, EAP_PAX_RAND_LEN,
+			(u8 *) data->cid, data->cid_len, mac) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK");
+		data->state = FAILURE;
+		return;
+	}
+
 	if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
 			   "PAX_STD-2");
@@ -417,10 +442,14 @@
 		return;
 	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
-	eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
-		    wpabuf_head(respData),
-		    wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
-		    icvbuf);
+	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+			wpabuf_head(respData),
+			wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0,
+			NULL, 0, icvbuf) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV");
+		return;
+	}
+
 	if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 92c0e5e..02d8b8e 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -105,8 +105,8 @@
 {
 	struct wpabuf *buf;
 
-	if (!sm->tls_session_lifetime ||
-	    tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+	if (!sm->cfg->tls_session_lifetime ||
+	    tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn))
 		return;
 
 	buf = wpabuf_alloc(1 + 1 + sm->identity_len);
@@ -336,7 +336,7 @@
 		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
 
-	if (tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+	if (tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn)) {
 		/* Fast-connect: IPMK|CMK = TK */
 		os_memcpy(data->ipmk, tk, 40);
 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
@@ -362,7 +362,7 @@
 	res = peap_prfplus(data->peap_version, tk, 40,
 			   "Inner Methods Compound Keys",
 			   isk, sizeof(isk), imck, sizeof(imck));
-	os_memset(isk, 0, sizeof(isk));
+	forced_memzero(isk, sizeof(isk));
 	if (res < 0) {
 		os_free(tk);
 		return -1;
@@ -376,7 +376,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
 	os_memcpy(data->cmk, imck + 40, 20);
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
-	os_memset(imck, 0, sizeof(imck));
+	forced_memzero(imck, sizeof(imck));
 
 	return 0;
 }
@@ -521,7 +521,8 @@
 		return eap_peap_build_start(sm, data, id);
 	case PHASE1:
 	case PHASE1_ID2:
-		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+		if (tls_connection_established(sm->cfg->ssl_ctx,
+					       data->ssl.conn)) {
 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, "
 				   "starting Phase2");
 			eap_peap_state(data, PHASE2_START);
@@ -585,7 +586,7 @@
 
 
 static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
-				int vendor, EapType eap_type)
+				int vendor, enum eap_type eap_type)
 {
 	if (data->phase2_priv && data->phase2_method) {
 		data->phase2_method->reset(sm, data->phase2_priv);
@@ -1020,7 +1021,7 @@
 		}
 
 #ifdef EAP_SERVER_TNC
-		if (data->state != PHASE2_SOH && sm->tnc &&
+		if (data->state != PHASE2_SOH && sm->cfg->tnc &&
 		    data->peap_version == 0) {
 			eap_peap_state(data, PHASE2_SOH);
 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize "
@@ -1077,7 +1078,7 @@
 		return;
 	}
 
-	in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+	in_decrypted = tls_connection_decrypt(sm->cfg->ssl_ctx, data->ssl.conn,
 					      in_buf);
 	if (in_decrypted == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 "
@@ -1237,8 +1238,8 @@
 	}
 
 	if (data->state == SUCCESS ||
-	    !tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
-	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+	    !tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn))
 		return;
 
 	buf = tls_connection_get_success_data(data->ssl.conn);
@@ -1326,7 +1327,7 @@
 				   "key");
 		}
 
-		os_memset(csk, 0, sizeof(csk));
+		forced_memzero(csk, sizeof(csk));
 
 		return eapKeyData;
 	}
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index 0eab893..511973c 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -68,7 +68,7 @@
 		    data->rand_s, EAP_PSK_RAND_LEN);
 
 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
-			    sizeof(*psk) + sm->server_id_len,
+			    sizeof(*psk) + sm->cfg->server_id_len,
 			    EAP_CODE_REQUEST, id);
 	if (req == NULL) {
 		wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
@@ -80,7 +80,7 @@
 	psk = wpabuf_put(req, sizeof(*psk));
 	psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */
 	os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
-	wpabuf_put_data(req, sm->server_id, sm->server_id_len);
+	wpabuf_put_data(req, sm->cfg->server_id, sm->cfg->server_id_len);
 
 	return req;
 }
@@ -110,13 +110,13 @@
 	os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
 
 	/* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
-	buflen = sm->server_id_len + EAP_PSK_RAND_LEN;
+	buflen = sm->cfg->server_id_len + EAP_PSK_RAND_LEN;
 	buf = os_malloc(buflen);
 	if (buf == NULL)
 		goto fail;
 
-	os_memcpy(buf, sm->server_id, sm->server_id_len);
-	os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN);
+	os_memcpy(buf, sm->cfg->server_id, sm->cfg->server_id_len);
+	os_memcpy(buf + sm->cfg->server_id_len, data->rand_p, EAP_PSK_RAND_LEN);
 	if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) {
 		os_free(buf);
 		goto fail;
@@ -293,7 +293,7 @@
 	os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN);
 
 	/* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
-	buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN;
+	buflen = data->id_p_len + sm->cfg->server_id_len + 2 * EAP_PSK_RAND_LEN;
 	buf = os_malloc(buflen);
 	if (buf == NULL) {
 		data->state = FAILURE;
@@ -301,8 +301,8 @@
 	}
 	os_memcpy(buf, data->id_p, data->id_p_len);
 	pos = buf + data->id_p_len;
-	os_memcpy(pos, sm->server_id, sm->server_id_len);
-	pos += sm->server_id_len;
+	os_memcpy(pos, sm->cfg->server_id, sm->cfg->server_id_len);
+	pos += sm->cfg->server_id_len;
 	os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN);
 	pos += EAP_PSK_RAND_LEN;
 	os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index e720a28..6bf3a23 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -97,7 +97,7 @@
 	if (data == NULL)
 		return NULL;
 
-	data->group_num = sm->pwd_group;
+	data->group_num = sm->cfg->pwd_group;
 	wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
 		   data->group_num);
 	data->state = PWD_ID_Req;
@@ -134,7 +134,7 @@
 	data->in_frag_pos = data->out_frag_pos = 0;
 	data->inbuf = data->outbuf = NULL;
 	/* use default MTU from RFC 5931 if not configured otherwise */
-	data->mtu = sm->fragment_size > 0 ? sm->fragment_size : 1020;
+	data->mtu = sm->cfg->fragment_size > 0 ? sm->cfg->fragment_size : 1020;
 
 	return data;
 }
@@ -632,7 +632,7 @@
 				       data->id_server, data->id_server_len,
 				       data->id_peer, data->id_peer_len,
 				       (u8 *) &data->token);
-	os_memset(pwhashhash, 0, sizeof(pwhashhash));
+	forced_memzero(pwhashhash, sizeof(pwhashhash));
 	if (res) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
 			   "PWE");
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 66183f5..56cfbfb 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -123,7 +123,7 @@
 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
 
 	plen = 4;
-	plen += 2 + sm->server_id_len;
+	plen += 2 + sm->cfg->server_id_len;
 	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
 	if (msg == NULL) {
 		data->state = FAILURE;
@@ -135,7 +135,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
 	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
-			  sm->server_id, sm->server_id_len);
+			  sm->cfg->server_id, sm->cfg->server_id_len);
 
 	return msg;
 }
@@ -158,7 +158,7 @@
 	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
 		    data->rand_s, EAP_SAKE_RAND_LEN);
 
-	plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len;
+	plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->cfg->server_id_len;
 	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
 	if (msg == NULL) {
 		data->state = FAILURE;
@@ -171,7 +171,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
 	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
-			  sm->server_id, sm->server_id_len);
+			  sm->cfg->server_id, sm->cfg->server_id_len);
 
 	return msg;
 }
@@ -198,13 +198,13 @@
 	wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
 	mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
 	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-				 sm->server_id, sm->server_id_len,
+				 sm->cfg->server_id, sm->cfg->server_id_len,
 				 data->peerid, data->peerid_len, 0,
 				 wpabuf_head(msg), wpabuf_len(msg), mic, mic))
 	{
 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
 		data->state = FAILURE;
-		os_free(msg);
+		wpabuf_free(msg);
 		return NULL;
 	}
 
@@ -340,16 +340,25 @@
 		data->state = FAILURE;
 		return;
 	}
-	eap_sake_derive_keys(sm->user->password,
-			     sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
-			     data->rand_s, data->rand_p,
-			     (u8 *) &data->tek, data->msk, data->emsk);
+	if (eap_sake_derive_keys(sm->user->password,
+				 sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
+				 data->rand_s, data->rand_p,
+				 (u8 *) &data->tek, data->msk,
+				 data->emsk) < 0) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
+		data->state = FAILURE;
+		return;
+	}
 
-	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-			     sm->server_id, sm->server_id_len,
-			     data->peerid, data->peerid_len, 1,
-			     wpabuf_head(respData), wpabuf_len(respData),
-			     attr.mic_p, mic_p);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 sm->cfg->server_id, sm->cfg->server_id_len,
+				 data->peerid, data->peerid_len, 1,
+				 wpabuf_head(respData), wpabuf_len(respData),
+				 attr.mic_p, mic_p) < 0) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		data->state = FAILURE;
+		return;
+	}
 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
 		eap_sake_state(data, FAILURE);
@@ -382,11 +391,14 @@
 		return;
 	}
 
-	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-			     sm->server_id, sm->server_id_len,
-			     data->peerid, data->peerid_len, 1,
-			     wpabuf_head(respData), wpabuf_len(respData),
-			     attr.mic_p, mic_p);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 sm->cfg->server_id, sm->cfg->server_id_len,
+				 data->peerid, data->peerid_len, 1,
+				 wpabuf_head(respData), wpabuf_len(respData),
+				 attr.mic_p, mic_p) < 0) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		return;
+	}
 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
 		eap_sake_state(data, FAILURE);
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 1287827..d7ac87c 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -26,6 +26,7 @@
 	u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
 	u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
 	u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+	u8 reauth_mac[EAP_SIM_MAC_LEN];
 	int num_chal;
 	enum {
 		START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
@@ -75,7 +76,7 @@
 {
 	struct eap_sim_data *data;
 
-	if (sm->eap_sim_db_priv == NULL) {
+	if (!sm->cfg->eap_sim_db_priv) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
 		return NULL;
 	}
@@ -149,18 +150,24 @@
 			      const u8 *nonce_s)
 {
 	os_free(data->next_pseudonym);
-	if (nonce_s == NULL) {
+	if (!(sm->cfg->eap_sim_id & 0x01)) {
+		/* Use of pseudonyms disabled in configuration */
+		data->next_pseudonym = NULL;
+	} else if (!nonce_s) {
 		data->next_pseudonym =
-			eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv,
+			eap_sim_db_get_next_pseudonym(sm->cfg->eap_sim_db_priv,
 						      EAP_SIM_DB_SIM);
 	} else {
 		/* Do not update pseudonym during re-authentication */
 		data->next_pseudonym = NULL;
 	}
 	os_free(data->next_reauth_id);
-	if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
+	if (!(sm->cfg->eap_sim_id & 0x02)) {
+		/* Use of fast reauth disabled in configuration */
+		data->next_reauth_id = NULL;
+	} else if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
 		data->next_reauth_id =
-			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv,
+			eap_sim_db_get_next_reauth_id(sm->cfg->eap_sim_db_priv,
 						      EAP_SIM_DB_SIM);
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
@@ -233,7 +240,7 @@
 		return NULL;
 	}
 
-	if (sm->eap_sim_aka_result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind) {
 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 	}
@@ -249,6 +256,7 @@
 					    struct eap_sim_data *data, u8 id)
 {
 	struct eap_sim_msg *msg;
+	struct wpabuf *buf;
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
 
@@ -271,14 +279,23 @@
 		return NULL;
 	}
 
-	if (sm->eap_sim_aka_result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind) {
 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 	}
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
+	buf = eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
+
+	/* Remember this MAC before sending it to the peer. This MAC is used for
+	 * Session-Id calculation after receiving response from the peer and
+	 * after all other checks pass. */
+	os_memcpy(data->reauth_mac,
+		  wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN,
+		  EAP_SIM_MAC_LEN);
+
+	return buf;
 }
 
 
@@ -458,7 +475,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'",
 			   username);
 		data->reauth = eap_sim_db_get_reauth_entry(
-			sm->eap_sim_db_priv, username);
+			sm->cfg->eap_sim_db_priv, username);
 		os_free(username);
 		if (data->reauth == NULL) {
 			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth "
@@ -480,7 +497,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'",
 			   username);
 		permanent = eap_sim_db_get_permanent(
-			sm->eap_sim_db_priv, username);
+			sm->cfg->eap_sim_db_priv, username);
 		os_free(username);
 		if (permanent == NULL) {
 			wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym "
@@ -521,7 +538,7 @@
 	data->reauth = NULL;
 
 	data->num_chal = eap_sim_db_get_gsm_triplets(
-		sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
+		sm->cfg->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
 		(u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
 	if (data->num_chal == EAP_SIM_DB_PENDING) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
@@ -582,7 +599,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
 		   "correct AT_MAC");
-	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
 		data->use_result_ind = 1;
 		data->notification = EAP_SIM_SUCCESS;
 		eap_sim_state(data, NOTIFICATION);
@@ -590,12 +607,13 @@
 		eap_sim_state(data, SUCCESS);
 
 	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
+		eap_sim_db_add_pseudonym(sm->cfg->eap_sim_db_priv,
+					 data->permanent,
 					 data->next_pseudonym);
 		data->next_pseudonym = NULL;
 	}
 	if (data->next_reauth_id) {
-		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
+		eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv, data->permanent,
 				      data->next_reauth_id, data->counter + 1,
 				      data->mk);
 		data->next_reauth_id = NULL;
@@ -655,7 +673,7 @@
 		return;
 	}
 
-	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+	if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
 		data->use_result_ind = 1;
 		data->notification = EAP_SIM_SUCCESS;
 		eap_sim_state(data, NOTIFICATION);
@@ -663,12 +681,13 @@
 		eap_sim_state(data, SUCCESS);
 
 	if (data->next_reauth_id) {
-		eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
+		eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv, data->permanent,
 				      data->next_reauth_id,
 				      data->counter + 1, data->mk);
 		data->next_reauth_id = NULL;
 	} else {
-		eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+		eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv,
+					 data->reauth);
 		data->reauth = NULL;
 	}
 
@@ -677,7 +696,7 @@
 fail:
 	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
 	eap_sim_state(data, NOTIFICATION);
-	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+	eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv, data->reauth);
 	data->reauth = NULL;
 	os_free(decrypted);
 }
@@ -829,15 +848,25 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	if (!data->reauth)
+		*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	else
+		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
 	id = os_malloc(*len);
 	if (id == NULL)
 		return NULL;
 
 	id[0] = EAP_TYPE_SIM;
-	os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
-	os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
-		  EAP_SIM_NONCE_MT_LEN);
+	if (!data->reauth) {
+		os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+		os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN,
+			  data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+	} else {
+		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
+		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
+			  EAP_SIM_MAC_LEN);
+
+	}
 	wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
 
 	return id;
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
new file mode 100644
index 0000000..a2cbf7a
--- /dev/null
+++ b/src/eap_server/eap_server_teap.c
@@ -0,0 +1,2092 @@
+/*
+ * EAP-TEAP server (RFC 7170)
+ * 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 "crypto/aes_wrap.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "eap_common/eap_teap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+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,
+		FAILURE_SEND_RESULT, SUCCESS_SEND_RESULT, SUCCESS, FAILURE
+	} state;
+
+	u8 teap_version;
+	u8 peer_version;
+	u16 tls_cs;
+
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+
+	u8 crypto_binding_nonce[32];
+	int final_result;
+
+	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;
+
+	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 */
+	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;
+};
+
+
+static int eap_teap_process_phase2_start(struct eap_sm *sm,
+					 struct eap_teap_data *data);
+
+
+static const char * eap_teap_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case PHASE1:
+		return "PHASE1";
+	case PHASE1B:
+		return "PHASE1B";
+	case PHASE2_START:
+		return "PHASE2_START";
+	case PHASE2_ID:
+		return "PHASE2_ID";
+	case PHASE2_BASIC_AUTH:
+		return "PHASE2_BASIC_AUTH";
+	case PHASE2_METHOD:
+		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:
+		return "SUCCESS_SEND_RESULT";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_teap_state(struct eap_teap_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: %s -> %s",
+		   eap_teap_state_txt(data->state),
+		   eap_teap_state_txt(state));
+	data->state = state;
+}
+
+
+static enum eap_type eap_teap_req_failure(struct eap_teap_data *data,
+					  enum teap_error_codes error)
+{
+	eap_teap_state(data, FAILURE_SEND_RESULT);
+	return EAP_TYPE_NONE;
+}
+
+
+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)
+{
+	int res;
+
+	/* 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);
+	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_idx = 0;
+	return 0;
+}
+
+
+static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data)
+{
+	u8 *msk = NULL, *emsk = NULL;
+	size_t msk_len = 0, emsk_len = 0;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Deriving ICMK[%d] (S-IMCK and CMK)",
+		   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);
+
+	if (!data->phase2_method || !data->phase2_priv) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
+		return -1;
+	}
+
+	if (data->phase2_method->getKey) {
+		msk = data->phase2_method->getKey(sm, data->phase2_priv,
+						  &msk_len);
+		if (!msk) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Could not fetch Phase 2 MSK");
+			return -1;
+		}
+	}
+
+	if (data->phase2_method->get_emsk) {
+		emsk = data->phase2_method->get_emsk(sm, data->phase2_priv,
+						     &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, data->cmk_msk,
+				   data->simck_emsk, data->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;
+	}
+	return 0;
+}
+
+
+static void * eap_teap_init(struct eap_sm *sm)
+{
+	struct eap_teap_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (!data)
+		return NULL;
+	data->teap_version = EAP_TEAP_VERSION;
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TEAP)) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL.");
+		eap_teap_reset(sm, data);
+		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);
+		return NULL;
+	}
+	data->srv_id = os_malloc(sm->cfg->eap_fast_a_id_len);
+	if (!data->srv_id) {
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+	os_memcpy(data->srv_id, sm->cfg->eap_fast_a_id,
+		  sm->cfg->eap_fast_a_id_len);
+	data->srv_id_len = sm->cfg->eap_fast_a_id_len;
+
+	if (!sm->cfg->eap_fast_a_id_info) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID-Info configured");
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+	data->srv_id_info = os_strdup(sm->cfg->eap_fast_a_id_info);
+	if (!data->srv_id_info) {
+		eap_teap_reset(sm, data);
+		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;
+}
+
+
+static void eap_teap_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	if (!data)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->reset(sm, data->phase2_priv);
+	eap_server_tls_ssl_deinit(sm, &data->ssl);
+	os_free(data->srv_id);
+	os_free(data->srv_id_info);
+	wpabuf_free(data->pending_phase2_resp);
+	wpabuf_free(data->server_outer_tlvs);
+	wpabuf_free(data->peer_outer_tlvs);
+	os_free(data->identity);
+	forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	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));
+}
+
+
+static struct wpabuf * eap_teap_build_start(struct eap_sm *sm,
+					    struct eap_teap_data *data, u8 id)
+{
+	struct wpabuf *req;
+	size_t outer_tlv_len = sizeof(struct teap_tlv_hdr) + data->srv_id_len;
+	const u8 *start, *end;
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TEAP,
+			    1 + 4 + outer_tlv_len, EAP_CODE_REQUEST, id);
+	if (!req) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TEAP: Failed to allocate memory for request");
+		eap_teap_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_TLS_FLAGS_START | EAP_TEAP_FLAGS_OUTER_TLV_LEN |
+		      data->teap_version);
+	wpabuf_put_be32(req, outer_tlv_len);
+
+	start = wpabuf_put(req, 0);
+
+	/* RFC 7170, Section 4.2.2: Authority-ID TLV */
+	eap_teap_put_tlv(req, TEAP_TLV_AUTHORITY_ID,
+			 data->srv_id, data->srv_id_len);
+
+	end = wpabuf_put(req, 0);
+	wpabuf_free(data->server_outer_tlvs);
+	data->server_outer_tlvs = wpabuf_alloc_copy(start, end - start);
+	if (!data->server_outer_tlvs) {
+		eap_teap_state(data, FAILURE);
+		return NULL;
+	}
+
+	eap_teap_state(data, PHASE1);
+
+	return req;
+}
+
+
+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");
+
+	data->tls_cs = tls_connection_get_cipher_suite(data->ssl.conn);
+	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;
+	}
+
+	eap_teap_state(data, PHASE2_START);
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_teap_build_phase2_req(struct eap_sm *sm,
+						 struct eap_teap_data *data,
+						 u8 id)
+{
+	struct wpabuf *req, *id_tlv = NULL;
+
+	if (sm->cfg->eap_teap_auth == 1 ||
+	    (data->phase2_priv && data->phase2_method &&
+	     data->phase2_method->vendor == EAP_VENDOR_IETF &&
+	     data->phase2_method->method == EAP_TYPE_IDENTITY)) {
+		switch (sm->cfg->eap_teap_id) {
+		case EAP_TEAP_ID_ALLOW_ANY:
+			break;
+		case EAP_TEAP_ID_REQUIRE_USER:
+		case EAP_TEAP_ID_REQUEST_USER_ACCEPT_MACHINE:
+			data->cur_id_type = TEAP_IDENTITY_TYPE_USER;
+			id_tlv = eap_teap_tlv_identity_type(data->cur_id_type);
+			break;
+		case EAP_TEAP_ID_REQUIRE_MACHINE:
+		case EAP_TEAP_ID_REQUEST_MACHINE_ACCEPT_USER:
+			data->cur_id_type = TEAP_IDENTITY_TYPE_MACHINE;
+			id_tlv = eap_teap_tlv_identity_type(data->cur_id_type);
+			break;
+		case EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE:
+			if (data->cur_id_type == TEAP_IDENTITY_TYPE_USER)
+				data->cur_id_type = TEAP_IDENTITY_TYPE_MACHINE;
+			else
+				data->cur_id_type = TEAP_IDENTITY_TYPE_USER;
+			id_tlv = eap_teap_tlv_identity_type(data->cur_id_type);
+			break;
+		}
+	}
+
+	if (sm->cfg->eap_teap_auth == 1) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate Basic-Password-Auth");
+		data->basic_auth_not_done = 1;
+		req = wpabuf_alloc(sizeof(struct teap_tlv_hdr));
+		if (!req) {
+			wpabuf_free(id_tlv);
+			return NULL;
+		}
+		eap_teap_put_tlv_hdr(req, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ, 0);
+		return wpabuf_concat(req, id_tlv);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate inner EAP method");
+	data->inner_eap_not_done = 1;
+	if (!data->phase2_priv) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Phase 2 method not initialized");
+		wpabuf_free(id_tlv);
+		return NULL;
+	}
+
+	req = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+	if (!req) {
+		wpabuf_free(id_tlv);
+		return NULL;
+	}
+
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-TEAP: Phase 2 EAP-Request", req);
+
+	return wpabuf_concat(eap_teap_tlv_eap_payload(req), id_tlv);
+}
+
+
+static struct wpabuf * eap_teap_build_crypto_binding(
+	struct eap_sm *sm, struct eap_teap_data *data)
+{
+	struct wpabuf *buf;
+	struct teap_tlv_result *result;
+	struct teap_tlv_crypto_binding *cb;
+	u8 subtype, flags;
+
+	buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*cb));
+	if (!buf)
+		return NULL;
+
+	if (data->send_new_pac || data->anon_provisioning ||
+	    data->basic_auth_not_done || data->inner_eap_not_done ||
+	    data->phase2_method || sm->cfg->eap_teap_separate_result)
+		data->final_result = 0;
+	else
+		data->final_result = 1;
+
+	if (!data->final_result || data->eap_seq > 0 ||
+	    sm->cfg->eap_teap_auth == 1) {
+		/* Intermediate-Result */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Add Intermediate-Result TLV (status=SUCCESS)");
+		result = wpabuf_put(buf, sizeof(*result));
+		result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+						TEAP_TLV_INTERMEDIATE_RESULT);
+		result->length = host_to_be16(2);
+		result->status = host_to_be16(TEAP_STATUS_SUCCESS);
+	}
+
+	if (data->final_result) {
+		/* Result TLV */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Add Result TLV (status=SUCCESS)");
+		result = wpabuf_put(buf, sizeof(*result));
+		result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+						TEAP_TLV_RESULT);
+		result->length = host_to_be16(2);
+		result->status = host_to_be16(TEAP_STATUS_SUCCESS);
+	}
+
+	/* Crypto-Binding TLV */
+	cb = wpabuf_put(buf, sizeof(*cb));
+	cb->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+				    TEAP_TLV_CRYPTO_BINDING);
+	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;
+	subtype = TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST;
+	cb->subtype = (flags << 4) | subtype;
+	if (random_get_bytes(cb->nonce, sizeof(cb->nonce)) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	/*
+	 * RFC 7170, Section 4.2.13:
+	 * The nonce in a request MUST have its least significant bit set to 0.
+	 */
+	cb->nonce[sizeof(cb->nonce) - 1] &= ~0x01;
+
+	os_memcpy(data->crypto_binding_nonce, cb->nonce, sizeof(cb->nonce));
+
+	if (eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs,
+				  data->peer_outer_tlvs, data->cmk_msk,
+				  cb->msk_compound_mac) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (data->cmk_emsk_available &&
+	    eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs,
+				  data->peer_outer_tlvs, data->cmk_emsk,
+				  cb->emsk_compound_mac) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Add Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+		   cb->version, cb->received_version, flags, subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+		    cb->nonce, sizeof(cb->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+		    cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+		    cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+	return buf;
+}
+
+
+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)
+{
+	struct wpabuf *encr;
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 TLVs",
+			    plain);
+	encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
+	wpabuf_free(plain);
+
+	if (!encr)
+		return -1;
+
+	if (data->ssl.tls_out && piggyback) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Piggyback Phase 2 data (len=%d) with last Phase 1 Message (len=%d used=%d)",
+			   (int) wpabuf_len(encr),
+			   (int) wpabuf_len(data->ssl.tls_out),
+			   (int) data->ssl.tls_out_pos);
+		if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
+			wpa_printf(MSG_WARNING,
+				   "EAP-TEAP: Failed to resize output buffer");
+			wpabuf_free(encr);
+			return -1;
+		}
+		wpabuf_put_buf(data->ssl.tls_out, encr);
+		wpabuf_free(encr);
+	} else {
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = encr;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_teap_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_teap_data *data = priv;
+	struct wpabuf *req = NULL;
+	int piggyback = 0;
+
+	if (data->ssl.state == FRAG_ACK) {
+		return eap_server_tls_build_ack(id, EAP_TYPE_TEAP,
+						data->teap_version);
+	}
+
+	if (data->ssl.state == WAIT_FRAG_ACK) {
+		return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP,
+						data->teap_version, id);
+	}
+
+	switch (data->state) {
+	case START:
+		return eap_teap_build_start(sm, data, id);
+	case PHASE1B:
+		if (tls_connection_established(sm->cfg->ssl_ctx,
+					       data->ssl.conn)) {
+			if (eap_teap_phase1_done(sm, data) < 0)
+				return NULL;
+			if (data->state == PHASE2_START) {
+				int res;
+
+				/*
+				 * Try to generate Phase 2 data to piggyback
+				 * with the end of Phase 1 to avoid extra
+				 * roundtrip.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: Try to start Phase 2");
+				res = eap_teap_process_phase2_start(sm, data);
+				if (res == 1) {
+					req = eap_teap_build_crypto_binding(
+						sm, data);
+					piggyback = 1;
+					break;
+				}
+
+				if (res)
+					break;
+				req = eap_teap_build_phase2_req(sm, data, id);
+				piggyback = 1;
+			}
+		}
+		break;
+	case PHASE2_ID:
+	case PHASE2_BASIC_AUTH:
+	case PHASE2_METHOD:
+		req = eap_teap_build_phase2_req(sm, data, id);
+		break;
+	case CRYPTO_BINDING:
+		req = eap_teap_build_crypto_binding(sm, data);
+		if (data->phase2_method) {
+			/*
+			 * Include the start of the next EAP method in the
+			 * sequence in the same message with Crypto-Binding to
+			 * save a round-trip.
+			 */
+			struct wpabuf *eap;
+
+			eap = eap_teap_build_phase2_req(sm, data, id);
+			req = wpabuf_concat(req, eap);
+			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)
+			req = wpabuf_concat(
+				req, eap_teap_tlv_error(data->error_code));
+		break;
+	case SUCCESS_SEND_RESULT:
+		req = eap_teap_tlv_result(TEAP_STATUS_SUCCESS, 0);
+		data->final_result = 1;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d",
+			   __func__, data->state);
+		return NULL;
+	}
+
+	if (req && eap_teap_encrypt_phase2(sm, data, req, piggyback) < 0)
+		return NULL;
+
+	return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP,
+					data->teap_version, id);
+}
+
+
+static Boolean eap_teap_check(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len);
+	if (!pos || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data,
+				int vendor, enum eap_type eap_type)
+{
+	if (data->phase2_priv && data->phase2_method) {
+		data->phase2_method->reset(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+	}
+	data->phase2_method = eap_server_get_eap_method(vendor, eap_type);
+	if (!data->phase2_method)
+		return -1;
+
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+
+	return data->phase2_priv ? 0 : -1;
+}
+
+
+static int eap_teap_valid_id_type(struct eap_sm *sm, struct eap_teap_data *data,
+				  enum teap_identity_types id_type)
+{
+	if (sm->cfg->eap_teap_id == EAP_TEAP_ID_REQUIRE_USER &&
+	    id_type != TEAP_IDENTITY_TYPE_USER)
+		return 0;
+	if (sm->cfg->eap_teap_id == EAP_TEAP_ID_REQUIRE_MACHINE &&
+	    id_type != TEAP_IDENTITY_TYPE_MACHINE)
+		return 0;
+	if (sm->cfg->eap_teap_id == EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE &&
+	    id_type != data->cur_id_type)
+		return 0;
+	if (sm->cfg->eap_teap_id != EAP_TEAP_ID_ALLOW_ANY &&
+	    id_type != TEAP_IDENTITY_TYPE_USER &&
+	    id_type != TEAP_IDENTITY_TYPE_MACHINE)
+		return 0;
+	return 1;
+}
+
+
+static void eap_teap_process_phase2_response(struct eap_sm *sm,
+					     struct eap_teap_data *data,
+					     u8 *in_data, size_t in_len,
+					     enum teap_identity_types id_type)
+{
+	int next_vendor = EAP_VENDOR_IETF;
+	enum eap_type next_type = EAP_TYPE_NONE;
+	struct eap_hdr *hdr;
+	u8 *pos;
+	size_t left;
+	struct wpabuf buf;
+	const struct eap_method *m = data->phase2_method;
+	void *priv = data->phase2_priv;
+
+	if (!priv) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: %s - Phase 2 not initialized?!",
+			   __func__);
+		return;
+	}
+
+	hdr = (struct eap_hdr *) in_data;
+	pos = (u8 *) (hdr + 1);
+
+	if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+		left = in_len - sizeof(*hdr);
+		wpa_hexdump(MSG_DEBUG,
+			    "EAP-TEAP: Phase 2 type Nak'ed; allowed types",
+			    pos + 1, left - 1);
+#ifdef EAP_SERVER_TNC
+		if (m && m->vendor == EAP_VENDOR_IETF &&
+		    m->method == EAP_TYPE_TNC) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Peer Nak'ed required TNC negotiation");
+			next_vendor = EAP_VENDOR_IETF;
+			next_type = eap_teap_req_failure(data, 0);
+			eap_teap_phase2_init(sm, data, next_vendor, next_type);
+			return;
+		}
+#endif /* EAP_SERVER_TNC */
+		eap_sm_process_nak(sm, pos + 1, left - 1);
+		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+		    sm->user->methods[sm->user_eap_method_index].method !=
+		    EAP_TYPE_NONE) {
+			next_vendor = sm->user->methods[
+				sm->user_eap_method_index].vendor;
+			next_type = sm->user->methods[
+				sm->user_eap_method_index++].method;
+			wpa_printf(MSG_DEBUG, "EAP-TEAP: try EAP type %u:%u",
+				   next_vendor, next_type);
+		} else {
+			next_vendor = EAP_VENDOR_IETF;
+			next_type = eap_teap_req_failure(data, 0);
+		}
+		eap_teap_phase2_init(sm, data, next_vendor, next_type);
+		return;
+	}
+
+	wpabuf_set(&buf, in_data, in_len);
+
+	if (m->check(sm, priv, &buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Phase 2 check() asked to ignore the packet");
+		eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		return;
+	}
+
+	m->process(sm, priv, &buf);
+
+	if (!m->isDone(sm, priv))
+		return;
+
+	if (!m->isSuccess(sm, priv)) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 method failed");
+		next_vendor = EAP_VENDOR_IETF;
+		next_type = eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		eap_teap_phase2_init(sm, data, next_vendor, next_type);
+		return;
+	}
+
+	switch (data->state) {
+	case PHASE2_ID:
+		if (!eap_teap_valid_id_type(sm, data, id_type)) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Provided Identity-Type %u not allowed",
+				   id_type);
+			eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+			break;
+		}
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "EAP-TEAP: Phase 2 Identity not found in the user database",
+					  sm->identity, sm->identity_len);
+			next_vendor = EAP_VENDOR_IETF;
+			next_type = eap_teap_req_failure(
+				data, TEAP_ERROR_INNER_METHOD);
+			break;
+		}
+
+		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;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %u:%u",
+			   next_vendor, next_type);
+		break;
+	case PHASE2_METHOD:
+	case CRYPTO_BINDING:
+		eap_teap_update_icmk(sm, data);
+		if (data->state == PHASE2_METHOD &&
+		    (sm->cfg->eap_teap_id !=
+		     EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE ||
+		     data->cur_id_type == TEAP_IDENTITY_TYPE_MACHINE))
+			data->inner_eap_not_done = 0;
+		eap_teap_state(data, CRYPTO_BINDING);
+		data->eap_seq++;
+		next_vendor = EAP_VENDOR_IETF;
+		next_type = EAP_TYPE_NONE;
+#ifdef EAP_SERVER_TNC
+		if (sm->cfg->tnc && !data->tnc_started) {
+			wpa_printf(MSG_DEBUG, "EAP-TEAP: Initialize TNC");
+			next_vendor = EAP_VENDOR_IETF;
+			next_type = EAP_TYPE_TNC;
+			data->tnc_started = 1;
+		}
+#endif /* EAP_SERVER_TNC */
+		break;
+	case FAILURE:
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d",
+			   __func__, data->state);
+		break;
+	}
+
+	eap_teap_phase2_init(sm, data, next_vendor, next_type);
+}
+
+
+static void eap_teap_process_phase2_eap(struct eap_sm *sm,
+					struct eap_teap_data *data,
+					u8 *in_data, size_t in_len,
+					enum teap_identity_types id_type)
+{
+	struct eap_hdr *hdr;
+	size_t len;
+
+	hdr = (struct eap_hdr *) in_data;
+	if (in_len < (int) sizeof(*hdr)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Too short Phase 2 EAP frame (len=%lu)",
+			   (unsigned long) in_len);
+		eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		return;
+	}
+	len = be_to_host16(hdr->length);
+	if (len > in_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Length mismatch in Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+			   (unsigned long) in_len, (unsigned long) len);
+		eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		return;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Received Phase 2: code=%d identifier=%d length=%lu",
+		   hdr->code, hdr->identifier,
+		   (unsigned long) len);
+	switch (hdr->code) {
+	case EAP_CODE_RESPONSE:
+		eap_teap_process_phase2_response(sm, data, (u8 *) hdr, len,
+						 id_type);
+		break;
+	default:
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header",
+			   hdr->code);
+		break;
+	}
+}
+
+
+static void eap_teap_process_basic_auth_resp(struct eap_sm *sm,
+					     struct eap_teap_data *data,
+					     u8 *in_data, size_t in_len,
+					     enum teap_identity_types id_type)
+{
+	u8 *pos, *end, *username, *password, *new_id;
+	u8 userlen, passlen;
+
+	if (!eap_teap_valid_id_type(sm, data, id_type)) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Provided Identity-Type %u not allowed",
+			   id_type);
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	pos = in_data;
+	end = pos + in_len;
+
+	if (end - pos < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No room for Basic-Password-Auth-Resp Userlen field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	userlen = *pos++;
+	if (end - pos < userlen) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Truncated Basic-Password-Auth-Resp Username field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	username = pos;
+	pos += userlen;
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "EAP-TEAP: Basic-Password-Auth-Resp Username",
+			  username, userlen);
+
+	if (end - pos < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No room for Basic-Password-Auth-Resp Passlen field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	passlen = *pos++;
+	if (end - pos < passlen) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Truncated Basic-Password-Auth-Resp Password field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	password = pos;
+	pos += passlen;
+	wpa_hexdump_ascii_key(MSG_DEBUG,
+			      "EAP-TEAP: Basic-Password-Auth-Resp Password",
+			      password, passlen);
+
+	if (end > pos) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected %d extra octet(s) at the end of Basic-Password-Auth-Resp TLV",
+			   (int) (end - pos));
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	if (eap_user_get(sm, username, userlen, 1) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Username not found in the user database");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	if (!sm->user || !sm->user->password || sm->user->password_hash) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No plaintext user password configured");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	if (sm->user->password_len != passlen ||
+	    os_memcmp_const(sm->user->password, password, passlen) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Invalid password");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Correct password");
+	new_id = os_memdup(username, userlen);
+	if (new_id) {
+		os_free(sm->identity);
+		sm->identity = new_id;
+		sm->identity_len = userlen;
+	}
+	if (sm->cfg->eap_teap_id != EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE ||
+	    data->cur_id_type == TEAP_IDENTITY_TYPE_MACHINE)
+		data->basic_auth_not_done = 0;
+	eap_teap_state(data, CRYPTO_BINDING);
+	eap_teap_update_icmk(sm, data);
+}
+
+
+static int eap_teap_parse_tlvs(struct wpabuf *data,
+			       struct eap_teap_tlv_parse *tlv)
+{
+	u16 tlv_type;
+	int mandatory, res;
+	size_t len;
+	u8 *pos, *end;
+
+	os_memset(tlv, 0, sizeof(*tlv));
+
+	pos = wpabuf_mhead(data);
+	end = pos + wpabuf_len(data);
+	while (end - pos > 4) {
+		mandatory = pos[0] & 0x80;
+		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > (size_t) (end - pos)) {
+			wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s",
+			   tlv_type, eap_teap_tlv_type_str(tlv_type),
+			   (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
+
+		res = eap_teap_parse_tlv(tlv, tlv_type, pos, len);
+		if (res == -2)
+			break;
+		if (res < 0) {
+			if (mandatory) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: NAK unknown mandatory TLV type %u",
+					   tlv_type);
+				/* TODO: generate NAK TLV */
+				break;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Ignore unknown optional TLV type %u",
+				   tlv_type);
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+static int eap_teap_validate_crypto_binding(
+	struct eap_teap_data *data, const struct teap_tlv_crypto_binding *cb,
+	size_t bind_len)
+{
+	u8 flags, subtype;
+
+	subtype = cb->subtype & 0x0f;
+	flags = cb->subtype >> 4;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+		   cb->version, cb->received_version, flags, subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+		    cb->nonce, sizeof(cb->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+		    cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+		    cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+	if (cb->version != EAP_TEAP_VERSION ||
+	    cb->received_version != data->peer_version) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected version in Crypto-Binding: Version %u Received Version %u",
+			   cb->version, cb->received_version);
+		return -1;
+	}
+
+	if (flags < 1 || flags > 3) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected Flags in Crypto-Binding: %u",
+			   flags);
+		return -1;
+	}
+
+	if (subtype != TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected Sub-Type in Crypto-Binding: %u",
+			   subtype);
+		return -1;
+	}
+
+	if (os_memcmp_const(data->crypto_binding_nonce, cb->nonce,
+			    EAP_TEAP_NONCE_LEN - 1) != 0 ||
+	    (data->crypto_binding_nonce[EAP_TEAP_NONCE_LEN - 1] | 1) !=
+	    cb->nonce[EAP_TEAP_NONCE_LEN - 1]) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Invalid Nonce in Crypto-Binding");
+		return -1;
+	}
+
+	if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+	    flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+		u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+		if (eap_teap_compound_mac(data->tls_cs, cb,
+					  data->server_outer_tlvs,
+					  data->peer_outer_tlvs, data->cmk_msk,
+					  msk_compound_mac) < 0)
+			return -1;
+		if (os_memcmp_const(msk_compound_mac, cb->msk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN) != 0) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TEAP: Calculated MSK Compound MAC",
+				    msk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN);
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: MSK Compound MAC did not match");
+			return -1;
+		}
+	}
+
+	if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+	     flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
+	    data->cmk_emsk_available) {
+		u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+		if (eap_teap_compound_mac(data->tls_cs, cb,
+					  data->server_outer_tlvs,
+					  data->peer_outer_tlvs, data->cmk_emsk,
+					  emsk_compound_mac) < 0)
+			return -1;
+		if (os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN) != 0) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TEAP: Calculated EMSK Compound MAC",
+				    emsk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN);
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: EMSK Compound MAC did not match");
+			return -1;
+		}
+	}
+
+	if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
+	    !data->cmk_emsk_available) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Peer included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this");
+		return -1;
+	}
+
+	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)
+{
+	struct eap_teap_tlv_parse tlv;
+	int check_crypto_binding = data->state == CRYPTO_BINDING;
+
+	if (eap_teap_parse_tlvs(in_data, &tlv) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Failed to parse received Phase 2 TLVs");
+		return;
+	}
+
+	if (tlv.result == TEAP_STATUS_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Result TLV indicated failure");
+		eap_teap_state(data, FAILURE);
+		return;
+	}
+
+	if (tlv.nak) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Peer NAK'ed Vendor-Id %u NAK-Type %u",
+			   WPA_GET_BE32(tlv.nak), WPA_GET_BE16(tlv.nak + 4));
+		eap_teap_state(data, FAILURE_SEND_RESULT);
+		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,
+				   "EAP-TEAP: No Crypto-Binding TLV received");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (data->final_result &&
+		    tlv.result != TEAP_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Crypto-Binding TLV without Success Result");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (sm->cfg->eap_teap_auth != 1 &&
+		    !data->skipped_inner_auth &&
+		    tlv.iresult != TEAP_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Crypto-Binding TLV without intermediate Success Result");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (eap_teap_validate_crypto_binding(data, tlv.crypto_binding,
+						     tlv.crypto_binding_len)) {
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Valid Crypto-Binding TLV received");
+		if (data->final_result) {
+			wpa_printf(MSG_DEBUG,
+				   "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) {
+			eap_teap_state(data, SUCCESS);
+		} else if (sm->cfg->eap_teap_separate_result) {
+			eap_teap_state(data, SUCCESS_SEND_RESULT);
+		}
+	}
+
+	if (tlv.basic_auth_resp) {
+		if (sm->cfg->eap_teap_auth != 1) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Unexpected Basic-Password-Auth-Resp when trying to use inner EAP");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+		eap_teap_process_basic_auth_resp(sm, data, tlv.basic_auth_resp,
+						 tlv.basic_auth_resp_len,
+						 tlv.identity_type);
+	}
+
+	if (tlv.eap_payload_tlv) {
+		if (sm->cfg->eap_teap_auth == 1) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Unexpected EAP Payload TLV when trying to use Basic-Password-Auth");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+		eap_teap_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
+					    tlv.eap_payload_tlv_len,
+					    tlv.identity_type);
+	}
+
+	if (data->state == SUCCESS_SEND_RESULT &&
+	    tlv.result == TEAP_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Peer agreed with final success - authentication completed");
+		eap_teap_state(data, SUCCESS);
+	} else if (check_crypto_binding && data->state == CRYPTO_BINDING &&
+		   sm->cfg->eap_teap_auth == 1 && data->basic_auth_not_done) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Continue with basic password authentication for second credential");
+		eap_teap_state(data, PHASE2_BASIC_AUTH);
+	} else if (check_crypto_binding && data->state == CRYPTO_BINDING &&
+		   sm->cfg->eap_teap_auth == 0 && data->inner_eap_not_done) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Continue with inner EAP authentication for second credential");
+		eap_teap_state(data, PHASE2_ID);
+		if (eap_teap_phase2_init(sm, data, EAP_VENDOR_IETF,
+					 EAP_TYPE_IDENTITY) < 0)
+			eap_teap_state(data, FAILURE);
+	}
+}
+
+
+static void eap_teap_process_phase2(struct eap_sm *sm,
+				    struct eap_teap_data *data,
+				    struct wpabuf *in_buf)
+{
+	struct wpabuf *in_decrypted;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Received %lu bytes encrypted data for Phase 2",
+		   (unsigned long) wpabuf_len(in_buf));
+
+	if (data->pending_phase2_resp) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Pending Phase 2 response - skip decryption and use old data");
+		eap_teap_process_phase2_tlvs(sm, data,
+					     data->pending_phase2_resp);
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = NULL;
+		return;
+	}
+
+	in_decrypted = tls_connection_decrypt(sm->cfg->ssl_ctx, data->ssl.conn,
+					      in_buf);
+	if (!in_decrypted) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Failed to decrypt Phase 2 data");
+		eap_teap_state(data, FAILURE);
+		return;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Decrypted Phase 2 TLVs",
+			    in_decrypted);
+
+	eap_teap_process_phase2_tlvs(sm, data, in_decrypted);
+
+	if (sm->method_pending == METHOD_PENDING_WAIT) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Phase 2 method is in pending wait state - save decrypted response");
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = in_decrypted;
+		return;
+	}
+
+	wpabuf_free(in_decrypted);
+}
+
+
+static int eap_teap_process_version(struct eap_sm *sm, void *priv,
+				    int peer_version)
+{
+	struct eap_teap_data *data = priv;
+
+	if (peer_version < 1) {
+		/* Version 1 was the first defined version, so reject 0 */
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Peer used unknown TEAP version %u",
+			   peer_version);
+		return -1;
+	}
+
+	if (peer_version < data->teap_version) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: peer ver=%u, own ver=%u; "
+			   "use version %u",
+			   peer_version, data->teap_version, peer_version);
+		data->teap_version = peer_version;
+	}
+
+	data->peer_version = peer_version;
+
+	return 0;
+}
+
+
+static int eap_teap_process_phase1(struct eap_sm *sm,
+				   struct eap_teap_data *data)
+{
+	if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: TLS processing failed");
+		eap_teap_state(data, FAILURE);
+		return -1;
+	}
+
+	if (!tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) ||
+	    wpabuf_len(data->ssl.tls_out) > 0)
+		return 1;
+
+	/*
+	 * Phase 1 was completed with the received message (e.g., when using
+	 * abbreviated handshake), so Phase 2 can be started immediately
+	 * without having to send through an empty message to the peer.
+	 */
+
+	return eap_teap_phase1_done(sm, data);
+}
+
+
+static int eap_teap_process_phase2_start(struct eap_sm *sm,
+					 struct eap_teap_data *data)
+{
+	int next_vendor;
+	enum eap_type next_type;
+
+	if (data->identity) {
+		/* Used PAC and identity is from PAC-Opaque */
+		os_free(sm->identity);
+		sm->identity = data->identity;
+		data->identity = NULL;
+		sm->identity_len = data->identity_len;
+		data->identity_len = 0;
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "EAP-TEAP: Phase 2 Identity not found in the user database",
+					  sm->identity, sm->identity_len);
+			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) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Used PAC 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);
+			eap_teap_state(data, CRYPTO_BINDING);
+			return 1;
+		} else if (sm->cfg->eap_teap_auth == 1) {
+			eap_teap_state(data, PHASE2_BASIC_AUTH);
+			return 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Identity already known - skip Phase 2 Identity Request");
+			next_vendor = sm->user->methods[0].vendor;
+			next_type = sm->user->methods[0].method;
+			sm->user_eap_method_index = 1;
+			eap_teap_state(data, PHASE2_METHOD);
+		}
+
+	} else if (sm->cfg->eap_teap_auth == 1) {
+		eap_teap_state(data, PHASE2_BASIC_AUTH);
+		return 0;
+	} else {
+		eap_teap_state(data, PHASE2_ID);
+		next_vendor = EAP_VENDOR_IETF;
+		next_type = EAP_TYPE_IDENTITY;
+	}
+
+	return eap_teap_phase2_init(sm, data, next_vendor, next_type);
+}
+
+
+static void eap_teap_process_msg(struct eap_sm *sm, void *priv,
+				 const struct wpabuf *respData)
+{
+	struct eap_teap_data *data = priv;
+
+	switch (data->state) {
+	case PHASE1:
+	case PHASE1B:
+		if (eap_teap_process_phase1(sm, data))
+			break;
+
+		/* fall through */
+	case PHASE2_START:
+		eap_teap_process_phase2_start(sm, data);
+		break;
+	case PHASE2_ID:
+	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;
+	case FAILURE_SEND_RESULT:
+		/* Protected failure result indication completed. Ignore the
+		 * received message (which is supposed to include Result TLV
+		 * indicating failure) and terminate exchange with cleartext
+		 * EAP-Failure. */
+		eap_teap_state(data, FAILURE);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Unexpected state %d in %s",
+			   data->state, __func__);
+		break;
+	}
+}
+
+
+static void eap_teap_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_teap_data *data = priv;
+	const u8 *pos;
+	size_t len;
+	struct wpabuf *resp = respData;
+	u8 flags;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len);
+	if (!pos || len < 1)
+		return;
+
+	flags = *pos++;
+	len--;
+
+	if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
+		/* Extract Outer TLVs from the message before common TLS
+		 * processing */
+		u32 message_len = 0, outer_tlv_len;
+		const u8 *hdr;
+
+		if (data->state != PHASE1) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Unexpected Outer TLVs in a message that is not the first message from the peer");
+			return;
+		}
+
+		if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+			if (len < 4) {
+				wpa_printf(MSG_INFO,
+					   "EAP-TEAP: Too short message to include Message Length field");
+				return;
+			}
+
+			message_len = WPA_GET_BE32(pos);
+			pos += 4;
+			len -= 4;
+			if (message_len < 4) {
+				wpa_printf(MSG_INFO,
+					   "EAP-TEAP: Message Length field has too msall value to include Outer TLV Length field");
+				return;
+			}
+		}
+
+		if (len < 4) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short message to include Outer TLVs Length field");
+			return;
+		}
+
+		outer_tlv_len = WPA_GET_BE32(pos);
+		pos += 4;
+		len -= 4;
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Message Length %u Outer TLV Length %u",
+			  message_len, outer_tlv_len);
+		if (len < outer_tlv_len) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short message to include Outer TLVs field");
+			return;
+		}
+
+		if (message_len &&
+		    (message_len < outer_tlv_len ||
+		     message_len < 4 + outer_tlv_len)) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Message Length field has too small value to include Outer TLVs");
+			return;
+		}
+
+		if (wpabuf_len(respData) < 4 + outer_tlv_len ||
+		    len < outer_tlv_len)
+			return;
+		resp = wpabuf_alloc(wpabuf_len(respData) - 4 - outer_tlv_len);
+		if (!resp)
+			return;
+		hdr = wpabuf_head(respData);
+		wpabuf_put_u8(resp, *hdr++); /* Code */
+		wpabuf_put_u8(resp, *hdr++); /* Identifier */
+		wpabuf_put_be16(resp, WPA_GET_BE16(hdr) - 4 - outer_tlv_len);
+		hdr += 2;
+		wpabuf_put_u8(resp, *hdr++); /* Type */
+		/* Flags | Ver */
+		wpabuf_put_u8(resp, flags & ~EAP_TEAP_FLAGS_OUTER_TLV_LEN);
+
+		if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+			wpabuf_put_be32(resp, message_len - 4 - outer_tlv_len);
+
+		wpabuf_put_data(resp, pos, len - outer_tlv_len);
+		pos += len - outer_tlv_len;
+		wpabuf_free(data->peer_outer_tlvs);
+		data->peer_outer_tlvs = wpabuf_alloc_copy(pos, outer_tlv_len);
+		if (!data->peer_outer_tlvs)
+			return;
+		wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Outer TLVs",
+				data->peer_outer_tlvs);
+
+		wpa_hexdump_buf(MSG_DEBUG,
+				"EAP-TEAP: TLS Data message after Outer TLV removal",
+				resp);
+		pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, resp,
+				       &len);
+		if (!pos || len < 1) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Invalid frame after Outer TLV removal");
+			return;
+		}
+	}
+
+	if (data->state == PHASE1)
+		eap_teap_state(data, PHASE1B);
+
+	if (eap_server_tls_process(sm, &data->ssl, resp, data,
+				   EAP_TYPE_TEAP, eap_teap_process_version,
+				   eap_teap_process_msg) < 0)
+		eap_teap_state(data, FAILURE);
+
+	if (resp != respData)
+		wpabuf_free(resp);
+}
+
+
+static Boolean eap_teap_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = os_malloc(EAP_TEAP_KEY_LEN);
+	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,
+				    eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
+	*len = EAP_TEAP_KEY_LEN;
+
+	return eapKeyData;
+}
+
+
+static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = os_malloc(EAP_EMSK_LEN);
+	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,
+				     eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
+	*len = EAP_EMSK_LEN;
+
+	return eapKeyData;
+}
+
+
+static Boolean eap_teap_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	const size_t max_id_len = 100;
+	int res;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	id = os_malloc(max_id_len);
+	if (!id)
+		return NULL;
+
+	id[0] = EAP_TYPE_TEAP;
+	res = tls_get_tls_unique(data->ssl.conn, id + 1, max_id_len - 1);
+	if (res < 0) {
+		os_free(id);
+		wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id");
+		return NULL;
+	}
+
+	*len = 1 + res;
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id", id, *len);
+	return id;
+}
+
+
+int eap_server_teap_register(void)
+{
+	struct eap_method *eap;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP");
+	if (!eap)
+		return -1;
+
+	eap->init = eap_teap_init;
+	eap->reset = eap_teap_reset;
+	eap->buildReq = eap_teap_buildReq;
+	eap->check = eap_teap_check;
+	eap->process = eap_teap_process;
+	eap->isDone = eap_teap_isDone;
+	eap->getKey = eap_teap_getKey;
+	eap->get_emsk = eap_teap_get_emsk;
+	eap->isSuccess = eap_teap_isSuccess;
+	eap->getSessionId = eap_teap_get_session_id;
+
+	return eap_server_method_register(eap);
+}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 357e72a..c64cebb 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -58,7 +58,7 @@
 {
 	struct wpabuf *buf;
 
-	if (!sm->tls_session_lifetime)
+	if (!sm->cfg->tls_session_lifetime)
 		return;
 
 	buf = wpabuf_alloc(1);
@@ -187,7 +187,8 @@
 	case START:
 		return eap_tls_build_start(sm, data, id);
 	case CONTINUE:
-		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+		if (tls_connection_established(sm->cfg->ssl_ctx,
+					       data->ssl.conn))
 			data->established = 1;
 		break;
 	default:
@@ -261,8 +262,43 @@
 			   "handshake message");
 		return;
 	}
-	if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+	if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
 		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	if (data->ssl.tls_v13 &&
+	    tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn)) {
+		struct wpabuf *plain, *encr;
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Send empty application data to indicate end of exchange");
+		/* FIX: This should be an empty application data based on
+		 * draft-ietf-emu-eap-tls13-05, but OpenSSL does not allow zero
+		 * length payload (SSL_write() documentation explicitly
+		 * describes this as not allowed), so work around that for now
+		 * by sending out a payload of one octet. Hopefully the draft
+		 * specification will change to allow this so that no crypto
+		 * library changes are needed. */
+		plain = wpabuf_alloc(1);
+		if (!plain)
+			return;
+		wpabuf_put_u8(plain, 0);
+		encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
+		wpabuf_free(plain);
+		if (!encr)
+			return;
+		if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TLS: Failed to resize output buffer");
+			wpabuf_free(encr);
+			return;
+		}
+		wpabuf_put_buf(data->ssl.tls_out, encr);
+		wpa_hexdump_buf(MSG_DEBUG,
+				"EAP-TLS: Data appended to the message", encr);
+		wpabuf_free(encr);
+	}
 }
 
 
@@ -280,8 +316,8 @@
 		return;
 	}
 
-	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
-	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+	if (!tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn))
 		return;
 
 	buf = tls_connection_get_success_data(data->ssl.conn);
@@ -322,16 +358,22 @@
 	struct eap_tls_data *data = priv;
 	u8 *eapKeyData;
 	const char *label;
+	const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
+	const u8 *context = NULL;
+	size_t context_len = 0;
 
 	if (data->state != SUCCESS)
 		return NULL;
 
-	if (data->ssl.tls_v13)
+	if (data->ssl.tls_v13) {
 		label = "EXPORTER_EAP_TLS_Key_Material";
-	else
+		context = eap_tls13_context;
+		context_len = 1;
+	} else {
 		label = "client EAP encryption";
+	}
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
-					       NULL, 0,
+					       context, context_len,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		*len = EAP_TLS_KEY_LEN;
@@ -351,16 +393,22 @@
 	struct eap_tls_data *data = priv;
 	u8 *eapKeyData, *emsk;
 	const char *label;
+	const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
+	const u8 *context = NULL;
+	size_t context_len = 0;
 
 	if (data->state != SUCCESS)
 		return NULL;
 
-	if (data->ssl.tls_v13)
+	if (data->ssl.tls_v13) {
 		label = "EXPORTER_EAP_TLS_Key_Material";
-	else
+		context = eap_tls13_context;
+		context_len = 1;
+	} else {
 		label = "client EAP encryption";
+	}
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
-					       NULL, 0,
+					       context, context_len,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 0eca0ff..b38f1e0 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -18,7 +18,7 @@
 static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
 
 
-struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len,
 				  u8 code, u8 identifier)
 {
 	if (type == EAP_UNAUTH_TLS_TYPE)
@@ -47,9 +47,9 @@
 			    int verify_peer, int eap_type)
 {
 	u8 session_ctx[8];
-	unsigned int flags = sm->tls_flags;
+	unsigned int flags = sm->cfg->tls_flags;
 
-	if (sm->ssl_ctx == NULL) {
+	if (!sm->cfg->ssl_ctx) {
 		wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
 		return -1;
 	}
@@ -57,7 +57,7 @@
 	data->eap = sm;
 	data->phase2 = sm->init_phase2;
 
-	data->conn = tls_connection_init(sm->ssl_ctx);
+	data->conn = tls_connection_init(sm->cfg->ssl_ctx);
 	if (data->conn == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
 			   "connection");
@@ -75,17 +75,18 @@
 		flags |= TLS_CONN_DISABLE_SESSION_TICKET;
 	os_memcpy(session_ctx, "hostapd", 7);
 	session_ctx[7] = (u8) eap_type;
-	if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer,
+	if (tls_connection_set_verify(sm->cfg->ssl_ctx, data->conn, verify_peer,
 				      flags, session_ctx,
 				      sizeof(session_ctx))) {
 		wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
 			   "of TLS peer certificate");
-		tls_connection_deinit(sm->ssl_ctx, data->conn);
+		tls_connection_deinit(sm->cfg->ssl_ctx, data->conn);
 		data->conn = NULL;
 		return -1;
 	}
 
-	data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398;
+	data->tls_out_limit = sm->cfg->fragment_size > 0 ?
+		sm->cfg->fragment_size : 1398;
 	if (data->phase2) {
 		/* Limit the fragment size in the inner TLS authentication
 		 * since the outer authentication with EAP-PEAP does not yet
@@ -99,7 +100,7 @@
 
 void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
 {
-	tls_connection_deinit(sm->ssl_ctx, data->conn);
+	tls_connection_deinit(sm->cfg->ssl_ctx, data->conn);
 	eap_server_tls_free_in_buf(data);
 	wpabuf_free(data->tls_out);
 	data->tls_out = NULL;
@@ -116,7 +117,7 @@
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_export_key(sm->ssl_ctx, data->conn, label,
+	if (tls_connection_export_key(sm->cfg->ssl_ctx, data->conn, label,
 				      context, context_len, out, len)) {
 		os_free(out);
 		return NULL;
@@ -145,20 +146,21 @@
 {
 	struct tls_random keys;
 	u8 *out;
+	const u8 context[] = { EAP_TYPE_TLS };
 
 	if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
 		u8 *id, *method_id;
 
 		/* Session-Id = <EAP-Type> || Method-Id
 		 * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id",
-		 *                          "", 64)
+		 *                          Type-Code, 64)
 		 */
 		*len = 1 + 64;
 		id = os_malloc(*len);
 		if (!id)
 			return NULL;
 		method_id = eap_server_tls_derive_key(
-			sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64);
+			sm, data, "EXPORTER_EAP_TLS_Method-Id", context, 1, 64);
 		if (!method_id) {
 			os_free(id);
 			return NULL;
@@ -169,7 +171,7 @@
 		return id;
 	}
 
-	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
+	if (tls_connection_get_random(sm->cfg->ssl_ctx, data->conn, &keys))
 		return NULL;
 
 	if (keys.client_random == NULL || keys.server_random == NULL)
@@ -339,29 +341,30 @@
 		WPA_ASSERT(data->tls_out == NULL);
 	}
 
-	data->tls_out = tls_connection_server_handshake(sm->ssl_ctx,
+	data->tls_out = tls_connection_server_handshake(sm->cfg->ssl_ctx,
 							data->conn,
 							data->tls_in, NULL);
 	if (data->tls_out == NULL) {
 		wpa_printf(MSG_INFO, "SSL: TLS processing failed");
 		return -1;
 	}
-	if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+	if (tls_connection_get_failed(sm->cfg->ssl_ctx, data->conn)) {
 		/* TLS processing has failed - return error */
 		wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
 			   "report error");
 		return -1;
 	}
 
-	if (tls_get_version(sm->ssl_ctx, data->conn, buf, sizeof(buf)) == 0) {
+	if (tls_get_version(sm->cfg->ssl_ctx, data->conn,
+			    buf, sizeof(buf)) == 0) {
 		wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf);
 		data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
 	}
 
 	if (!sm->serial_num &&
-	    tls_connection_established(sm->ssl_ctx, data->conn))
-		sm->serial_num = tls_connection_peer_serial_num(sm->ssl_ctx,
-								data->conn);
+	    tls_connection_established(sm->cfg->ssl_ctx, data->conn))
+		sm->serial_num = tls_connection_peer_serial_num(
+			sm->cfg->ssl_ctx, data->conn);
 
 	return 0;
 }
@@ -373,6 +376,8 @@
 	unsigned int tls_msg_len = 0;
 	const u8 *end = *pos + *left;
 
+	wpa_hexdump(MSG_MSGDUMP, "SSL: Received data", *pos, *left);
+
 	if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
 		if (*left < 4) {
 			wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
@@ -448,8 +453,7 @@
 {
 	struct wpabuf *buf;
 
-	buf = tls_connection_encrypt(sm->ssl_ctx, data->conn,
-				     plain);
+	buf = tls_connection_encrypt(sm->cfg->ssl_ctx, data->conn, plain);
 	if (buf == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data");
 		return NULL;
@@ -503,7 +507,7 @@
 	if (proc_msg)
 		proc_msg(sm, priv, respData);
 
-	if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) {
+	if (tls_connection_get_write_alerts(sm->cfg->ssl_ctx, data->conn) > 1) {
 		wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in "
 			   "TLS processing");
 		res = -1;
diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c
index b568558..f6cdcb1 100644
--- a/src/eap_server/eap_server_tnc.c
+++ b/src/eap_server/eap_server_tnc.c
@@ -84,8 +84,8 @@
 		return NULL;
 	}
 
-	data->fragment_size = sm->fragment_size > 100 ?
-		sm->fragment_size - 98 : 1300;
+	data->fragment_size = sm->cfg->fragment_size > 100 ?
+		sm->cfg->fragment_size - 98 : 1300;
 
 	return data;
 }
@@ -508,7 +508,7 @@
 		eap_tnc_set_state(data, FAIL);
 		return;
 	}
-		
+
 	if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
 		if (eap_tnc_process_fragment(data, flags, message_length,
 					     pos, end - pos) < 0)
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 52bff8a..721835d 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -81,7 +81,7 @@
 {
 	struct wpabuf *buf;
 
-	if (!sm->tls_session_lifetime)
+	if (!sm->cfg->tls_session_lifetime)
 		return;
 
 	buf = wpabuf_alloc(1 + 1 + sm->identity_len);
@@ -480,7 +480,8 @@
 	case START:
 		return eap_ttls_build_start(sm, data, id);
 	case PHASE1:
-		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+		if (tls_connection_established(sm->cfg->ssl_ctx,
+					       data->ssl.conn)) {
 			wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, "
 				   "starting Phase2");
 			eap_ttls_state(data, PHASE2_START);
@@ -827,15 +828,14 @@
 
 static int eap_ttls_phase2_eap_init(struct eap_sm *sm,
 				    struct eap_ttls_data *data,
-				    EapType eap_type)
+				    int vendor, enum eap_type eap_type)
 {
 	if (data->phase2_priv && data->phase2_method) {
 		data->phase2_method->reset(sm, data->phase2_priv);
 		data->phase2_method = NULL;
 		data->phase2_priv = NULL;
 	}
-	data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
-							eap_type);
+	data->phase2_method = eap_server_get_eap_method(vendor, eap_type);
 	if (!data->phase2_method)
 		return -1;
 
@@ -850,7 +850,8 @@
 						 struct eap_ttls_data *data,
 						 u8 *in_data, size_t in_len)
 {
-	u8 next_type = EAP_TYPE_NONE;
+	int next_vendor = EAP_VENDOR_IETF;
+	enum eap_type next_type = EAP_TYPE_NONE;
 	struct eap_hdr *hdr;
 	u8 *pos;
 	size_t left;
@@ -875,14 +876,17 @@
 		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
 		    sm->user->methods[sm->user_eap_method_index].method !=
 		    EAP_TYPE_NONE) {
+			next_vendor = sm->user->methods[
+				sm->user_eap_method_index].vendor;
 			next_type = sm->user->methods[
 				sm->user_eap_method_index++].method;
-			wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d",
-				   next_type);
-			if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
-				wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to "
-					   "initialize EAP type %d",
-					   next_type);
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %u:%u",
+				   next_vendor, next_type);
+			if (eap_ttls_phase2_eap_init(sm, data, next_vendor,
+						     next_type)) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TTLS: Failed to initialize EAP type %u:%u",
+					   next_vendor, next_type);
 				eap_ttls_state(data, FAILURE);
 				return;
 			}
@@ -930,12 +934,16 @@
 		}
 
 		eap_ttls_state(data, PHASE2_METHOD);
+		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-TTLS: try EAP type %d", next_type);
-		if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
-			wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize "
-				   "EAP type %d", next_type);
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %u:%u",
+			   next_vendor, next_type);
+		if (eap_ttls_phase2_eap_init(sm, data, next_vendor,
+					     next_type)) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TTLS: Failed to initialize EAP type %u:%u",
+				   next_vendor, next_type);
 			eap_ttls_state(data, FAILURE);
 		}
 		break;
@@ -962,8 +970,8 @@
 
 	if (data->state == PHASE2_START) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2");
-		if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0)
-		{
+		if (eap_ttls_phase2_eap_init(sm, data, EAP_VENDOR_IETF,
+					     EAP_TYPE_IDENTITY) < 0) {
 			wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to "
 				   "initialize EAP-Identity");
 			return;
@@ -1022,7 +1030,7 @@
 		return;
 	}
 
-	in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+	in_decrypted = tls_connection_decrypt(sm->cfg->ssl_ctx, data->ssl.conn,
 					      in_buf);
 	if (in_decrypted == NULL) {
 		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 "
@@ -1112,11 +1120,11 @@
 static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data)
 {
 #ifdef EAP_SERVER_TNC
-	if (!sm->tnc || data->state != SUCCESS || data->tnc_started)
+	if (!sm->cfg->tnc || data->state != SUCCESS || data->tnc_started)
 		return;
 
 	wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC");
-	if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) {
+	if (eap_ttls_phase2_eap_init(sm, data, EAP_VENDOR_IETF, EAP_TYPE_TNC)) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC");
 		eap_ttls_state(data, FAILURE);
 		return;
@@ -1202,8 +1210,8 @@
 		return;
 	}
 
-	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
-	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+	if (!tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn))
 		return;
 
 	buf = tls_connection_get_success_data(data->ssl.conn);
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
index 4a5cb98..364c089 100644
--- a/src/eap_server/eap_server_wsc.c
+++ b/src/eap_server/eap_server_wsc.c
@@ -103,10 +103,10 @@
 	data->registrar = registrar;
 
 	os_memset(&cfg, 0, sizeof(cfg));
-	cfg.wps = sm->wps;
+	cfg.wps = sm->cfg->wps;
 	cfg.registrar = registrar;
 	if (registrar) {
-		if (sm->wps == NULL || sm->wps->registrar == NULL) {
+		if (!sm->cfg->wps || !sm->cfg->wps->registrar) {
 			wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
 				   "initialized");
 			os_free(data);
@@ -138,14 +138,14 @@
 		cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
 	}
 #endif /* CONFIG_P2P */
-	cfg.pbc_in_m1 = sm->pbc_in_m1;
+	cfg.pbc_in_m1 = sm->cfg->pbc_in_m1;
 	data->wps = wps_init(&cfg);
 	if (data->wps == NULL) {
 		os_free(data);
 		return NULL;
 	}
-	data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
-		WSC_FRAGMENT_SIZE;
+	data->fragment_size = sm->cfg->fragment_size > 0 ?
+		sm->cfg->fragment_size : WSC_FRAGMENT_SIZE;
 
 	return data;
 }
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 0b04983..b0b7361 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -62,6 +62,7 @@
 #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
 #define EAP_TLS_FLAGS_START 0x20
+#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10
 #define EAP_TLS_VERSION_MASK 0x07
 
  /* could be up to 128 bytes, but only the first 64 bytes are used */
@@ -72,7 +73,7 @@
 #define EAP_WFA_UNAUTH_TLS_TYPE 254
 
 
-struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len,
 				  u8 code, u8 identifier);
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
 			    int verify_peer, int eap_type);
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index 942a195..4a30486 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -179,7 +179,7 @@
 	TNC_MessageType messageType)
 {
 	struct tncs_data *tncs;
-	unsigned char *b64;
+	char *b64;
 	size_t b64len;
 
 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
@@ -678,8 +678,7 @@
 		return NULL;
 	*pos2 = '\0';
 
-	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
-				decoded_len);
+	decoded = base64_decode(pos, os_strlen(pos), decoded_len);
 	*pos2 = '<';
 	if (decoded == NULL) {
 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 36074d3..5a2ba26 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -300,7 +300,7 @@
 
 	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
 		sm->authAuthSuccessesWhileAuthenticating++;
-							
+
 	SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae);
 
 	sm->authPortStatus = Authorized;
@@ -775,7 +775,7 @@
 		 const char *identity, const char *radius_cui)
 {
 	struct eapol_state_machine *sm;
-	struct eap_config eap_conf;
+	struct eap_session_data eap_sess;
 
 	if (eapol == NULL)
 		return NULL;
@@ -823,33 +823,12 @@
 	else
 		sm->portValid = TRUE;
 
-	os_memset(&eap_conf, 0, sizeof(eap_conf));
-	eap_conf.eap_server = eapol->conf.eap_server;
-	eap_conf.ssl_ctx = eapol->conf.ssl_ctx;
-	eap_conf.msg_ctx = eapol->conf.msg_ctx;
-	eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv;
-	eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key;
-	eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id;
-	eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len;
-	eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info;
-	eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov;
-	eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime;
-	eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time;
-	eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind;
-	eap_conf.tnc = eapol->conf.tnc;
-	eap_conf.wps = eapol->conf.wps;
-	eap_conf.assoc_wps_ie = assoc_wps_ie;
-	eap_conf.assoc_p2p_ie = assoc_p2p_ie;
-	eap_conf.peer_addr = addr;
-	eap_conf.fragment_size = eapol->conf.fragment_size;
-	eap_conf.pwd_group = eapol->conf.pwd_group;
-	eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
-	eap_conf.server_id = eapol->conf.server_id;
-	eap_conf.server_id_len = eapol->conf.server_id_len;
-	eap_conf.erp = eapol->conf.erp;
-	eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
-	eap_conf.tls_flags = eapol->conf.tls_flags;
-	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
+	os_memset(&eap_sess, 0, sizeof(eap_sess));
+	eap_sess.assoc_wps_ie = assoc_wps_ie;
+	eap_sess.assoc_p2p_ie = assoc_p2p_ie;
+	eap_sess.peer_addr = addr;
+	sm->eap = eap_server_sm_init(sm, &eapol_cb, eapol->conf.eap_cfg,
+				     &eap_sess);
 	if (sm->eap == NULL) {
 		eapol_auth_free(sm);
 		return NULL;
@@ -1184,19 +1163,12 @@
 static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
 				 struct eapol_auth_config *src)
 {
+	dst->eap_cfg = src->eap_cfg;
 	dst->ctx = src->ctx;
 	dst->eap_reauth_period = src->eap_reauth_period;
 	dst->wpa = src->wpa;
 	dst->individual_wep_key_len = src->individual_wep_key_len;
-	dst->eap_server = src->eap_server;
-	dst->ssl_ctx = src->ssl_ctx;
-	dst->msg_ctx = src->msg_ctx;
-	dst->eap_sim_db_priv = src->eap_sim_db_priv;
 	os_free(dst->eap_req_id_text);
-	dst->pwd_group = src->pwd_group;
-	dst->pbc_in_m1 = src->pbc_in_m1;
-	dst->server_id = src->server_id;
-	dst->server_id_len = src->server_id_len;
 	if (src->eap_req_id_text) {
 		dst->eap_req_id_text = os_memdup(src->eap_req_id_text,
 						 src->eap_req_id_text_len);
@@ -1207,34 +1179,6 @@
 		dst->eap_req_id_text = NULL;
 		dst->eap_req_id_text_len = 0;
 	}
-	if (src->pac_opaque_encr_key) {
-		dst->pac_opaque_encr_key = os_memdup(src->pac_opaque_encr_key,
-						     16);
-		if (dst->pac_opaque_encr_key == NULL)
-			goto fail;
-	} else
-		dst->pac_opaque_encr_key = NULL;
-	if (src->eap_fast_a_id) {
-		dst->eap_fast_a_id = os_memdup(src->eap_fast_a_id,
-					       src->eap_fast_a_id_len);
-		if (dst->eap_fast_a_id == NULL)
-			goto fail;
-		dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
-	} else
-		dst->eap_fast_a_id = NULL;
-	if (src->eap_fast_a_id_info) {
-		dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
-		if (dst->eap_fast_a_id_info == NULL)
-			goto fail;
-	} else
-		dst->eap_fast_a_id_info = NULL;
-	dst->eap_fast_prov = src->eap_fast_prov;
-	dst->pac_key_lifetime = src->pac_key_lifetime;
-	dst->pac_key_refresh_time = src->pac_key_refresh_time;
-	dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
-	dst->tnc = src->tnc;
-	dst->wps = src->wps;
-	dst->fragment_size = src->fragment_size;
 
 	os_free(dst->erp_domain);
 	if (src->erp_domain) {
@@ -1245,9 +1189,6 @@
 		dst->erp_domain = NULL;
 	}
 	dst->erp_send_reauth_start = src->erp_send_reauth_start;
-	dst->erp = src->erp;
-	dst->tls_session_lifetime = src->tls_session_lifetime;
-	dst->tls_flags = src->tls_flags;
 
 	return 0;
 
@@ -1261,12 +1202,6 @@
 {
 	os_free(conf->eap_req_id_text);
 	conf->eap_req_id_text = NULL;
-	os_free(conf->pac_opaque_encr_key);
-	conf->pac_opaque_encr_key = NULL;
-	os_free(conf->eap_fast_a_id);
-	conf->eap_fast_a_id = NULL;
-	os_free(conf->eap_fast_a_id_info);
-	conf->eap_fast_a_id_info = NULL;
 	os_free(conf->erp_domain);
 	conf->erp_domain = NULL;
 }
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 44f3f31..5fe89c6 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -15,35 +15,14 @@
 #define EAPOL_SM_FROM_PMKSA_CACHE BIT(3)
 
 struct eapol_auth_config {
+	const struct eap_config *eap_cfg;
 	int eap_reauth_period;
 	int wpa;
 	int individual_wep_key_len;
-	int eap_server;
-	void *ssl_ctx;
-	void *msg_ctx;
-	void *eap_sim_db_priv;
 	char *eap_req_id_text; /* a copy of this will be allocated */
 	size_t eap_req_id_text_len;
 	int erp_send_reauth_start;
 	char *erp_domain; /* a copy of this will be allocated */
-	int erp; /* Whether ERP is enabled on authentication server */
-	unsigned int tls_session_lifetime;
-	unsigned int tls_flags;
-	u8 *pac_opaque_encr_key;
-	u8 *eap_fast_a_id;
-	size_t eap_fast_a_id_len;
-	char *eap_fast_a_id_info;
-	int eap_fast_prov;
-	int pac_key_lifetime;
-	int pac_key_refresh_time;
-	int eap_sim_aka_result_ind;
-	int tnc;
-	struct wps_context *wps;
-	int fragment_size;
-	u16 pwd_group;
-	int pbc_in_m1;
-	const u8 *server_id;
-	size_t server_id_len;
 
 	/* Opaque context pointer to owner data for callback functions */
 	void *ctx;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index a0f27fd..f1ca0a8 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1998,15 +1998,12 @@
 #define eapol_sm_eap_param_needed NULL
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
-static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
-				 const char *altsubject[],
-				 int num_altsubject, const char *cert_hash,
-				 const struct wpabuf *cert)
+static void eapol_sm_notify_cert(void *ctx, struct tls_cert_data *cert,
+				 const char *cert_hash)
 {
 	struct eapol_sm *sm = ctx;
 	if (sm->ctx->cert_cb)
-		sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject,
-				 num_altsubject, cert_hash, cert);
+		sm->ctx->cert_cb(sm->ctx->ctx, cert, cert_hash);
 }
 
 
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 74f40bb..c9d7522 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -11,6 +11,8 @@
 
 #include "common/defs.h"
 
+struct tls_cert_data;
+
 typedef enum { Unauthorized, Authorized } PortStatus;
 typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl;
 
@@ -246,16 +248,11 @@
 	/**
 	 * cert_cb - Notification of a peer certificate
 	 * @ctx: Callback context (ctx)
-	 * @depth: Depth in certificate chain (0 = server)
-	 * @subject: Subject of the peer certificate
-	 * @altsubject: Select fields from AltSubject of the peer certificate
-	 * @num_altsubject: Number of altsubject values
+	 * @cert: Certificate information
 	 * @cert_hash: SHA-256 hash of the certificate
-	 * @cert: Peer certificate
 	 */
-	void (*cert_cb)(void *ctx, int depth, const char *subject,
-			const char *altsubject[], int num_altsubject,
-			const char *cert_hash, const struct wpabuf *cert);
+	void (*cert_cb)(void *ctx, struct tls_cert_data *cert,
+			const char *cert_hash);
 
 	/**
 	 * cert_in_cb - Include server certificates in callback
diff --git a/src/fst/fst.c b/src/fst/fst.c
index 32cd941..fbe1175 100644
--- a/src/fst/fst.c
+++ b/src/fst/fst.c
@@ -214,6 +214,15 @@
 }
 
 
+void fst_update_mac_addr(struct fst_iface *iface, const u8 *addr)
+{
+	fst_printf_iface(iface, MSG_DEBUG, "new MAC address " MACSTR,
+			 MAC2STR(addr));
+	os_memcpy(iface->own_addr, addr, sizeof(iface->own_addr));
+	fst_group_update_ie(fst_iface_get_group(iface));
+}
+
+
 enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode)
 {
 	switch (mode) {
diff --git a/src/fst/fst.h b/src/fst/fst.h
index 2967491..7ba60d5 100644
--- a/src/fst/fst.h
+++ b/src/fst/fst.h
@@ -279,6 +279,13 @@
 Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
 				  struct fst_iface *iface2);
 
+/**
+ * fst_update_mac_addr - Notify FST about MAC address change
+ * @iface: FST interface object
+ * @addr: New MAC address
+ */
+void fst_update_mac_addr(struct fst_iface *iface, const u8 *addr);
+
 #else /* CONFIG_FST */
 
 static inline int fst_global_init(void)
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 291c9dd..138dcaf 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -171,13 +171,16 @@
 		u8 hash[SHA1_MAC_LEN];
 		const u8 *addr[1];
 		size_t len[1];
+		const struct l2_ethhdr *eth = (const struct l2_ethhdr *) buf;
 
 		/*
 		 * Close the workaround socket if the kernel version seems to be
 		 * able to deliver packets through the packet socket before
 		 * authorization has been completed (in dormant state).
 		 */
-		if (l2->num_rx_br <= 1) {
+		if (l2->num_rx_br <= 1 &&
+		    (os_memcmp(eth->h_dest, l2->own_addr, ETH_ALEN) == 0 ||
+		     is_multicast_ether_addr(eth->h_dest))) {
 			wpa_printf(MSG_DEBUG,
 				   "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
 				   l2->ifname);
diff --git a/src/lib.rules b/src/lib.rules
index 4ec4711..a463154 100644
--- a/src/lib.rules
+++ b/src/lib.rules
@@ -11,6 +11,7 @@
 CFLAGS += -DTEST_FUZZ
 endif
 
+CFLAGS += $(FUZZ_CFLAGS)
 CFLAGS += -I.. -I../utils
 
 
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 6107b14..9c73885 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1066,22 +1066,6 @@
 	struct p2p_device *dev;
 	enum p2p_after_scan op;
 
-	if (p2p->after_scan_tx) {
-		p2p->after_scan_tx_in_progress = 1;
-		p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion");
-		p2p->cfg->send_action(p2p->cfg->cb_ctx,
-				      p2p->after_scan_tx->freq,
-				      p2p->after_scan_tx->dst,
-				      p2p->after_scan_tx->src,
-				      p2p->after_scan_tx->bssid,
-				      (u8 *) (p2p->after_scan_tx + 1),
-				      p2p->after_scan_tx->len,
-				      p2p->after_scan_tx->wait_time, NULL);
-		os_free(p2p->after_scan_tx);
-		p2p->after_scan_tx = NULL;
-		return 1;
-	}
-
 	op = p2p->start_after_scan;
 	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
 	switch (op) {
@@ -1420,6 +1404,7 @@
 	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
 	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
 	const int op_classes_vht[] = { 128, 0 };
+	const int op_classes_edmg[] = { 181, 182, 183, 0 };
 
 	p2p_dbg(p2p, "Prepare channel best");
 
@@ -1451,6 +1436,11 @@
 		p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference");
 		p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class;
 		p2p->op_channel = p2p->cfg->pref_chan[0].chan;
+	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_edmg,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible EDMG channel (op_class %u channel %u) as operating channel preference",
+			p2p->op_reg_class, p2p->op_channel);
 	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht,
 				      &p2p->op_reg_class, &p2p->op_channel) ==
 		   0) {
@@ -1646,17 +1636,6 @@
 	if (p2p->state != P2P_IDLE)
 		p2p_stop_find(p2p);
 
-	if (p2p->after_scan_tx) {
-		/*
-		 * We need to drop the pending frame to avoid issues with the
-		 * new GO Negotiation, e.g., when the pending frame was from a
-		 * previous attempt at starting a GO Negotiation.
-		 */
-		p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion");
-		os_free(p2p->after_scan_tx);
-		p2p->after_scan_tx = NULL;
-	}
-
 	dev->wps_method = wps_method;
 	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
@@ -1667,7 +1646,6 @@
 		os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
 		return 0;
 	}
-	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
 
 	return p2p_connect_send(p2p, dev);
 }
@@ -3055,8 +3033,6 @@
 		p2p_device_free(p2p, dev);
 	}
 	p2p_free_sd_queries(p2p);
-	os_free(p2p->after_scan_tx);
-	p2p->after_scan_tx = NULL;
 	p2p->ssid_set = 0;
 	p2ps_prov_free(p2p);
 	p2p_reset_pending_pd(p2p);
@@ -3085,13 +3061,6 @@
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 
-	/* Check if after_scan_tx is for this peer. If so free it */
-	if (p2p->after_scan_tx &&
-	    os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) {
-		os_free(p2p->after_scan_tx);
-		p2p->after_scan_tx = NULL;
-	}
-
 	return 0;
 }
 
@@ -3481,23 +3450,6 @@
 }
 
 
-static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p)
-{
-	if (p2p->after_scan_tx_in_progress) {
-		p2p->after_scan_tx_in_progress = 0;
-		if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
-		    p2p_run_after_scan(p2p))
-			return 1;
-		if (p2p->state == P2P_SEARCH) {
-			p2p_dbg(p2p, "Continue find after after_scan_tx completion");
-			p2p_continue_find(p2p);
-		}
-	}
-
-	return 0;
-}
-
-
 static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
 {
 	p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
@@ -3510,19 +3462,21 @@
 
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
-	if (!success)
-		goto continue_search;
+	if (!success) {
+		if (p2p->state == P2P_SEARCH)
+			p2p_continue_find(p2p);
+		return;
+	}
 
 	if (!p2p->cfg->prov_disc_resp_cb ||
-	    p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1)
-		goto continue_search;
+	    p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1) {
+		if (p2p->state == P2P_SEARCH)
+			p2p_continue_find(p2p);
+		return;
+	}
 
 	p2p_dbg(p2p,
 		"Post-Provision Discovery operations started - do not try to continue other P2P operations");
-	return;
-
-continue_search:
-	p2p_check_after_scan_tx_continuation(p2p);
 }
 
 
@@ -3812,7 +3766,6 @@
 			p2p->send_action_in_progress = 0;
 			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		}
-		p2p_check_after_scan_tx_continuation(p2p);
 		break;
 	case P2P_PENDING_GO_NEG_REQUEST:
 		p2p_go_neg_req_cb(p2p, success);
@@ -3840,8 +3793,6 @@
 		break;
 	case P2P_PENDING_INVITATION_RESPONSE:
 		p2p_invitation_resp_cb(p2p, success);
-		if (p2p->inv_status != P2P_SC_SUCCESS)
-			p2p_check_after_scan_tx_continuation(p2p);
 		break;
 	case P2P_PENDING_DEV_DISC_REQUEST:
 		p2p_dev_disc_req_cb(p2p, success);
@@ -3853,8 +3804,6 @@
 		p2p_go_disc_req_cb(p2p, success);
 		break;
 	}
-
-	p2p->after_scan_tx_in_progress = 0;
 }
 
 
@@ -4980,29 +4929,10 @@
 {
 	int res, scheduled;
 
-	if (p2p->p2p_scan_running) {
-		p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes");
-		if (p2p->after_scan_tx) {
-			p2p_dbg(p2p, "Dropped previous pending Action frame TX");
-			os_free(p2p->after_scan_tx);
-		}
-		p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) +
-					       len);
-		if (p2p->after_scan_tx == NULL)
-			return -1;
-		p2p->after_scan_tx->freq = freq;
-		os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN);
-		os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN);
-		os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN);
-		p2p->after_scan_tx->len = len;
-		p2p->after_scan_tx->wait_time = wait_time;
-		os_memcpy(p2p->after_scan_tx + 1, buf, len);
-		return 0;
-	}
-
 	res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
 				    buf, len, wait_time, &scheduled);
 	if (res == 0 && scheduled && p2p->in_listen && freq > 0 &&
+	    p2p->drv_in_listen > 0 &&
 	    (unsigned int) p2p->drv_in_listen != freq) {
 		p2p_dbg(p2p,
 			"Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz",
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 425b037..79de09c 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -99,6 +99,8 @@
 
 	int vht;
 
+	int edmg;
+
 	u8 max_oper_chwidth;
 
 	unsigned int vht_center_freq2;
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 65ab4b8..1133461 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -390,6 +390,7 @@
 	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
 	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
 	const int op_classes_vht[] = { 128, 129, 130, 0 };
+	const int op_classes_edmg[] = { 181, 182, 183, 0 };
 
 	if (p2p->own_freq_preference > 0 &&
 	    p2p_freq_to_channel(p2p->own_freq_preference,
@@ -454,6 +455,14 @@
 		}
 	}
 
+	/* Try a channel where we might be able to use EDMG */
+	if (p2p_channel_select(intersection, op_classes_edmg,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Pick possible EDMG channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
+	}
+
 	/* Try a channel where we might be able to use VHT */
 	if (p2p_channel_select(intersection, op_classes_vht,
 			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
@@ -676,7 +685,9 @@
 				"Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)",
 				freq_list[i], go);
 			if (size - i - 1 > 0)
-				os_memmove(&freq_list[i], &freq_list[i + 1], size - i - 1);
+				os_memmove(&freq_list[i], &freq_list[i + 1],
+					   (size - i - 1) *
+					   sizeof(unsigned int));
 			size--;
 			continue;
 		}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 64a9977..2b168e8 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -159,16 +159,6 @@
 	struct wpabuf *tlvs;
 };
 
-struct p2p_pending_action_tx {
-	unsigned int freq;
-	u8 dst[ETH_ALEN];
-	u8 src[ETH_ALEN];
-	u8 bssid[ETH_ALEN];
-	size_t len;
-	unsigned int wait_time;
-	/* Followed by len octets of the frame */
-};
-
 /**
  * struct p2p_data - P2P module data (internal to P2P module)
  */
@@ -460,8 +450,6 @@
 		P2P_AFTER_SCAN_CONNECT
 	} start_after_scan;
 	u8 after_scan_peer[ETH_ALEN];
-	struct p2p_pending_action_tx *after_scan_tx;
-	unsigned int after_scan_tx_in_progress:1;
 	unsigned int send_action_in_progress:1;
 
 	/* Requested device types for find/search */
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
index 1c4dc3e..69e5758 100644
--- a/src/pae/ieee802_1x_cp.c
+++ b/src/pae/ieee802_1x_cp.c
@@ -141,6 +141,24 @@
 		ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
 	if (sm->oki)
 		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
+	/* The standard doesn't say it but we should clear out the latest
+	 * and old key values. Why would we keep advertising them if
+	 * they've been deleted and the key server has been changed?
+	 */
+	os_free(sm->oki);
+	sm->oki = NULL;
+	sm->otx = FALSE;
+	sm->orx = FALSE;
+	sm->oan = 0;
+	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+				       sm->otx, sm->orx);
+	os_free(sm->lki);
+	sm->lki = NULL;
+	sm->lrx = FALSE;
+	sm->ltx = FALSE;
+	sm->lan = 0;
+	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+					  sm->ltx, sm->lrx);
 }
 
 
@@ -212,18 +230,6 @@
 SM_STATE(CP, RECEIVE)
 {
 	SM_ENTRY(CP, RECEIVE);
-	/* RECEIVE state machine not keep with Figure 12-2 in
-	 * IEEE Std 802.1X-2010 */
-	if (sm->oki) {
-		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
-		os_free(sm->oki);
-	}
-	sm->oki = sm->lki;
-	sm->oan = sm->lan;
-	sm->otx = sm->ltx;
-	sm->orx = sm->lrx;
-	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
-				       sm->otx, sm->orx);
 
 	sm->lki = os_malloc(sizeof(*sm->lki));
 	if (!sm->lki) {
@@ -313,24 +319,29 @@
 	sm->lki = NULL;
 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
 					  sm->ltx, sm->lrx);
-	sm->new_sak = FALSE;
 }
 
 
 SM_STATE(CP, RETIRE)
 {
 	SM_ENTRY(CP, RETIRE);
-	/* RETIRE state machine not keep with Figure 12-2 in
-	 * IEEE Std 802.1X-2010 */
 	if (sm->oki) {
 		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
 		os_free(sm->oki);
 		sm->oki = NULL;
 	}
-	sm->orx = FALSE;
-	sm->otx = FALSE;
+	sm->oki = sm->lki;
+	sm->otx = sm->ltx;
+	sm->orx = sm->lrx;
+	sm->oan = sm->lan;
 	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
 				       sm->otx, sm->orx);
+	sm->lki = NULL;
+	sm->ltx = FALSE;
+	sm->lrx = FALSE;
+	sm->lan = 0;
+	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+					  sm->ltx, sm->lrx);
 }
 
 
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index b4455c8..3dbd3ca 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -1085,7 +1085,17 @@
 				wpa_printf(MSG_DEBUG,
 					   "KaY: My MI - received MN %u, most recently transmitted MN %u",
 					   mn, participant->mn);
-				if (mn == participant->mn)
+				/* IEEE Std 802.1X-2010 is not exactly clear
+				 * which values of MN should be accepted here.
+				 * It uses "acceptably recent MN" language
+				 * without defining what would be acceptable
+				 * recent. For now, allow the last two used MN
+				 * values (i.e., peer having copied my MI,MN
+				 * from either of the last two MKPDUs that I
+				 * have sent). */
+				if (mn == participant->mn ||
+				    (participant->mn > 1 &&
+				     mn == participant->mn - 1))
 					return TRUE;
 			}
 		}
@@ -1277,7 +1287,7 @@
 	struct ieee802_1x_mka_sak_use_body *body;
 	struct ieee802_1x_kay *kay = participant->kay;
 	unsigned int length;
-	u32 pn = 1;
+	u32 olpn, llpn;
 
 	length = ieee802_1x_mka_get_sak_use_length(participant);
 	body = wpabuf_put(buf, length);
@@ -1297,18 +1307,31 @@
 
 	/* data delay protect */
 	body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME;
-	/* lowest accept packet number */
-	pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
-	if (pn > kay->pn_exhaustion) {
-		wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
-		if (participant->is_key_server)
-			participant->new_sak = TRUE;
+	/* lowest accept packet numbers */
+	olpn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
+	body->olpn = host_to_be32(olpn);
+	llpn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
+	body->llpn = host_to_be32(llpn);
+	if (participant->is_key_server) {
+		/* The CP will spend most of it's time in RETIRE where only
+		 * the old key is populated. Therefore we should be checking
+		 * the OLPN most of the time.
+		 */
+		if (participant->lrx) {
+			if (llpn > kay->pn_exhaustion) {
+				wpa_printf(MSG_WARNING,
+					   "KaY: My LLPN exhaustion");
+				participant->new_sak = TRUE;
+			}
+		} else {
+			if (olpn > kay->pn_exhaustion) {
+				wpa_printf(MSG_WARNING,
+					   "KaY: My OLPN exhaustion");
+				participant->new_sak = TRUE;
+			}
+		}
 	}
 
-	body->llpn = host_to_be32(pn);
-	pn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
-	body->olpn = host_to_be32(pn);
-
 	/* plain tx, plain rx */
 	body->ptx = !kay->macsec_protect;
 	body->prx = kay->macsec_validate != Strict;
@@ -1358,15 +1381,12 @@
 	struct ieee802_1x_mka_hdr *hdr;
 	struct ieee802_1x_mka_sak_use_body *body;
 	struct ieee802_1x_kay_peer *peer;
-	struct receive_sc *rxsc;
-	struct receive_sa *rxsa;
 	struct data_key *sa_key = NULL;
 	size_t body_len;
 	struct ieee802_1x_mka_ki ki;
 	u32 lpn;
-	Boolean all_receiving;
-	Boolean found;
 	struct ieee802_1x_kay *kay = participant->kay;
+	u32 olpn, llpn;
 
 	if (!participant->principal) {
 		wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
@@ -1407,46 +1427,6 @@
 
 	if (body->ptx)
 		wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
-
-	/* check latest key is valid */
-	if (body->ltx || body->lrx) {
-		found = FALSE;
-		os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
-		ki.kn = be_to_host32(body->lkn);
-		dl_list_for_each(sa_key, &participant->sak_list,
-				 struct data_key, list) {
-			if (is_ki_equal(&sa_key->key_identifier, &ki)) {
-				found = TRUE;
-				break;
-			}
-		}
-		if (!found) {
-			wpa_printf(MSG_INFO, "KaY: Latest key is invalid");
-			return -1;
-		}
-		if (os_memcmp(participant->lki.mi, body->lsrv_mi,
-			      sizeof(participant->lki.mi)) == 0 &&
-		    be_to_host32(body->lkn) == participant->lki.kn &&
-		    body->lan == participant->lan) {
-			peer->sak_used = TRUE;
-		}
-		if (body->ltx && peer->is_key_server) {
-			ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE);
-			ieee802_1x_cp_sm_step(kay->cp);
-		}
-	}
-
-	/* check old key is valid (but only if we remember our old key) */
-	if (participant->oki.kn != 0 && (body->otx || body->orx)) {
-		if (os_memcmp(participant->oki.mi, body->osrv_mi,
-			      sizeof(participant->oki.mi)) != 0 ||
-		    be_to_host32(body->okn) != participant->oki.kn ||
-		    body->oan != participant->oan) {
-			wpa_printf(MSG_WARNING, "KaY: Old key is invalid");
-			return -1;
-		}
-	}
-
 	/* TODO: how to set the MACsec hardware when delay_protect is true */
 	if (body->delay_protect &&
 	    (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) {
@@ -1455,65 +1435,132 @@
 		return -1;
 	}
 
-	/* check all live peer have used the sak for receiving sa */
-	all_receiving = TRUE;
-	dl_list_for_each(peer, &participant->live_peers,
-			 struct ieee802_1x_kay_peer, list) {
-		if (!peer->sak_used) {
-			all_receiving = FALSE;
-			break;
-		}
-	}
-	if (all_receiving) {
-		participant->to_dist_sak = FALSE;
-		ieee802_1x_cp_set_allreceiving(kay->cp, TRUE);
-		ieee802_1x_cp_sm_step(kay->cp);
+	olpn = be_to_host32(body->olpn);
+	llpn = be_to_host32(body->llpn);
+
+	/* Our most recent distributed key should be the first in the list.
+	 * If it doesn't exist then we can't really do anything.
+	 * Be lenient and don't return error here as there are legitimate cases
+	 * where this can happen such as when a new participant joins the CA and
+	 * the first frame it receives can have a SAKuse but not distSAK.
+	 */
+	sa_key = dl_list_first(&participant->sak_list, struct data_key, list);
+	if (!sa_key) {
+		wpa_printf(MSG_INFO,
+			   "KaY: We don't have a latest distributed key - ignore SAK use");
+		return 0;
 	}
 
-	/* if I'm key server, and detects peer member pn exhaustion, rekey. */
-	lpn = be_to_host32(body->llpn);
-	if (lpn > kay->pn_exhaustion) {
-		if (participant->is_key_server) {
-			participant->new_sak = TRUE;
-			wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
-		}
+	/* The peer's most recent key will be the "latest key" if it is present
+	 * otherwise it will be the "old key" if in the RETIRE state.
+	 */
+	if (body->lrx) {
+		os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
+		ki.kn = be_to_host32(body->lkn);
+		lpn = llpn;
+	} else {
+		os_memcpy(ki.mi, body->osrv_mi, sizeof(ki.mi));
+		ki.kn = be_to_host32(body->okn);
+		lpn = olpn;
 	}
 
-	if (sa_key)
-		sa_key->next_pn = lpn;
-	found = FALSE;
-	dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc,
-			 list) {
-		dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa,
-				 list) {
-			if (sa_key && rxsa->pkey == sa_key) {
-				found = TRUE;
+	/* If the most recent distributed keys don't agree then someone is out
+	 * of sync. Perhaps non key server hasn't processed the most recent
+	 * distSAK yet and the key server is processing an old packet after it
+	 * has done distSAK. Be lenient and don't return error in this
+	 * particular case; otherwise, the key server will reset its MI and
+	 * cause a traffic disruption which is really undesired for a simple
+	 * timing issue.
+	 */
+	if (!is_ki_equal(&sa_key->key_identifier, &ki)) {
+		wpa_printf(MSG_INFO,
+			   "KaY: Distributed keys don't match - ignore SAK use");
+		return 0;
+	}
+	sa_key->next_pn = lpn;
+
+	/* The key server must check that all peers are using the most recent
+	 * distributed key. Non key servers must check if the key server is
+	 * transmitting.
+	 */
+	if (participant->is_key_server) {
+		struct ieee802_1x_kay_peer *peer_iter;
+		Boolean all_receiving = TRUE;
+
+		/* Distributed keys are equal from above comparison. */
+		peer->sak_used = TRUE;
+
+		dl_list_for_each(peer_iter, &participant->live_peers,
+				 struct ieee802_1x_kay_peer, list) {
+			if (!peer_iter->sak_used) {
+				all_receiving = FALSE;
 				break;
 			}
 		}
-		if (found)
-			break;
-	}
-	if (!found) {
-		wpa_printf(MSG_WARNING, "KaY: Can't find rxsa");
-		return -1;
+		if (all_receiving) {
+			participant->to_dist_sak = FALSE;
+			ieee802_1x_cp_set_allreceiving(kay->cp, TRUE);
+			ieee802_1x_cp_sm_step(kay->cp);
+		}
+	} else if (peer->is_key_server) {
+		if (body->ltx) {
+			ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE);
+			ieee802_1x_cp_sm_step(kay->cp);
+		}
 	}
 
+	/* If I'm key server, and detects peer member PN exhaustion, rekey.
+	 * We only need to check the PN of the most recent distributed key. This
+	 * could be the peer's "latest" or "old" key depending on its current
+	 * state. If both "old" and "latest" keys are present then the "old" key
+	 * has already been exhausted.
+	 */
+	if (participant->is_key_server && lpn > kay->pn_exhaustion) {
+		participant->new_sak = TRUE;
+		wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
+	}
+
+	/* Get the associated RX SAs of the keys for delay protection since both
+	 * can be in use. Delay protect window (communicated via MKA) is tighter
+	 * than SecY's current replay protect window, so tell SecY the new (and
+	 * higher) lpn.
+	 */
 	if (body->delay_protect) {
-		secy_get_receive_lowest_pn(participant->kay, rxsa);
-		if (lpn > rxsa->lowest_pn) {
-			/* Delay protect window (communicated via MKA) is
-			 * tighter than SecY's current replay protect window,
-			 * so tell SecY the new (and higher) lpn. */
-			rxsa->lowest_pn = lpn;
-			secy_set_receive_lowest_pn(participant->kay, rxsa);
-			wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn);
+		struct receive_sc *rxsc;
+		struct receive_sa *rxsa;
+		Boolean found = FALSE;
+
+		dl_list_for_each(rxsc, &participant->rxsc_list,
+				 struct receive_sc, list) {
+			dl_list_for_each(rxsa, &rxsc->sa_list,
+					 struct receive_sa, list) {
+				if (sa_key && rxsa->pkey == sa_key) {
+					found = TRUE;
+					break;
+				}
+			}
+			if (found)
+				break;
 		}
-		/* FIX: Delay protection for olpn not implemented.
-		 * Note that Old Key is only active for MKA_SAK_RETIRE_TIME
-		 * (3 seconds) and delay protection does allow PN's within
-		 * a 2 seconds window, so olpn would be a lot of work for
-		 * just 1 second's worth of protection. */
+		if (found) {
+			secy_get_receive_lowest_pn(participant->kay, rxsa);
+			if (lpn > rxsa->lowest_pn) {
+				rxsa->lowest_pn = lpn;
+				secy_set_receive_lowest_pn(participant->kay,
+							   rxsa);
+				wpa_printf(MSG_DEBUG,
+					   "KaY: update dist LPN=0x%x", lpn);
+			}
+		}
+
+		/* FIX: Delay protection for the SA being replaced is not
+		 * implemented. Note that this key will be active for at least
+		 * MKA_SAK_RETIRE_TIME (3 seconds) but could be longer depending
+		 * on how long it takes to get from RECEIVE to TRANSMITTING or
+		 * if going via ABANDON. Delay protection does allow PNs within
+		 * a 2 second window, so getting PN would be a lot of work for
+		 * just 1 second's worth of protection.
+		 */
 	}
 
 	return 0;
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index b621ada..c17e53b 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -161,140 +161,10 @@
 	 */
 	int num_sess;
 
-	/**
-	 * eap_sim_db_priv - EAP-SIM/AKA database context
-	 *
-	 * This is passed to the EAP-SIM/AKA server implementation as a
-	 * callback context.
-	 */
-	void *eap_sim_db_priv;
-
-	/**
-	 * ssl_ctx - TLS context
-	 *
-	 * This is passed to the EAP server implementation as a callback
-	 * context for TLS operations.
-	 */
-	void *ssl_ctx;
-
-	/**
-	 * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
-	 *
-	 * This parameter is used to set a key for EAP-FAST to encrypt the
-	 * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
-	 * set, must point to a 16-octet key.
-	 */
-	u8 *pac_opaque_encr_key;
-
-	/**
-	 * eap_fast_a_id - EAP-FAST authority identity (A-ID)
-	 *
-	 * If EAP-FAST is not used, this can be set to %NULL. In theory, this
-	 * is a variable length field, but due to some existing implementations
-	 * requiring A-ID to be 16 octets in length, it is recommended to use
-	 * that length for the field to provide interoperability with deployed
-	 * peer implementations.
-	 */
-	u8 *eap_fast_a_id;
-
-	/**
-	 * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
-	 */
-	size_t eap_fast_a_id_len;
-
-	/**
-	 * eap_fast_a_id_info - EAP-FAST authority identifier information
-	 *
-	 * This A-ID-Info contains a user-friendly name for the A-ID. For
-	 * example, this could be the enterprise and server names in
-	 * human-readable format. This field is encoded as UTF-8. If EAP-FAST
-	 * is not used, this can be set to %NULL.
-	 */
-	char *eap_fast_a_id_info;
-
-	/**
-	 * eap_fast_prov - EAP-FAST provisioning modes
-	 *
-	 * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
-	 * 2 = only authenticated provisioning allowed, 3 = both provisioning
-	 * modes allowed.
-	 */
-	int eap_fast_prov;
-
-	/**
-	 * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
-	 *
-	 * This is the hard limit on how long a provisioned PAC-Key can be
-	 * used.
-	 */
-	int pac_key_lifetime;
-
-	/**
-	 * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
-	 *
-	 * This is a soft limit on the PAC-Key. The server will automatically
-	 * generate a new PAC-Key when this number of seconds (or fewer) of the
-	 * lifetime remains.
-	 */
-	int pac_key_refresh_time;
-
-	/**
-	 * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
-	 *
-	 * This controls whether the protected success/failure indication
-	 * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
-	 */
-	int eap_sim_aka_result_ind;
-
-	/**
-	 * tnc - Trusted Network Connect (TNC)
-	 *
-	 * This controls whether TNC is enabled and will be required before the
-	 * peer is allowed to connect. Note: This is only used with EAP-TTLS
-	 * and EAP-FAST. If any other EAP method is enabled, the peer will be
-	 * allowed to connect without TNC.
-	 */
-	int tnc;
-
-	/**
-	 * pwd_group - The D-H group assigned for EAP-pwd
-	 *
-	 * If EAP-pwd is not used it can be set to zero.
-	 */
-	u16 pwd_group;
-
-	/**
-	 * server_id - Server identity
-	 */
-	const char *server_id;
-
-	/**
-	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
-	 *
-	 * This controls whether the authentication server derives ERP key
-	 * hierarchy (rRK and rIK) from full EAP authentication and allows
-	 * these keys to be used to perform ERP to derive rMSK instead of full
-	 * EAP authentication to derive MSK.
-	 */
-	int erp;
-
 	const char *erp_domain;
 
 	struct dl_list erp_keys; /* struct eap_server_erp_key */
 
-	unsigned int tls_session_lifetime;
-
-	unsigned int tls_flags;
-
-	/**
-	 * wps - Wi-Fi Protected Setup context
-	 *
-	 * If WPS is used with an external RADIUS server (which is quite
-	 * unlikely configuration), this is used to provide a pointer to WPS
-	 * context data. Normally, this can be set to %NULL.
-	 */
-	struct wps_context *wps;
-
 	/**
 	 * ipv6 - Whether to enable IPv6 support in the RADIUS server
 	 */
@@ -346,11 +216,6 @@
 	 */
 	size_t eap_req_id_text_len;
 
-	/*
-	 * msg_ctx - Context data for wpa_msg() calls
-	 */
-	void *msg_ctx;
-
 #ifdef CONFIG_RADIUS_TEST
 	char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
@@ -364,6 +229,8 @@
 #ifdef CONFIG_SQLITE
 	sqlite3 *db;
 #endif /* CONFIG_SQLITE */
+
+	const struct eap_config *eap_cfg;
 };
 
 
@@ -614,7 +481,7 @@
 #ifdef CONFIG_TESTING_OPTIONS
 static void radius_server_testing_options_tls(struct radius_session *sess,
 					      const char *tls,
-					      struct eap_config *eap_conf)
+					      struct eap_session_data *eap_conf)
 {
 	int test = atoi(tls);
 
@@ -659,7 +526,7 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 
 static void radius_server_testing_options(struct radius_session *sess,
-					  struct eap_config *eap_conf)
+					  struct eap_session_data *eap_conf)
 {
 #ifdef CONFIG_TESTING_OPTIONS
 	const char *pos;
@@ -702,7 +569,7 @@
 	size_t user_len, id_len;
 	int res;
 	struct radius_session *sess;
-	struct eap_config eap_conf;
+	struct eap_session_data eap_sess;
 	struct eap_user *tmp;
 
 	RADIUS_DEBUG("Creating a new session");
@@ -720,7 +587,7 @@
 
 	res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp);
 #ifdef CONFIG_ERP
-	if (res != 0 && data->erp) {
+	if (res != 0 && data->eap_cfg->erp) {
 		char *username;
 
 		username = os_zalloc(user_len + 1);
@@ -779,31 +646,10 @@
 
 	srv_log(sess, "New session created");
 
-	os_memset(&eap_conf, 0, sizeof(eap_conf));
-	eap_conf.ssl_ctx = data->ssl_ctx;
-	eap_conf.msg_ctx = data->msg_ctx;
-	eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
-	eap_conf.backend_auth = TRUE;
-	eap_conf.eap_server = 1;
-	eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key;
-	eap_conf.eap_fast_a_id = data->eap_fast_a_id;
-	eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len;
-	eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info;
-	eap_conf.eap_fast_prov = data->eap_fast_prov;
-	eap_conf.pac_key_lifetime = data->pac_key_lifetime;
-	eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
-	eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
-	eap_conf.tnc = data->tnc;
-	eap_conf.wps = data->wps;
-	eap_conf.pwd_group = data->pwd_group;
-	eap_conf.server_id = (const u8 *) data->server_id;
-	eap_conf.server_id_len = os_strlen(data->server_id);
-	eap_conf.erp = data->erp;
-	eap_conf.tls_session_lifetime = data->tls_session_lifetime;
-	eap_conf.tls_flags = data->tls_flags;
-	radius_server_testing_options(sess, &eap_conf);
+	os_memset(&eap_sess, 0, sizeof(eap_sess));
+	radius_server_testing_options(sess, &eap_sess);
 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
-				       &eap_conf);
+				       data->eap_cfg, &eap_sess);
 	if (sess->eap == NULL) {
 		RADIUS_DEBUG("Failed to initialize EAP state machine for the "
 			     "new session");
@@ -1136,6 +982,13 @@
 					      len)) {
 			RADIUS_DEBUG("Failed to add MPPE key attributes");
 		}
+
+		if (sess->eap_if->eapSessionId &&
+		    !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME,
+					 sess->eap_if->eapSessionId,
+					 sess->eap_if->eapSessionIdLen)) {
+			RADIUS_DEBUG("Failed to add EAP-Key-Name attribute");
+		}
 	}
 
 #ifdef CONFIG_HS20
@@ -2348,71 +2201,52 @@
 	if (data == NULL)
 		return NULL;
 
+	data->eap_cfg = conf->eap_cfg;
+	data->auth_sock = -1;
+	data->acct_sock = -1;
 	dl_list_init(&data->erp_keys);
 	os_get_reltime(&data->start_time);
 	data->conf_ctx = conf->conf_ctx;
-	data->eap_sim_db_priv = conf->eap_sim_db_priv;
-	data->ssl_ctx = conf->ssl_ctx;
-	data->msg_ctx = conf->msg_ctx;
+	conf->eap_cfg->backend_auth = TRUE;
+	conf->eap_cfg->eap_server = 1;
 	data->ipv6 = conf->ipv6;
-	if (conf->pac_opaque_encr_key) {
-		data->pac_opaque_encr_key = os_malloc(16);
-		if (data->pac_opaque_encr_key) {
-			os_memcpy(data->pac_opaque_encr_key,
-				  conf->pac_opaque_encr_key, 16);
-		}
-	}
-	if (conf->eap_fast_a_id) {
-		data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
-		if (data->eap_fast_a_id) {
-			os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id,
-				  conf->eap_fast_a_id_len);
-			data->eap_fast_a_id_len = conf->eap_fast_a_id_len;
-		}
-	}
-	if (conf->eap_fast_a_id_info)
-		data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
-	data->eap_fast_prov = conf->eap_fast_prov;
-	data->pac_key_lifetime = conf->pac_key_lifetime;
-	data->pac_key_refresh_time = conf->pac_key_refresh_time;
 	data->get_eap_user = conf->get_eap_user;
-	data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
-	data->tnc = conf->tnc;
-	data->wps = conf->wps;
-	data->pwd_group = conf->pwd_group;
-	data->server_id = conf->server_id;
 	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) {
-			os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
-				  conf->eap_req_id_text_len);
-			data->eap_req_id_text_len = conf->eap_req_id_text_len;
-		}
+		if (!data->eap_req_id_text)
+			goto fail;
+		os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
+			  conf->eap_req_id_text_len);
+		data->eap_req_id_text_len = conf->eap_req_id_text_len;
 	}
-	data->erp = conf->erp;
 	data->erp_domain = conf->erp_domain;
-	data->tls_session_lifetime = conf->tls_session_lifetime;
-	data->tls_flags = conf->tls_flags;
 
 	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)
+	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)
+	if (conf->t_c_server_url) {
 		data->t_c_server_url = os_strdup(conf->t_c_server_url);
+		if (!data->t_c_server_url)
+			goto fail;
+	}
 
 #ifdef CONFIG_SQLITE
 	if (conf->sqlite_file) {
 		if (sqlite3_open(conf->sqlite_file, &data->db)) {
 			RADIUS_ERROR("Could not open SQLite file '%s'",
 				     conf->sqlite_file);
-			radius_server_deinit(data);
-			return NULL;
+			goto fail;
 		}
 	}
 #endif /* CONFIG_SQLITE */
@@ -2426,8 +2260,7 @@
 						   conf->ipv6);
 	if (data->clients == NULL) {
 		wpa_printf(MSG_ERROR, "No RADIUS clients configured");
-		radius_server_deinit(data);
-		return NULL;
+		goto fail;
 	}
 
 #ifdef CONFIG_IPV6
@@ -2438,14 +2271,12 @@
 	data->auth_sock = radius_server_open_socket(conf->auth_port);
 	if (data->auth_sock < 0) {
 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
-		radius_server_deinit(data);
-		return NULL;
+		goto fail;
 	}
 	if (eloop_register_read_sock(data->auth_sock,
 				     radius_server_receive_auth,
 				     data, NULL)) {
-		radius_server_deinit(data);
-		return NULL;
+		goto fail;
 	}
 
 	if (conf->acct_port) {
@@ -2458,20 +2289,20 @@
 		data->acct_sock = radius_server_open_socket(conf->acct_port);
 		if (data->acct_sock < 0) {
 			wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server");
-			radius_server_deinit(data);
-			return NULL;
+			goto fail;
 		}
 		if (eloop_register_read_sock(data->acct_sock,
 					     radius_server_receive_acct,
-					     data, NULL)) {
-			radius_server_deinit(data);
-			return NULL;
-		}
+					     data, NULL))
+			goto fail;
 	} else {
 		data->acct_sock = -1;
 	}
 
 	return data;
+fail:
+	radius_server_deinit(data);
+	return NULL;
 }
 
 
@@ -2514,9 +2345,6 @@
 
 	radius_server_free_clients(data, data->clients);
 
-	os_free(data->pac_opaque_encr_key);
-	os_free(data->eap_fast_a_id);
-	os_free(data->eap_fast_a_id_info);
 	os_free(data->eap_req_id_text);
 #ifdef CONFIG_RADIUS_TEST
 	os_free(data->dump_msk_file);
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 53728f9..43192e5 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -51,138 +51,8 @@
 	 */
 	void *conf_ctx;
 
-	/**
-	 * eap_sim_db_priv - EAP-SIM/AKA database context
-	 *
-	 * This is passed to the EAP-SIM/AKA server implementation as a
-	 * callback context.
-	 */
-	void *eap_sim_db_priv;
-
-	/**
-	 * ssl_ctx - TLS context
-	 *
-	 * This is passed to the EAP server implementation as a callback
-	 * context for TLS operations.
-	 */
-	void *ssl_ctx;
-
-	/**
-	 * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
-	 *
-	 * This parameter is used to set a key for EAP-FAST to encrypt the
-	 * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
-	 * set, must point to a 16-octet key.
-	 */
-	u8 *pac_opaque_encr_key;
-
-	/**
-	 * eap_fast_a_id - EAP-FAST authority identity (A-ID)
-	 *
-	 * If EAP-FAST is not used, this can be set to %NULL. In theory, this
-	 * is a variable length field, but due to some existing implementations
-	 * requiring A-ID to be 16 octets in length, it is recommended to use
-	 * that length for the field to provide interoperability with deployed
-	 * peer implementations.
-	 */
-	u8 *eap_fast_a_id;
-
-	/**
-	 * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
-	 */
-	size_t eap_fast_a_id_len;
-
-	/**
-	 * eap_fast_a_id_info - EAP-FAST authority identifier information
-	 *
-	 * This A-ID-Info contains a user-friendly name for the A-ID. For
-	 * example, this could be the enterprise and server names in
-	 * human-readable format. This field is encoded as UTF-8. If EAP-FAST
-	 * is not used, this can be set to %NULL.
-	 */
-	char *eap_fast_a_id_info;
-
-	/**
-	 * eap_fast_prov - EAP-FAST provisioning modes
-	 *
-	 * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
-	 * 2 = only authenticated provisioning allowed, 3 = both provisioning
-	 * modes allowed.
-	 */
-	int eap_fast_prov;
-
-	/**
-	 * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
-	 *
-	 * This is the hard limit on how long a provisioned PAC-Key can be
-	 * used.
-	 */
-	int pac_key_lifetime;
-
-	/**
-	 * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
-	 *
-	 * This is a soft limit on the PAC-Key. The server will automatically
-	 * generate a new PAC-Key when this number of seconds (or fewer) of the
-	 * lifetime remains.
-	 */
-	int pac_key_refresh_time;
-
-	/**
-	 * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
-	 *
-	 * This controls whether the protected success/failure indication
-	 * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
-	 */
-	int eap_sim_aka_result_ind;
-
-	/**
-	 * tnc - Trusted Network Connect (TNC)
-	 *
-	 * This controls whether TNC is enabled and will be required before the
-	 * peer is allowed to connect. Note: This is only used with EAP-TTLS
-	 * and EAP-FAST. If any other EAP method is enabled, the peer will be
-	 * allowed to connect without TNC.
-	 */
-	int tnc;
-
-	/**
-	 * pwd_group - EAP-pwd D-H group
-	 *
-	 * This is used to select which D-H group to use with EAP-pwd.
-	 */
-	u16 pwd_group;
-
-	/**
-	 * server_id - Server identity
-	 */
-	const char *server_id;
-
-	/**
-	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
-	 *
-	 * This controls whether the authentication server derives ERP key
-	 * hierarchy (rRK and rIK) from full EAP authentication and allows
-	 * these keys to be used to perform ERP to derive rMSK instead of full
-	 * EAP authentication to derive MSK.
-	 */
-	int erp;
-
 	const char *erp_domain;
 
-	unsigned int tls_session_lifetime;
-
-	unsigned int tls_flags;
-
-	/**
-	 * wps - Wi-Fi Protected Setup context
-	 *
-	 * If WPS is used with an external RADIUS server (which is quite
-	 * unlikely configuration), this is used to provide a pointer to WPS
-	 * context data. Normally, this can be set to %NULL.
-	 */
-	struct wps_context *wps;
-
 	/**
 	 * ipv6 - Whether to enable IPv6 support in the RADIUS server
 	 */
@@ -222,11 +92,6 @@
 	 */
 	size_t eap_req_id_text_len;
 
-	/*
-	 * msg_ctx - Context data for wpa_msg() calls
-	 */
-	void *msg_ctx;
-
 #ifdef CONFIG_RADIUS_TEST
 	const char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
@@ -236,6 +101,8 @@
 	char *hs20_sim_provisioning_url;
 
 	char *t_c_server_url;
+
+	struct eap_config *eap_cfg;
 };
 
 
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index c2d81f2..eea0efb 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -8,7 +8,6 @@
 
 include ../lib.rules
 
-CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211R
 CFLAGS += -DCONFIG_TDLS
 CFLAGS += -DCONFIG_WNM
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index d720f7b..fa5215c 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -14,6 +14,8 @@
 #include "wpa.h"
 #include "wpa_i.h"
 #include "pmksa_cache.h"
+#include "wpa_supplicant_i.h"
+#include "notify.h"
 
 #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
 
@@ -265,6 +267,7 @@
 	wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
 		   " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
 		   entry->network_ctx, entry->akmp);
+	wpas_notify_pmk_cache_added((struct wpa_supplicant *)pmksa->sm->ctx->ctx, entry);
 	wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
 			 entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
 			 entry->pmk, entry->pmk_len);
@@ -277,7 +280,7 @@
  * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  * @network_ctx: Network configuration context or %NULL to flush all entries
- * @pmk: PMK to match for or %NYLL to match all PMKs
+ * @pmk: PMK to match for or %NULL to match all PMKs
  * @pmk_len: PMK length
  */
 void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 66a8643..892eece 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -305,6 +305,9 @@
 #endif /* CONFIG_IEEE80211R */
 	} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
 		int res, pmk_len;
+#ifdef CONFIG_IEEE80211R
+		u8 buf[2 * PMK_LEN];
+#endif /* CONFIG_IEEE80211R */
 
 		if (wpa_key_mgmt_sha384(sm->key_mgmt))
 			pmk_len = PMK_LEN_SUITE_B_192;
@@ -320,24 +323,42 @@
 				res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
 				pmk_len = 16;
 			}
-		} else {
-#ifdef CONFIG_IEEE80211R
-			u8 buf[2 * PMK_LEN];
-			if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
-			{
-				if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
-					os_memcpy(sm->xxkey, buf,
-						  SHA384_MAC_LEN);
-					sm->xxkey_len = SHA384_MAC_LEN;
-				} else {
-					os_memcpy(sm->xxkey, buf + PMK_LEN,
-						  PMK_LEN);
-					sm->xxkey_len = PMK_LEN;
-				}
-				os_memset(buf, 0, sizeof(buf));
-			}
-#endif /* CONFIG_IEEE80211R */
 		}
+#ifdef CONFIG_IEEE80211R
+		if (res == 0 &&
+		    eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) {
+			if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+				os_memcpy(sm->xxkey, buf, SHA384_MAC_LEN);
+				sm->xxkey_len = SHA384_MAC_LEN;
+			} else {
+				os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
+				sm->xxkey_len = PMK_LEN;
+			}
+			forced_memzero(buf, sizeof(buf));
+			if (sm->proto == WPA_PROTO_RSN &&
+			    wpa_key_mgmt_ft(sm->key_mgmt)) {
+				struct rsn_pmksa_cache_entry *sa = NULL;
+				const u8 *fils_cache_id = NULL;
+
+#ifdef CONFIG_FILS
+				if (sm->fils_cache_id_set)
+					fils_cache_id = sm->fils_cache_id;
+#endif /* CONFIG_FILS */
+				wpa_hexdump_key(MSG_DEBUG,
+						"FT: Cache XXKey/MPMK",
+						sm->xxkey, sm->xxkey_len);
+				sa = pmksa_cache_add(sm->pmksa,
+						     sm->xxkey, sm->xxkey_len,
+						     NULL, NULL, 0,
+						     src_addr, sm->own_addr,
+						     sm->network_ctx,
+						     sm->key_mgmt,
+						     fils_cache_id);
+				if (!sm->cur_pmksa)
+					sm->cur_pmksa = sa;
+			}
+		}
+#endif /* CONFIG_IEEE80211R */
 		if (res == 0) {
 			struct rsn_pmksa_cache_entry *sa = NULL;
 			const u8 *fils_cache_id = NULL;
@@ -384,6 +405,11 @@
 
 			if (!sm->cur_pmksa)
 				sm->cur_pmksa = sa;
+#ifdef CONFIG_IEEE80211R
+		} else if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->ft_protocol) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: Continue 4-way handshake without PMK/PMKID for association using FT protocol");
+#endif /* CONFIG_IEEE80211R */
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Failed to get master session key from "
@@ -462,6 +488,8 @@
 	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
 		int res;
 
+		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE before FT processing",
+			    wpa_ie, wpa_ie_len);
 		/*
 		 * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and
 		 * FTIE from (Re)Association Response.
@@ -477,8 +505,14 @@
 			os_free(rsn_ie_buf);
 			return -1;
 		}
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: WPA IE after PMKID[PMKR1Name] addition into RSNE",
+			    rsn_ie_buf, wpa_ie_len);
 
 		if (sm->assoc_resp_ies) {
+			wpa_hexdump(MSG_DEBUG, "WPA: Add assoc_resp_ies",
+				    sm->assoc_resp_ies,
+				    sm->assoc_resp_ies_len);
 			os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
 				  sm->assoc_resp_ies_len);
 			wpa_ie_len += sm->assoc_resp_ies_len;
@@ -623,57 +657,57 @@
 		os_memcpy(buf, &ptk->tk[16], 8);
 		os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
 		os_memcpy(&ptk->tk[24], buf, 8);
-		os_memset(buf, 0, sizeof(buf));
+		forced_memzero(buf, sizeof(buf));
 	}
 	sm->tptk_set = 1;
 
 	kde = sm->assoc_wpa_ie;
 	kde_len = sm->assoc_wpa_ie_len;
+	kde_buf = os_malloc(kde_len +
+			    2 + RSN_SELECTOR_LEN + 3 +
+			    sm->assoc_rsnxe_len +
+			    2 + RSN_SELECTOR_LEN + 1);
+	if (!kde_buf)
+		goto failed;
+	os_memcpy(kde_buf, kde, kde_len);
+	kde = kde_buf;
 
 #ifdef CONFIG_OCV
 	if (wpa_sm_ocv_enabled(sm)) {
 		struct wpa_channel_info ci;
 		u8 *pos;
 
+		pos = kde + kde_len;
 		if (wpa_sm_channel_info(sm, &ci) != 0) {
 			wpa_printf(MSG_WARNING,
 				   "Failed to get channel info for OCI element in EAPOL-Key 2/4");
 			goto failed;
 		}
 
-		kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3);
-		if (!kde_buf) {
-			wpa_printf(MSG_WARNING,
-				   "Failed to allocate memory for KDE with OCI in EAPOL-Key 2/4");
-			goto failed;
-		}
-
-		os_memcpy(kde_buf, kde, kde_len);
-		kde = kde_buf;
-		pos = kde + kde_len;
 		if (ocv_insert_oci_kde(&ci, &pos) < 0)
 			goto failed;
 		kde_len = pos - kde;
 	}
 #endif /* CONFIG_OCV */
 
+	if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) {
+		os_memcpy(kde + kde_len, sm->assoc_rsnxe, sm->assoc_rsnxe_len);
+		kde_len += sm->assoc_rsnxe_len;
+	}
+
 #ifdef CONFIG_P2P
 	if (sm->p2p) {
-		kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
-		if (kde_buf) {
-			u8 *pos;
-			wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE "
-				   "into EAPOL-Key 2/4");
-			os_memcpy(kde_buf, kde, kde_len);
-			kde = kde_buf;
-			pos = kde + kde_len;
-			*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-			*pos++ = RSN_SELECTOR_LEN + 1;
-			RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
-			pos += RSN_SELECTOR_LEN;
-			*pos++ = 0x01;
-			kde_len = pos - kde;
-		}
+		u8 *pos;
+
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Add IP Address Request KDE into EAPOL-Key 2/4");
+		pos = kde + kde_len;
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = RSN_SELECTOR_LEN + 1;
+		RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
+		pos += RSN_SELECTOR_LEN;
+		*pos++ = 0x01;
+		kde_len = pos - kde;
 	}
 #endif /* CONFIG_P2P */
 
@@ -897,7 +931,7 @@
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Failed to set GTK to the driver "
 				"(Group only)");
-			os_memset(gtk_buf, 0, sizeof(gtk_buf));
+			forced_memzero(gtk_buf, sizeof(gtk_buf));
 			return -1;
 		}
 	} else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
@@ -907,10 +941,10 @@
 			"WPA: Failed to set GTK to "
 			"the driver (alg=%d keylen=%d keyidx=%d)",
 			gd->alg, gd->gtk_len, gd->keyidx);
-		os_memset(gtk_buf, 0, sizeof(gtk_buf));
+		forced_memzero(gtk_buf, sizeof(gtk_buf));
 		return -1;
 	}
-	os_memset(gtk_buf, 0, sizeof(gtk_buf));
+	forced_memzero(gtk_buf, sizeof(gtk_buf));
 
 	if (wnm_sleep) {
 		sm->gtk_wnm_sleep.gtk_len = gd->gtk_len;
@@ -1016,16 +1050,15 @@
 	     wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"RSN: Failed to install GTK");
-		os_memset(&gd, 0, sizeof(gd));
+		forced_memzero(&gd, sizeof(gd));
 		return -1;
 	}
-	os_memset(&gd, 0, sizeof(gd));
+	forced_memzero(&gd, sizeof(gd));
 
 	return 0;
 }
 
 
-#ifdef CONFIG_IEEE80211W
 static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
 				       const struct wpa_igtk_kde *igtk,
 				       int wnm_sleep)
@@ -1092,13 +1125,11 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211W */
 
 
 static int ieee80211w_set_keys(struct wpa_sm *sm,
 			       struct wpa_eapol_ie_parse *ie)
 {
-#ifdef CONFIG_IEEE80211W
 	if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
 		return 0;
 
@@ -1116,9 +1147,6 @@
 	}
 
 	return 0;
-#else /* CONFIG_IEEE80211W */
-	return 0;
-#endif /* CONFIG_IEEE80211W */
 }
 
 
@@ -1345,6 +1373,16 @@
 		return -1;
 	}
 
+	if ((sm->ap_rsnxe && !ie->rsnxe) ||
+	    (!sm->ap_rsnxe && ie->rsnxe) ||
+	    (sm->ap_rsnxe && ie->rsnxe &&
+	     (sm->ap_rsnxe_len != ie->rsnxe_len ||
+	      os_memcmp(sm->ap_rsnxe, ie->rsnxe, sm->ap_rsnxe_len) != 0))) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4");
+		return -1;
+	}
+
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt) &&
 	    wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0)
@@ -1429,7 +1467,6 @@
 			"WPA: GTK IE in unencrypted key data");
 		goto failed;
 	}
-#ifdef CONFIG_IEEE80211W
 	if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: IGTK KDE in unencrypted key data");
@@ -1445,7 +1482,6 @@
 			(unsigned long) ie.igtk_len);
 		goto failed;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
 		goto failed;
@@ -1688,12 +1724,12 @@
 		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
 		os_memcpy(gd->gtk, key_data, key_data_len);
 		if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
-			os_memset(ek, 0, sizeof(ek));
+			forced_memzero(ek, sizeof(ek));
 			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
 				"WPA: RC4 failed");
 			return -1;
 		}
-		os_memset(ek, 0, sizeof(ek));
+		forced_memzero(ek, sizeof(ek));
 #endif /* CONFIG_NO_RC4 */
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		if (maxkeylen % 8) {
@@ -1842,7 +1878,7 @@
 	if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
 	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
 		goto failed;
-	os_memset(&gd, 0, sizeof(gd));
+	forced_memzero(&gd, sizeof(gd));
 
 	if (rekey) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
@@ -1861,7 +1897,7 @@
 	return;
 
 failed:
-	os_memset(&gd, 0, sizeof(gd));
+	forced_memzero(&gd, sizeof(gd));
 	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
@@ -1975,12 +2011,12 @@
 		os_memcpy(ek, key->key_iv, 16);
 		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
 		if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
-			os_memset(ek, 0, sizeof(ek));
+			forced_memzero(ek, sizeof(ek));
 			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
 				"WPA: RC4 failed");
 			return -1;
 		}
-		os_memset(ek, 0, sizeof(ek));
+		forced_memzero(ek, sizeof(ek));
 #endif /* CONFIG_NO_RC4 */
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
@@ -2268,9 +2304,7 @@
 	key_info = WPA_GET_BE16(key->key_info);
 	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
 	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
 	    !wpa_use_akm_defined(sm->key_mgmt)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -2298,7 +2332,6 @@
 		}
 	} else
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
 		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 		    !wpa_use_akm_defined(sm->key_mgmt)) {
@@ -2307,11 +2340,9 @@
 				"negotiated AES-128-CMAC");
 			goto out;
 		}
-	} else
-#endif /* CONFIG_IEEE80211W */
-	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
-	    !wpa_use_akm_defined(sm->key_mgmt) &&
-	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+	} else if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+		   !wpa_use_akm_defined(sm->key_mgmt) &&
+		   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: CCMP is used, but EAPOL-Key "
 			"descriptor version (%d) is not 2", ver);
@@ -2454,12 +2485,10 @@
 	case WPA_KEY_MGMT_FT_PSK:
 		return RSN_AUTH_KEY_MGMT_FT_PSK;
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	case WPA_KEY_MGMT_IEEE8021X_SHA256:
 		return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
 	case WPA_KEY_MGMT_PSK_SHA256:
 		return RSN_AUTH_KEY_MGMT_PSK_SHA256;
-#endif /* CONFIG_IEEE80211W */
 	case WPA_KEY_MGMT_CCKM:
 		return (sm->proto == WPA_PROTO_RSN ?
 			RSN_AUTH_KEY_MGMT_CCKM:
@@ -2651,8 +2680,10 @@
 	eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
 	eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
 	os_free(sm->assoc_wpa_ie);
+	os_free(sm->assoc_rsnxe);
 	os_free(sm->ap_wpa_ie);
 	os_free(sm->ap_rsn_ie);
+	os_free(sm->ap_rsnxe);
 	wpa_sm_drop_sa(sm);
 	os_free(sm->ctx);
 #ifdef CONFIG_IEEE80211R
@@ -2714,6 +2745,9 @@
 		wpa_ft_prepare_auth_request(sm, NULL);
 
 		clear_keys = 0;
+		sm->ft_protocol = 1;
+	} else {
+		sm->ft_protocol = 0;
 	}
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_FILS
@@ -2739,10 +2773,8 @@
 		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 		os_memset(&sm->gtk, 0, sizeof(sm->gtk));
 		os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
-#ifdef CONFIG_IEEE80211W
 		os_memset(&sm->igtk, 0, sizeof(sm->igtk));
 		os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
-#endif /* CONFIG_IEEE80211W */
 	}
 
 #ifdef CONFIG_TDLS
@@ -2778,6 +2810,7 @@
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R
 	sm->ft_reassoc_completed = 0;
+	sm->ft_protocol = 0;
 #endif /* CONFIG_IEEE80211R */
 
 	/* Keys are not needed in the WPA state machine anymore */
@@ -3013,11 +3046,9 @@
 	case WPA_PARAM_KEY_MGMT:
 		sm->key_mgmt = value;
 		break;
-#ifdef CONFIG_IEEE80211W
 	case WPA_PARAM_MGMT_GROUP:
 		sm->mgmt_group_cipher = value;
 		break;
-#endif /* CONFIG_IEEE80211W */
 	case WPA_PARAM_RSN_ENABLED:
 		sm->rsn_enabled = value;
 		break;
@@ -3027,6 +3058,9 @@
 	case WPA_PARAM_OCV:
 		sm->ocv = value;
 		break;
+	case WPA_PARAM_SAE_PWE:
+		sm->sae_pwe = value;
+		break;
 	default:
 		break;
 	}
@@ -3205,6 +3239,83 @@
 
 
 /**
+ * wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @rsnxe: Pointer to buffer for RSNXE
+ * @rsnxe_len: Pointer to the length of the rsne buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe,
+				   size_t *rsnxe_len)
+{
+	int res;
+
+	if (!sm)
+		return -1;
+
+	res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len);
+	if (res < 0)
+		return -1;
+	*rsnxe_len = res;
+
+	wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len);
+
+	if (sm->assoc_rsnxe) {
+		wpa_hexdump(MSG_DEBUG,
+			    "RSN: Leave previously set RSNXE default",
+			    sm->assoc_rsnxe, sm->assoc_rsnxe_len);
+	} else if (*rsnxe_len > 0) {
+		/*
+		 * Make a copy of the RSNXE so that 4-Way Handshake gets the
+		 * correct version of the IE even if it gets changed.
+		 */
+		sm->assoc_rsnxe = os_memdup(rsnxe, *rsnxe_len);
+		if (!sm->assoc_rsnxe)
+			return -1;
+
+		sm->assoc_rsnxe_len = *rsnxe_len;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_rsnxe - Set own RSNXE from (Re)AssocReq
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the RSNXE used in (Re)Association Request
+ * frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_rsnxe_default().
+ */
+int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+	if (!sm)
+		return -1;
+
+	os_free(sm->assoc_rsnxe);
+	if (!ie || len == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: clearing own RSNXE");
+		sm->assoc_rsnxe = NULL;
+		sm->assoc_rsnxe_len = 0;
+	} else {
+		wpa_hexdump(MSG_DEBUG, "RSN: set own RSNXE", ie, len);
+		sm->assoc_rsnxe = os_memdup(ie, len);
+		if (!sm->assoc_rsnxe)
+			return -1;
+
+		sm->assoc_rsnxe_len = len;
+	}
+
+	return 0;
+}
+
+
+/**
  * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @ie: Pointer to IE data (starting from id)
@@ -3273,6 +3384,39 @@
 
 
 /**
+ * wpa_sm_set_ap_rsnxe - Set AP RSNXE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the RSNXE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+	if (!sm)
+		return -1;
+
+	os_free(sm->ap_rsnxe);
+	if (!ie || len == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP RSNXE");
+		sm->ap_rsnxe = NULL;
+		sm->ap_rsnxe_len = 0;
+	} else {
+		wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len);
+		sm->ap_rsnxe = os_memdup(ie, len);
+		if (!sm->ap_rsnxe)
+			return -1;
+
+		sm->ap_rsnxe_len = len;
+	}
+
+	return 0;
+}
+
+
+/**
  * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @data: Pointer to data area for parsing results
@@ -3345,10 +3489,8 @@
 	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 	os_memset(&sm->gtk, 0, sizeof(sm->gtk));
 	os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
-#ifdef CONFIG_IEEE80211W
 	os_memset(&sm->igtk, 0, sizeof(sm->igtk));
 	os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
 	os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
 	sm->xxkey_len = 0;
@@ -3416,20 +3558,18 @@
 		wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
 				gd.gtk, gd.gtk_len);
 		if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) {
-			os_memset(&gd, 0, sizeof(gd));
+			forced_memzero(&gd, sizeof(gd));
 			wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
 				   "WNM mode");
 			return -1;
 		}
-		os_memset(&gd, 0, sizeof(gd));
-#ifdef CONFIG_IEEE80211W
+		forced_memzero(&gd, sizeof(gd));
 	} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
 		const struct wpa_igtk_kde *igtk;
 
 		igtk = (const struct wpa_igtk_kde *) (buf + 2);
 		if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0)
 			return -1;
-#endif /* CONFIG_IEEE80211W */
 	} else {
 		wpa_printf(MSG_DEBUG, "Unknown element id");
 		return -1;
@@ -3851,7 +3991,7 @@
 				       dh_ss ? wpabuf_head(dh_ss) : NULL,
 				       dh_ss ? wpabuf_len(dh_ss) : 0,
 				       sm->pmk, &sm->pmk_len);
-		os_memset(rmsk, 0, sizeof(rmsk));
+		forced_memzero(rmsk, sizeof(rmsk));
 
 		/* Don't use DHss in PTK derivation if PMKSA caching is not
 		 * used. */
@@ -3926,7 +4066,7 @@
 			       sm->fils_key_auth_ap,
 			       &sm->fils_key_auth_len);
 	wpabuf_free(pub);
-	os_memset(ick, 0, sizeof(ick));
+	forced_memzero(ick, sizeof(ick));
 	return res;
 fail:
 	wpabuf_free(pub);
@@ -3989,10 +4129,10 @@
 
 	/* RSN Capabilities */
 	capab = 0;
-#ifdef CONFIG_IEEE80211W
-	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+	if (sm->mfp)
 		capab |= WPA_CAPABILITY_MFPC;
-#endif /* CONFIG_IEEE80211W */
+	if (sm->mfp == 2)
+		capab |= WPA_CAPABILITY_MFPR;
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
 	wpabuf_put_le16(buf, capab);
@@ -4032,13 +4172,11 @@
 		    WPA_PMK_NAME_LEN);
 	os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
-#ifdef CONFIG_IEEE80211W
 	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
 		/* Management Group Cipher Suite */
 		pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2;
 	return 0;
@@ -4290,6 +4428,26 @@
 			    sm->fils_session, FILS_SESSION_LEN);
 	}
 
+	if (!elems.rsn_ie) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No RSNE in (Re)Association Response");
+		/* As an interop workaround, allow this for now since IEEE Std
+		 * 802.11ai-2016 did not include all the needed changes to make
+		 * a FILS AP include RSNE in the frame. This workaround might
+		 * eventually be removed and replaced with rejection (goto fail)
+		 * to follow a strict interpretation of the standard. */
+	} else if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
+				      sm->ap_rsn_ie, sm->ap_rsn_ie_len,
+				      elems.rsn_ie - 2, elems.rsn_ie_len + 2)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"FILS: RSNE mismatch between Beacon/Probe Response and (Re)Association Response");
+		wpa_hexdump(MSG_DEBUG, "FILS: RSNE in Beacon/Probe Response",
+			    sm->ap_rsn_ie, sm->ap_rsn_ie_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: RSNE in (Re)Association Response",
+			    elems.rsn_ie, elems.rsn_ie_len);
+		goto fail;
+	}
+
 	/* TODO: FILS Public Key */
 
 	if (!elems.fils_key_confirm) {
@@ -4430,9 +4588,11 @@
 
 	wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully");
 	sm->fils_completed = 1;
+	forced_memzero(&gd, sizeof(gd));
 
 	return 0;
 fail:
+	forced_memzero(&gd, sizeof(gd));
 	return -1;
 }
 
@@ -4644,7 +4804,7 @@
 	else if (group == 21)
 		res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
 				      os_strlen(info), sm->pmk, hash_len);
-	os_memset(prk, 0, SHA512_MAC_LEN);
+	forced_memzero(prk, SHA512_MAC_LEN);
 	if (res < 0) {
 		sm->pmk_len = 0;
 		return -1;
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 8903f8e..f1fbb1b 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -26,7 +26,7 @@
 
 	void (*set_state)(void *ctx, enum wpa_states state);
 	enum wpa_states (*get_state)(void *ctx);
-	void (*deauthenticate)(void * ctx, int reason_code);
+	void (*deauthenticate)(void * ctx, u16 reason_code);
 	int (*set_key)(void *ctx, enum wpa_alg alg,
 		       const u8 *addr, int key_idx, int set_tx,
 		       const u8 *seq, size_t seq_len,
@@ -98,7 +98,8 @@
 	WPA_PARAM_MGMT_GROUP,
 	WPA_PARAM_RSN_ENABLED,
 	WPA_PARAM_MFP,
-	WPA_PARAM_OCV
+	WPA_PARAM_OCV,
+	WPA_PARAM_SAE_PWE,
 };
 
 struct rsn_supp_config {
@@ -134,8 +135,12 @@
 int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
 				    size_t *wpa_ie_len);
+int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe,
+				   size_t *rsnxe_len);
+int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
 int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
 
 int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
@@ -260,6 +265,12 @@
 	return -1;
 }
 
+static inline int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie,
+				      size_t len)
+{
+	return -1;
+}
+
 static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
 {
 	return 0;
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 7dcb104..2b8b41f 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -18,6 +18,8 @@
 #include "drivers/driver.h"
 #include "wpa.h"
 #include "wpa_i.h"
+#include "wpa_ie.h"
+#include "pmksa_cache.h"
 
 #ifdef CONFIG_IEEE80211R
 
@@ -27,15 +29,23 @@
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	const u8 *anonce = key->key_nonce;
 	int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+	const u8 *mpmk;
+	size_t mpmk_len;
 
-	if (sm->xxkey_len == 0) {
+	if (sm->xxkey_len > 0) {
+		mpmk = sm->xxkey;
+		mpmk_len = sm->xxkey_len;
+	} else if (sm->cur_pmksa) {
+		mpmk = sm->cur_pmksa->pmk;
+		mpmk_len = sm->cur_pmksa->pmk_len;
+	} else {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
 			   "derivation");
 		return -1;
 	}
 
 	sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
-	if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
+	if (wpa_derive_pmk_r0(mpmk, mpmk_len, sm->ssid,
 			      sm->ssid_len, sm->mobility_domain,
 			      sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
 			      sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0)
@@ -162,6 +172,9 @@
 	struct rsn_ie_hdr *rsnie;
 	u16 capab;
 	int mdie_len;
+	u8 rsnxe[10];
+	size_t rsnxe_len;
+	int res;
 
 	sm->ft_completed = 0;
 	sm->ft_reassoc_completed = 0;
@@ -237,13 +250,10 @@
 
 	/* RSN Capabilities */
 	capab = 0;
-#ifdef CONFIG_IEEE80211W
-	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ||
-	    sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 ||
-	    sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256 ||
-	    sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256)
+	if (sm->mfp)
 		capab |= WPA_CAPABILITY_MFPC;
-#endif /* CONFIG_IEEE80211W */
+	if (sm->mfp == 2)
+		capab |= WPA_CAPABILITY_MFPR;
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
 	WPA_PUT_LE16(pos, capab);
@@ -257,7 +267,6 @@
 	os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
 	pos += WPA_PMK_NAME_LEN;
 
-#ifdef CONFIG_IEEE80211W
 	/* Management Group Cipher Suite */
 	switch (sm->mgmt_group_cipher) {
 	case WPA_CIPHER_AES_128_CMAC:
@@ -277,7 +286,6 @@
 		pos += RSN_SELECTOR_LEN;
 		break;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	rsnie->len = (pos - (u8 *) rsnie) - 2;
 
@@ -355,6 +363,13 @@
 		pos += ric_ies_len;
 	}
 
+	res = wpa_gen_rsnxe(sm, rsnxe, sizeof(rsnxe));
+	if (res < 0) {
+		os_free(buf);
+		return NULL;
+	}
+	rsnxe_len = res;
+
 	if (kck) {
 		/*
 		 * IEEE Std 802.11r-2008, 11A.8.4
@@ -366,14 +381,18 @@
 		 * MDIE
 		 * FTIE (with MIC field set to 0)
 		 * RIC-Request (if present)
+		 * RSNXE (if present)
 		 */
 		/* Information element count */
 		*elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
+		if (rsnxe_len)
+			*elem_count += 1;
 		if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
 			       ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
 			       ftie_pos, 2 + *ftie_len,
 			       (u8 *) rsnie, 2 + rsnie->len, ric_ies,
-			       ric_ies_len, fte_mic) < 0) {
+			       ric_ies_len, rsnxe_len ? rsnxe : NULL, rsnxe_len,
+			       fte_mic) < 0) {
 			wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
 			os_free(buf);
 			return NULL;
@@ -589,6 +608,12 @@
 		return -1;
 	}
 
+	if (sm->mfp == 2 && !(parse.rsn_capab & WPA_CAPABILITY_MFPC)) {
+		wpa_printf(MSG_INFO,
+			   "FT: Target AP does not support PMF, but local configuration requires that");
+		return -1;
+	}
+
 	os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
@@ -758,7 +783,6 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
 static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
 				       size_t igtk_elem_len)
 {
@@ -819,14 +843,13 @@
 			   igtk_elem + 2, 6, igtk, igtk_len) < 0) {
 		wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
 			   "driver.");
-		os_memset(igtk, 0, sizeof(igtk));
+		forced_memzero(igtk, sizeof(igtk));
 		return -1;
 	}
-	os_memset(igtk, 0, sizeof(igtk));
+	forced_memzero(igtk, sizeof(igtk));
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211W */
 
 
 int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
@@ -953,6 +976,8 @@
 	count = 3;
 	if (parse.ric)
 		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+	if (parse.rsnxe)
+		count++;
 	if (fte_elem_count != count) {
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
@@ -973,6 +998,8 @@
 		       parse.ftie - 2, parse.ftie_len + 2,
 		       parse.rsn - 2, parse.rsn_len + 2,
 		       parse.ric, parse.ric_len,
+		       parse.rsnxe ? parse.rsnxe - 2 : NULL,
+		       parse.rsnxe ? parse.rsnxe_len + 2 : 0,
 		       mic) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 		return -1;
@@ -1009,10 +1036,8 @@
 	if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
 		return -1;
 
-#ifdef CONFIG_IEEE80211W
 	if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
 		return -1;
-#endif /* CONFIG_IEEE80211W */
 
 	if (sm->set_ptk_after_assoc) {
 		wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 5dc9f2e..2a43342 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -31,10 +31,8 @@
 	u8 request_counter[WPA_REPLAY_COUNTER_LEN];
 	struct wpa_gtk gtk;
 	struct wpa_gtk gtk_wnm_sleep;
-#ifdef CONFIG_IEEE80211W
 	struct wpa_igtk igtk;
 	struct wpa_igtk igtk_wnm_sleep;
-#endif /* CONFIG_IEEE80211W */
 
 	struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
 
@@ -87,11 +85,14 @@
 	int rsn_enabled; /* Whether RSN is enabled in configuration */
 	int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
 	int ocv; /* Operating Channel Validation */
+	int sae_pwe; /* SAE PWE generation options */
 
 	u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
 	size_t assoc_wpa_ie_len;
-	u8 *ap_wpa_ie, *ap_rsn_ie;
-	size_t ap_wpa_ie_len, ap_rsn_ie_len;
+	u8 *assoc_rsnxe; /* Own RSNXE from (Re)AssocReq */
+	size_t assoc_rsnxe_len;
+	u8 *ap_wpa_ie, *ap_rsn_ie, *ap_rsnxe;
+	size_t ap_wpa_ie_len, ap_rsn_ie_len, ap_rsnxe_len;
 
 #ifdef CONFIG_TDLS
 	struct wpa_tdls_peer *tdls;
@@ -126,8 +127,9 @@
 	u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
 	size_t r0kh_id_len;
 	u8 r1kh_id[FT_R1KH_ID_LEN];
-	int ft_completed;
-	int ft_reassoc_completed;
+	unsigned int ft_completed:1;
+	unsigned int ft_reassoc_completed:1;
+	unsigned int ft_protocol:1;
 	int over_the_ds_in_progress;
 	u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
 	int set_ptk_after_assoc;
@@ -187,7 +189,7 @@
 	return sm->ctx->get_state(sm->ctx->ctx);
 }
 
-static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code)
+static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, u16 reason_code)
 {
 	WPA_ASSERT(sm->ctx->deauthenticate);
 	sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index ae9f4ca..03c0d7e 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -168,12 +168,10 @@
 	} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
 	} else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_SAE
 	} else if (key_mgmt == WPA_KEY_MGMT_SAE) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
@@ -217,12 +215,10 @@
 
 	/* RSN Capabilities */
 	capab = 0;
-#ifdef CONFIG_IEEE80211W
 	if (sm->mfp)
 		capab |= WPA_CAPABILITY_MFPC;
 	if (sm->mfp == 2)
 		capab |= WPA_CAPABILITY_MFPR;
-#endif /* CONFIG_IEEE80211W */
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
 	WPA_PUT_LE16(pos, capab);
@@ -237,7 +233,6 @@
 		pos += PMKID_LEN;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
 		if (!sm->cur_pmksa) {
 			/* PMKID Count */
@@ -250,7 +245,6 @@
 							  mgmt_group_cipher));
 		pos += RSN_SELECTOR_LEN;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	hdr->len = (pos - rsn_ie) - 2;
 
@@ -348,261 +342,23 @@
 }
 
 
-/**
- * wpa_parse_vendor_specific - Parse Vendor Specific IEs
- * @pos: Pointer to the IE header
- * @end: Pointer to the end of the Key Data buffer
- * @ie: Pointer to parsed IE data
- * Returns: 0 on success, 1 if end mark is found, -1 on failure
- */
-static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
-				     struct wpa_eapol_ie_parse *ie)
+int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
 {
-	unsigned int oui;
+	u8 *pos = rsnxe;
 
-	if (pos[1] < 4) {
-		wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)",
-			   pos[1]);
-		return 1;
-	}
+	if (!wpa_key_mgmt_sae(sm->key_mgmt))
+		return 0; /* SAE not in use */
+	if (sm->sae_pwe != 1 && sm->sae_pwe != 2)
+		return 0; /* no supported extended RSN capabilities */
 
-	oui = WPA_GET_BE24(&pos[2]);
-	if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
-		if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
-			ie->wmm = &pos[2];
-			ie->wmm_len = pos[1];
-			wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
-				    ie->wmm, ie->wmm_len);
-		} else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
-			ie->wmm = &pos[2];
-			ie->wmm_len = pos[1];
-			wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
-				    ie->wmm, ie->wmm_len);
-		}
-	}
-	return 0;
-}
+	if (rsnxe_len < 3)
+		return -1;
 
+	*pos++ = WLAN_EID_RSNX;
+	*pos++ = 1;
+	/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
+	 * used for now */
+	*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 
-/**
- * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
- * @pos: Pointer to the IE header
- * @end: Pointer to the end of the Key Data buffer
- * @ie: Pointer to parsed IE data
- * Returns: 0 on success, 1 if end mark is found, -1 on failure
- */
-static int wpa_parse_generic(const u8 *pos, const u8 *end,
-			     struct wpa_eapol_ie_parse *ie)
-{
-	if (pos[1] == 0)
-		return 1;
-
-	if (pos[1] >= 6 &&
-	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
-	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
-	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
-		ie->wpa_ie = pos;
-		ie->wpa_ie_len = pos[1] + 2;
-		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
-			    ie->wpa_ie, ie->wpa_ie_len);
-		return 0;
-	}
-
-	if (1 + RSN_SELECTOR_LEN < end - pos &&
-	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
-		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
-		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
-				pos, pos[1] + 2);
-		return 0;
-	}
-
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
-		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
-		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-
-#ifdef CONFIG_IEEE80211W
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
-		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
-				pos, pos[1] + 2);
-		return 0;
-	}
-#endif /* CONFIG_IEEE80211W */
-
-#ifdef CONFIG_P2P
-	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
-		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
-			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
-		return 0;
-	}
-
-	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
-		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG,
-			    "WPA: IP Address Allocation in EAPOL-Key",
-			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
-		return 0;
-	}
-#endif /* CONFIG_P2P */
-
-#ifdef CONFIG_OCV
-	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
-		ie->oci = pos + 2 + RSN_SELECTOR_LEN;
-		ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key",
-			    pos, pos[1] + 2);
-		return 0;
-	}
-#endif /* CONFIG_OCV */
-
-	return 0;
-}
-
-
-/**
- * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
- * @buf: Pointer to the Key Data buffer
- * @len: Key Data Length
- * @ie: Pointer to parsed IE data
- * Returns: 0 on success, -1 on failure
- */
-int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
-			     struct wpa_eapol_ie_parse *ie)
-{
-	const u8 *pos, *end;
-	int ret = 0;
-
-	os_memset(ie, 0, sizeof(*ie));
-	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
-		if (pos[0] == 0xdd &&
-		    ((pos == buf + len - 1) || pos[1] == 0)) {
-			/* Ignore padding */
-			break;
-		}
-		if (2 + pos[1] > end - pos) {
-			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
-				   "underflow (ie=%d len=%d pos=%d)",
-				   pos[0], pos[1], (int) (pos - buf));
-			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
-					buf, len);
-			ret = -1;
-			break;
-		}
-		if (*pos == WLAN_EID_RSN) {
-			ie->rsn_ie = pos;
-			ie->rsn_ie_len = pos[1] + 2;
-			wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
-				    ie->rsn_ie, ie->rsn_ie_len);
-		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN &&
-			   pos[1] >= sizeof(struct rsn_mdie)) {
-			ie->mdie = pos;
-			ie->mdie_len = pos[1] + 2;
-			wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
-				    ie->mdie, ie->mdie_len);
-		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION &&
-			   pos[1] >= sizeof(struct rsn_ftie)) {
-			ie->ftie = pos;
-			ie->ftie_len = pos[1] + 2;
-			wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
-				    ie->ftie, ie->ftie_len);
-		} else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
-			if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
-				ie->reassoc_deadline = pos;
-				wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
-					    "in EAPOL-Key",
-					    ie->reassoc_deadline, pos[1] + 2);
-			} else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
-				ie->key_lifetime = pos;
-				wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
-					    "in EAPOL-Key",
-					    ie->key_lifetime, pos[1] + 2);
-			} else {
-				wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
-					    "EAPOL-Key Key Data IE",
-					    pos, 2 + pos[1]);
-			}
-		} else if (*pos == WLAN_EID_LINK_ID) {
-			if (pos[1] >= 18) {
-				ie->lnkid = pos;
-				ie->lnkid_len = pos[1] + 2;
-			}
-		} else if (*pos == WLAN_EID_EXT_CAPAB) {
-			ie->ext_capab = pos;
-			ie->ext_capab_len = pos[1] + 2;
-		} else if (*pos == WLAN_EID_SUPP_RATES) {
-			ie->supp_rates = pos;
-			ie->supp_rates_len = pos[1] + 2;
-		} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
-			ie->ext_supp_rates = pos;
-			ie->ext_supp_rates_len = pos[1] + 2;
-		} else if (*pos == WLAN_EID_HT_CAP &&
-			   pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
-			ie->ht_capabilities = pos + 2;
-		} else if (*pos == WLAN_EID_VHT_AID) {
-			if (pos[1] >= 2)
-				ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
-		} else if (*pos == WLAN_EID_VHT_CAP &&
-			   pos[1] >= sizeof(struct ieee80211_vht_capabilities))
-		{
-			ie->vht_capabilities = pos + 2;
-		} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
-			ie->qosinfo = pos[2];
-		} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
-			ie->supp_channels = pos + 2;
-			ie->supp_channels_len = pos[1];
-		} else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
-			/*
-			 * The value of the Length field of the Supported
-			 * Operating Classes element is between 2 and 253.
-			 * Silently skip invalid elements to avoid interop
-			 * issues when trying to use the value.
-			 */
-			if (pos[1] >= 2 && pos[1] <= 253) {
-				ie->supp_oper_classes = pos + 2;
-				ie->supp_oper_classes_len = pos[1];
-			}
-		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
-			ret = wpa_parse_generic(pos, end, ie);
-			if (ret < 0)
-				break;
-			if (ret > 0) {
-				ret = 0;
-				break;
-			}
-
-			ret = wpa_parse_vendor_specific(pos, end, ie);
-			if (ret < 0)
-				break;
-			if (ret > 0) {
-				ret = 0;
-				break;
-			}
-		} else {
-			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
-				    "Key Data IE", pos, 2 + pos[1]);
-		}
-	}
-
-	return ret;
+	return pos - rsnxe;
 }
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 9d53973..6dc6cf5 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -11,56 +11,7 @@
 
 struct wpa_sm;
 
-struct wpa_eapol_ie_parse {
-	const u8 *wpa_ie;
-	size_t wpa_ie_len;
-	const u8 *rsn_ie;
-	size_t rsn_ie_len;
-	const u8 *pmkid;
-	const u8 *gtk;
-	size_t gtk_len;
-	const u8 *mac_addr;
-	size_t mac_addr_len;
-#ifdef CONFIG_IEEE80211W
-	const u8 *igtk;
-	size_t igtk_len;
-#endif /* CONFIG_IEEE80211W */
-	const u8 *mdie;
-	size_t mdie_len;
-	const u8 *ftie;
-	size_t ftie_len;
-	const u8 *reassoc_deadline;
-	const u8 *key_lifetime;
-	const u8 *lnkid;
-	size_t lnkid_len;
-	const u8 *ext_capab;
-	size_t ext_capab_len;
-	const u8 *supp_rates;
-	size_t supp_rates_len;
-	const u8 *ext_supp_rates;
-	size_t ext_supp_rates_len;
-	const u8 *ht_capabilities;
-	const u8 *vht_capabilities;
-	const u8 *supp_channels;
-	size_t supp_channels_len;
-	const u8 *supp_oper_classes;
-	size_t supp_oper_classes_len;
-	u8 qosinfo;
-	u16 aid;
-	const u8 *wmm;
-	size_t wmm_len;
-#ifdef CONFIG_P2P
-	const u8 *ip_addr_req;
-	const u8 *ip_addr_alloc;
-#endif /* CONFIG_P2P */
-#ifdef CONFIG_OCV
-	const u8 *oci;
-	size_t oci_len;
-#endif /* CONFIG_OCV */
-};
-
-int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
-			     struct wpa_eapol_ie_parse *ie);
 int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
+int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len);
 
 #endif /* WPA_IE_H */
diff --git a/src/tls/asn1.c b/src/tls/asn1.c
index 822f87c..a08c2e1 100644
--- a/src/tls/asn1.c
+++ b/src/tls/asn1.c
@@ -22,6 +22,36 @@
 };
 
 
+static int asn1_valid_der_boolean(struct asn1_hdr *hdr)
+{
+	/* Enforce DER requirements for a single way of encoding a BOOLEAN */
+	if (hdr->length != 1) {
+		wpa_printf(MSG_DEBUG, "ASN.1: Unexpected BOOLEAN length (%u)",
+			   hdr->length);
+		return 0;
+	}
+
+	if (hdr->payload[0] != 0 && hdr->payload[0] != 0xff) {
+		wpa_printf(MSG_DEBUG,
+			   "ASN.1: Invalid BOOLEAN value 0x%x (DER requires 0 or 0xff)",
+			   hdr->payload[0]);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int asn1_valid_der(struct asn1_hdr *hdr)
+{
+	if (hdr->class != ASN1_CLASS_UNIVERSAL)
+		return 1;
+	if (hdr->tag == ASN1_TAG_BOOLEAN && !asn1_valid_der_boolean(hdr))
+		return 0;
+	return 1;
+}
+
+
 int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
 {
 	const u8 *pos, *end;
@@ -91,7 +121,8 @@
 	}
 
 	hdr->payload = pos;
-	return 0;
+
+	return asn1_valid_der(hdr) ? 0 : -1;
 }
 
 
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 4f7a148..7156744 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -2441,6 +2441,7 @@
 
   /* clear the carry */
   _W = 0;
+  os_memset(W, 0, sizeof(W));
   for (ix = 0; ix < pa; ix++) {
       int      tx, ty;
       int      iy;
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 842e5dd..01b2f83 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -130,7 +130,7 @@
 			return -1;
 		}
 
-		der = base64_decode(pos, end - pos, &der_len);
+		der = base64_decode((const char *) pos, end - pos, &der_len);
 		if (der == NULL) {
 			wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
 				   "certificate");
@@ -293,7 +293,7 @@
 		}
 	}
 
-	der = base64_decode(pos, end - pos, &der_len);
+	der = base64_decode((const char *) pos, end - pos, &der_len);
 	if (!der)
 		return NULL;
 	pkey = crypto_private_key_import(der, der_len, NULL);
@@ -321,7 +321,7 @@
 	if (!end)
 		return NULL;
 
-	der = base64_decode(pos, end - pos, &der_len);
+	der = base64_decode((const char *) pos, end - pos, &der_len);
 	if (!der)
 		return NULL;
 	pkey = crypto_private_key_import(der, der_len, passwd);
@@ -1225,7 +1225,7 @@
 		return -1;
 	}
 
-	der = base64_decode(pos, end - pos, &der_len);
+	der = base64_decode((const char *) pos, end - pos, &der_len);
 	if (der == NULL) {
 		wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
 		return -1;
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index fa4d442..1bd5aa0 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -538,9 +538,43 @@
 }
 
 
+static int parse_uint2(const char *pos, size_t len)
+{
+	char buf[3];
+	int ret;
+
+	if (len < 2)
+		return -1;
+	buf[0] = pos[0];
+	buf[1] = pos[1];
+	buf[2] = 0x00;
+	if (sscanf(buf, "%2d", &ret) != 1)
+		return -1;
+	return ret;
+}
+
+
+static int parse_uint4(const char *pos, size_t len)
+{
+	char buf[5];
+	int ret;
+
+	if (len < 4)
+		return -1;
+	buf[0] = pos[0];
+	buf[1] = pos[1];
+	buf[2] = pos[2];
+	buf[3] = pos[3];
+	buf[4] = 0x00;
+	if (sscanf(buf, "%4d", &ret) != 1)
+		return -1;
+	return ret;
+}
+
+
 int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val)
 {
-	const char *pos;
+	const char *pos, *end;
 	int year, month, day, hour, min, sec;
 
 	/*
@@ -554,6 +588,7 @@
 	 */
 
 	pos = (const char *) buf;
+	end = pos + len;
 
 	switch (asn1_tag) {
 	case ASN1_TAG_UTCTIME:
@@ -562,7 +597,8 @@
 					  "UTCTime format", buf, len);
 			return -1;
 		}
-		if (sscanf(pos, "%02d", &year) != 1) {
+		year = parse_uint2(pos, end - pos);
+		if (year < 0) {
 			wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
 					  "UTCTime year", buf, len);
 			return -1;
@@ -579,7 +615,8 @@
 					  "GeneralizedTime format", buf, len);
 			return -1;
 		}
-		if (sscanf(pos, "%04d", &year) != 1) {
+		year = parse_uint4(pos, end - pos);
+		if (year < 0) {
 			wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
 					  "GeneralizedTime year", buf, len);
 			return -1;
@@ -592,35 +629,40 @@
 		return -1;
 	}
 
-	if (sscanf(pos, "%02d", &month) != 1) {
+	month = parse_uint2(pos, end - pos);
+	if (month < 0) {
 		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 				  "(month)", buf, len);
 		return -1;
 	}
 	pos += 2;
 
-	if (sscanf(pos, "%02d", &day) != 1) {
+	day = parse_uint2(pos, end - pos);
+	if (day < 0) {
 		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 				  "(day)", buf, len);
 		return -1;
 	}
 	pos += 2;
 
-	if (sscanf(pos, "%02d", &hour) != 1) {
+	hour = parse_uint2(pos, end - pos);
+	if (hour < 0) {
 		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 				  "(hour)", buf, len);
 		return -1;
 	}
 	pos += 2;
 
-	if (sscanf(pos, "%02d", &min) != 1) {
+	min = parse_uint2(pos, end - pos);
+	if (min < 0) {
 		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 				  "(min)", buf, len);
 		return -1;
 	}
 	pos += 2;
 
-	if (sscanf(pos, "%02d", &sec) != 1) {
+	sec = parse_uint2(pos, end - pos);
+	if (sec < 0) {
 		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 				  "(sec)", buf, len);
 		return -1;
@@ -773,6 +815,7 @@
 	struct asn1_hdr hdr;
 	unsigned long value;
 	size_t left;
+	const u8 *end_seq;
 
 	/*
 	 * BasicConstraints ::= SEQUENCE {
@@ -794,6 +837,7 @@
 	if (hdr.length == 0)
 		return 0;
 
+	end_seq = hdr.payload + hdr.length;
 	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
 	    hdr.class != ASN1_CLASS_UNIVERSAL) {
 		wpa_printf(MSG_DEBUG, "X509: Failed to parse "
@@ -802,22 +846,16 @@
 	}
 
 	if (hdr.tag == ASN1_TAG_BOOLEAN) {
-		if (hdr.length != 1) {
-			wpa_printf(MSG_DEBUG, "X509: Unexpected "
-				   "Boolean length (%u) in BasicConstraints",
-				   hdr.length);
-			return -1;
-		}
 		cert->ca = hdr.payload[0];
 
-		if (hdr.length == pos + len - hdr.payload) {
+		pos = hdr.payload + hdr.length;
+		if (pos >= end_seq) {
+			/* No optional pathLenConstraint */
 			wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
 				   cert->ca);
 			return 0;
 		}
-
-		if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
-				  &hdr) < 0 ||
+		if (asn1_get_next(pos, end_seq - pos, &hdr) < 0 ||
 		    hdr.class != ASN1_CLASS_UNIVERSAL) {
 			wpa_printf(MSG_DEBUG, "X509: Failed to parse "
 				   "BasicConstraints");
@@ -1263,11 +1301,6 @@
 	}
 
 	if (hdr.tag == ASN1_TAG_BOOLEAN) {
-		if (hdr.length != 1) {
-			wpa_printf(MSG_DEBUG, "X509: Unexpected "
-				   "Boolean length (%u)", hdr.length);
-			return -1;
-		}
 		critical_ext = hdr.payload[0];
 		pos = hdr.payload;
 		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
diff --git a/src/utils/base64.c b/src/utils/base64.c
index 53a92f4..a17d2d3 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -12,18 +12,16 @@
 #include "os.h"
 #include "base64.h"
 
-static const unsigned char base64_table[65] =
+static const char base64_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const unsigned char base64_url_table[65] =
+static const char base64_url_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 
 
-static unsigned char * base64_gen_encode(const unsigned char *src, size_t len,
-					 size_t *out_len,
-					 const unsigned char *table,
-					 int add_pad)
+static char * base64_gen_encode(const unsigned char *src, size_t len,
+				size_t *out_len, const char *table, int add_pad)
 {
-	unsigned char *out, *pos;
+	char *out, *pos;
 	const unsigned char *end, *in;
 	size_t olen;
 	int line_len;
@@ -83,9 +81,8 @@
 }
 
 
-static unsigned char * base64_gen_decode(const unsigned char *src, size_t len,
-					 size_t *out_len,
-					 const unsigned char *table)
+static unsigned char * base64_gen_decode(const char *src, size_t len,
+					 size_t *out_len, const char *table)
 {
 	unsigned char dtable[256], *out, *pos, block[4], tmp;
 	size_t i, count, olen;
@@ -94,12 +91,12 @@
 
 	os_memset(dtable, 0x80, 256);
 	for (i = 0; i < sizeof(base64_table) - 1; i++)
-		dtable[table[i]] = (unsigned char) i;
+		dtable[(unsigned char) table[i]] = (unsigned char) i;
 	dtable['='] = 0;
 
 	count = 0;
 	for (i = 0; i < len; i++) {
-		if (dtable[src[i]] != 0x80)
+		if (dtable[(unsigned char) src[i]] != 0x80)
 			count++;
 	}
 
@@ -165,17 +162,15 @@
  * nul terminated to make it easier to use as a C string. The nul terminator is
  * not included in out_len.
  */
-unsigned char * base64_encode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+char * base64_encode(const void *src, size_t len, size_t *out_len)
 {
 	return base64_gen_encode(src, len, out_len, base64_table, 1);
 }
 
 
-unsigned char * base64_url_encode(const unsigned char *src, size_t len,
-				  size_t *out_len, int add_pad)
+char * base64_url_encode(const void *src, size_t len, size_t *out_len)
 {
-	return base64_gen_encode(src, len, out_len, base64_url_table, add_pad);
+	return base64_gen_encode(src, len, out_len, base64_url_table, 0);
 }
 
 
@@ -189,15 +184,13 @@
  *
  * Caller is responsible for freeing the returned buffer.
  */
-unsigned char * base64_decode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+unsigned char * base64_decode(const char *src, size_t len, size_t *out_len)
 {
 	return base64_gen_decode(src, len, out_len, base64_table);
 }
 
 
-unsigned char * base64_url_decode(const unsigned char *src, size_t len,
-				  size_t *out_len)
+unsigned char * base64_url_decode(const char *src, size_t len, size_t *out_len)
 {
 	return base64_gen_decode(src, len, out_len, base64_url_table);
 }
diff --git a/src/utils/base64.h b/src/utils/base64.h
index 5a72c3e..6216f44 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -9,13 +9,9 @@
 #ifndef BASE64_H
 #define BASE64_H
 
-unsigned char * base64_encode(const unsigned char *src, size_t len,
-			      size_t *out_len);
-unsigned char * base64_decode(const unsigned char *src, size_t len,
-			      size_t *out_len);
-unsigned char * base64_url_encode(const unsigned char *src, size_t len,
-				  size_t *out_len, int add_pad);
-unsigned char * base64_url_decode(const unsigned char *src, size_t len,
-				  size_t *out_len);
+char * base64_encode(const void *src, size_t len, size_t *out_len);
+unsigned char * base64_decode(const char *src, size_t len, size_t *out_len);
+char * base64_url_encode(const void *src, size_t len, size_t *out_len);
+unsigned char * base64_url_decode(const char *src, size_t len, size_t *out_len);
 
 #endif /* BASE64_H */
diff --git a/src/utils/common.c b/src/utils/common.c
index b9c8bfd..27bf435 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -230,6 +230,16 @@
 }
 
 
+void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+	size_t i;
+
+	for (i = len - 1; i > 0; i--)
+		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+	buf[0] >>= bits;
+}
+
+
 void wpa_get_ntp_timestamp(u8 *buf)
 {
 	struct os_time now;
@@ -960,7 +970,7 @@
 {
 	if (str) {
 		size_t len = os_strlen(str);
-		os_memset(str, 0, len);
+		forced_memzero(str, len);
 		os_free(str);
 	}
 }
@@ -969,7 +979,7 @@
 void bin_clear_free(void *bin, size_t len)
 {
 	if (bin) {
-		os_memset(bin, 0, len);
+		forced_memzero(bin, len);
 		os_free(bin);
 	}
 }
@@ -1249,3 +1259,22 @@
 	val[len] = '\0';
 	return val;
 }
+
+
+/* Try to prevent most compilers from optimizing out clearing of memory that
+ * becomes unaccessible after this function is called. This is mostly the case
+ * for clearing local stack variables at the end of a function. This is not
+ * exactly perfect, i.e., someone could come up with a compiler that figures out
+ * the pointer is pointing to memset and then end up optimizing the call out, so
+ * try go a bit further by storing the first octet (now zero) to make this even
+ * a bit more difficult to optimize out. Once memset_s() is available, that
+ * could be used here instead. */
+static void * (* const volatile memset_func)(void *, int, size_t) = memset;
+static u8 forced_memzero_val;
+
+void forced_memzero(void *ptr, size_t len)
+{
+	memset_func(ptr, 0, len);
+	if (len)
+		forced_memzero_val = ((u8 *) ptr)[0];
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 792a30a..833469a 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -344,6 +344,9 @@
 #ifndef ETH_P_OUI
 #define ETH_P_OUI 0x88B7
 #endif /* ETH_P_OUI */
+#ifndef ETH_P_8021Q
+#define ETH_P_8021Q 0x8100
+#endif /* ETH_P_8021Q */
 
 
 #ifdef __GNUC__
@@ -477,6 +480,7 @@
 int hex2byte(const char *hex);
 int hexstr2bin(const char *hex, u8 *buf, size_t len);
 void inc_byte_array(u8 *counter, size_t len);
+void buf_shift_right(u8 *buf, size_t len, size_t bits);
 void wpa_get_ntp_timestamp(u8 *buf);
 int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
 int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
@@ -569,6 +573,8 @@
 u8 rssi_to_rcpi(int rssi);
 char * get_param(const char *cmd, const char *param);
 
+void forced_memzero(void *ptr, size_t len);
+
 /*
  * gcc 4.4 ends up generating strict-aliasing warnings about some very common
  * networking socket uses that do not really result in a real problem and
diff --git a/src/utils/json.c b/src/utils/json.c
index b644339..5a0edf2 100644
--- a/src/utils/json.c
+++ b/src/utils/json.c
@@ -51,7 +51,7 @@
 				*txt++ = data[i];
 			} else {
 				txt += os_snprintf(txt, end - txt, "\\u%04x",
-						   data[i]);
+						   (unsigned char) data[i]);
 			}
 			break;
 		}
@@ -300,8 +300,10 @@
 				goto fail;
 			if (!curr_token) {
 				token = json_alloc_token(&tokens);
-				if (!token)
+				if (!token) {
+					os_free(str);
 					goto fail;
+				}
 				token->type = JSON_STRING;
 				token->string = str;
 				token->state = JSON_COMPLETED;
@@ -514,8 +516,8 @@
 	token = json_get_member(json, name);
 	if (!token || token->type != JSON_STRING)
 		return NULL;
-	buf = base64_url_decode((const unsigned char *) token->string,
-				os_strlen(token->string), &buflen);
+	buf = base64_url_decode(token->string, os_strlen(token->string),
+				&buflen);
 	if (!buf)
 		return NULL;
 	ret = wpabuf_alloc_ext_data(buf, buflen);
@@ -574,3 +576,79 @@
 	buf[0] = '\0';
 	json_print_token(root, 1, buf, buflen);
 }
+
+
+void json_add_int(struct wpabuf *json, const char *name, int val)
+{
+	wpabuf_printf(json, "\"%s\":%d", name, val);
+}
+
+
+void json_add_string(struct wpabuf *json, const char *name, const char *val)
+{
+	wpabuf_printf(json, "\"%s\":\"%s\"", name, val);
+}
+
+
+int json_add_string_escape(struct wpabuf *json, const char *name,
+			   const void *val, size_t len)
+{
+	char *tmp;
+	size_t tmp_len = 6 * len + 1;
+
+	tmp = os_malloc(tmp_len);
+	if (!tmp)
+		return -1;
+	json_escape_string(tmp, tmp_len, val, len);
+	json_add_string(json, name, tmp);
+	bin_clear_free(tmp, tmp_len);
+	return 0;
+}
+
+
+int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
+		       size_t len)
+{
+	char *b64;
+
+	b64 = base64_url_encode(val, len, NULL);
+	if (!b64)
+		return -1;
+	json_add_string(json, name, b64);
+	os_free(b64);
+	return 0;
+}
+
+
+void json_start_object(struct wpabuf *json, const char *name)
+{
+	if (name)
+		wpabuf_printf(json, "\"%s\":", name);
+	wpabuf_put_u8(json, '{');
+}
+
+
+void json_end_object(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, '}');
+}
+
+
+void json_start_array(struct wpabuf *json, const char *name)
+{
+	if (name)
+		wpabuf_printf(json, "\"%s\":", name);
+	wpabuf_put_u8(json, '[');
+}
+
+
+void json_end_array(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, ']');
+}
+
+
+void json_value_sep(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, ',');
+}
diff --git a/src/utils/json.h b/src/utils/json.h
index 8faa95d..ca4a2e4 100644
--- a/src/utils/json.h
+++ b/src/utils/json.h
@@ -38,5 +38,16 @@
 struct wpabuf * json_get_member_base64url(struct json_token *json,
 					  const char *name);
 void json_print_tree(struct json_token *root, char *buf, size_t buflen);
+void json_add_int(struct wpabuf *json, const char *name, int val);
+void json_add_string(struct wpabuf *json, const char *name, const char *val);
+int json_add_string_escape(struct wpabuf *json, const char *name,
+			   const void *val, size_t len);
+int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
+		       size_t len);
+void json_start_object(struct wpabuf *json, const char *name);
+void json_end_object(struct wpabuf *json);
+void json_start_array(struct wpabuf *json, const char *name);
+void json_end_array(struct wpabuf *json);
+void json_value_sep(struct wpabuf *json);
 
 #endif /* JSON_H */
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index 474c8a3..feade6e 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -25,10 +25,16 @@
 
 void os_sleep(os_time_t sec, os_time_t usec)
 {
+#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
+	const struct timespec req = { sec, usec * 1000 };
+
+	nanosleep(&req, NULL);
+#else
 	if (sec)
 		sleep(sec);
 	if (usec)
 		usleep(usec);
+#endif
 }
 
 
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index b56bab2..494bf4c 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -51,10 +51,16 @@
 
 void os_sleep(os_time_t sec, os_time_t usec)
 {
+#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
+	const struct timespec req = { sec, usec * 1000 };
+
+	nanosleep(&req, NULL);
+#else
 	if (sec)
 		sleep(sec);
 	if (usec)
 		usleep(usec);
+#endif
 }
 
 
diff --git a/src/utils/trace.c b/src/utils/trace.c
index e0b5b0b..4084343 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -186,7 +186,7 @@
 	if (abfd == NULL)
 		return;
 
-	data.pc = (bfd_hostptr_t) (pc - start_offset);
+	data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -227,7 +227,7 @@
 	if (abfd == NULL)
 		return NULL;
 
-	data.pc = (bfd_hostptr_t) (pc - start_offset);
+	data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -299,7 +299,7 @@
 	for (i = 0; i < btrace_num; i++) {
 		struct bfd_data data;
 
-		data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset);
+		data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset);
 		data.found = FALSE;
 		bfd_map_over_sections(abfd, find_addr_sect, &data);
 
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index 3af4fcd..b09225d 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -296,52 +296,53 @@
 {
 	int errors = 0;
 	unsigned char *res;
+	char *res2;
 	size_t res_len;
 
 	wpa_printf(MSG_INFO, "base64 tests");
 
-	res = base64_encode((const unsigned char *) "", ~0, &res_len);
+	res2 = base64_encode("", ~0, &res_len);
+	if (res2) {
+		errors++;
+		os_free(res2);
+	}
+
+	res2 = base64_encode("=", 1, &res_len);
+	if (!res2 || res_len != 5 || res2[0] != 'P' || res2[1] != 'Q' ||
+	    res2[2] != '=' || res2[3] != '=' || res2[4] != '\n')
+		errors++;
+	os_free(res2);
+
+	res2 = base64_encode("=", 1, NULL);
+	if (!res2 || res2[0] != 'P' || res2[1] != 'Q' ||
+	    res2[2] != '=' || res2[3] != '=' || res2[4] != '\n')
+		errors++;
+	os_free(res2);
+
+	res = base64_decode("", 0, &res_len);
 	if (res) {
 		errors++;
 		os_free(res);
 	}
 
-	res = base64_encode((const unsigned char *) "=", 1, &res_len);
-	if (!res || res_len != 5 || res[0] != 'P' || res[1] != 'Q' ||
-	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
-		errors++;
-	os_free(res);
-
-	res = base64_encode((const unsigned char *) "=", 1, NULL);
-	if (!res || res[0] != 'P' || res[1] != 'Q' ||
-	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
-		errors++;
-	os_free(res);
-
-	res = base64_decode((const unsigned char *) "", 0, &res_len);
+	res = base64_decode("a", 1, &res_len);
 	if (res) {
 		errors++;
 		os_free(res);
 	}
 
-	res = base64_decode((const unsigned char *) "a", 1, &res_len);
+	res = base64_decode("====", 4, &res_len);
 	if (res) {
 		errors++;
 		os_free(res);
 	}
 
-	res = base64_decode((const unsigned char *) "====", 4, &res_len);
-	if (res) {
-		errors++;
-		os_free(res);
-	}
-
-	res = base64_decode((const unsigned char *) "PQ==", 4, &res_len);
+	res = base64_decode("PQ==", 4, &res_len);
 	if (!res || res_len != 1 || res[0] != '=')
 		errors++;
 	os_free(res);
 
-	res = base64_decode((const unsigned char *) "P.Q-=!=*", 8, &res_len);
+	res = base64_decode("P.Q-=!=*", 8, &res_len);
 	if (!res || res_len != 1 || res[0] != '=')
 		errors++;
 	os_free(res);
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index c437000..c336e53 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -144,6 +144,7 @@
 		printf("failed to read /proc/mounts\n");
 		return -1;
 	}
+	buf[buflen] = '\0';
 
 	line = strtok_r(buf, "\n", &tmp1);
 	while (line) {
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 1fe0b7d..c94c439 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -305,7 +305,6 @@
 #define HOSTAPD_MODULE_RADIUS		0x00000004
 #define HOSTAPD_MODULE_WPA		0x00000008
 #define HOSTAPD_MODULE_DRIVER		0x00000010
-#define HOSTAPD_MODULE_IAPP		0x00000020
 #define HOSTAPD_MODULE_MLME		0x00000040
 
 enum hostapd_logger_level {
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
index 7b6d276..d73654e 100644
--- a/src/utils/xml_libxml2.c
+++ b/src/utils/xml_libxml2.c
@@ -409,7 +409,7 @@
 	if (txt == NULL)
 		return NULL;
 
-	ret = base64_decode((unsigned char *) txt, strlen(txt), &len);
+	ret = base64_decode(txt, strlen(txt), &len);
 	if (ret_len)
 		*ret_len = len;
 	xml_node_get_text_free(ctx, txt);
diff --git a/src/wps/upnp_xml.c b/src/wps/upnp_xml.c
index a9958ee..ca0925c 100644
--- a/src/wps/upnp_xml.c
+++ b/src/wps/upnp_xml.c
@@ -235,7 +235,7 @@
 		return NULL;
 	}
 
-	decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
+	decoded = base64_decode(msg, os_strlen(msg), &len);
 	os_free(msg);
 	if (decoded == NULL) {
 		*ret = UPNP_OUT_OF_MEMORY;
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 14ce863..9963c46 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -733,7 +733,7 @@
 	 * uses this when acting as an Enrollee to notify Registrar of the
 	 * current configuration.
 	 *
-	 * When using WPA/WPA2-Person, this key can be either the ASCII
+	 * When using WPA/WPA2-Personal, this key can be either the ASCII
 	 * passphrase (8..63 characters) or the 32-octet PSK (64 hex
 	 * characters). When this is set to the ASCII passphrase, the PSK can
 	 * be provided in the psk buffer and used per-Enrollee to control which
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 4e872f3..5ec7133 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -175,7 +175,9 @@
 	len[0] = wpabuf_len(wps->last_msg);
 	addr[1] = wpabuf_head(msg);
 	len[1] = wpabuf_len(msg);
-	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+	if (hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len,
+			       hash) < 0)
+		return -1;
 
 	wpa_printf(MSG_DEBUG, "WPS:  * Authenticator");
 	wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
@@ -371,8 +373,9 @@
 	u8 hash[SHA256_MAC_LEN];
 
 	wpa_printf(MSG_DEBUG, "WPS:  * Key Wrap Authenticator");
-	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
-		    wpabuf_len(msg), hash);
+	if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+			wpabuf_len(msg), hash) < 0)
+		return -1;
 
 	wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
 	wpabuf_put_be16(msg, WPS_KWA_LEN);
diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c
index e8c4579..44436a4 100644
--- a/src/wps/wps_attr_process.c
+++ b/src/wps/wps_attr_process.c
@@ -39,9 +39,10 @@
 	len[0] = wpabuf_len(wps->last_msg);
 	addr[1] = wpabuf_head(msg);
 	len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
-	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
 
-	if (os_memcmp_const(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+	if (hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len,
+			       hash) < 0 ||
+	    os_memcmp_const(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
 		return -1;
 	}
@@ -70,8 +71,8 @@
 		return -1;
 	}
 
-	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
-	if (os_memcmp_const(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+	if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash) < 0 ||
+	    os_memcmp_const(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
 		return -1;
 	}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 06a8fda..6bded14 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -897,7 +897,7 @@
 				       const struct sockaddr_in *dst,
 				       char **len_ptr, char **body_ptr)
 {
-	unsigned char *encoded;
+	char *encoded;
 	size_t encoded_len;
 	struct wpabuf *buf;
 
@@ -939,7 +939,7 @@
 	wpabuf_put_str(buf, "\">\n");
 	if (encoded) {
 		wpabuf_printf(buf, "<%s>%s</%s>\n",
-			      arg_name, (char *) encoded, arg_name);
+			      arg_name, encoded, arg_name);
 		os_free(encoded);
 	}
 
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 0ac5b28..671f5fe 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1745,7 +1745,8 @@
 			return -1;
 		}
 		os_free(wps->new_psk);
-		wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+		wps->new_psk = (u8 *) base64_encode(r, sizeof(r),
+						    &wps->new_psk_len);
 		if (wps->new_psk == NULL)
 			return -1;
 		wps->new_psk_len--; /* remove newline */
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index ca893a4..6e10e4b 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -519,8 +519,9 @@
 
 	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
 			      list) {
-		event_add(s, buf,
-			  sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
+		wps_upnp_event_add(
+			s, buf,
+			sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
 	}
 
 	wpabuf_free(buf);
@@ -541,7 +542,7 @@
 	struct upnp_wps_device_interface *iface;
 	wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
 	subscr_addr_free_all(s);
-	event_delete_all(s);
+	wps_upnp_event_delete_all(s);
 	dl_list_for_each(iface, &s->sm->interfaces,
 			 struct upnp_wps_device_interface, list)
 		upnp_er_remove_notification(iface->wps->registrar, s);
@@ -647,7 +648,7 @@
 			   "initial WLANEvent");
 		msg = build_fake_wsc_ack();
 		if (msg) {
-			s->sm->wlanevent = (char *)
+			s->sm->wlanevent =
 				base64_encode(wpabuf_head(msg),
 					      wpabuf_len(msg), NULL);
 			wpabuf_free(msg);
@@ -672,7 +673,7 @@
 		wpabuf_put_property(buf, "WLANEvent", wlan_event);
 	wpabuf_put_str(buf, tail);
 
-	ret = event_add(s, buf, 0);
+	ret = wps_upnp_event_add(s, buf, 0);
 	if (ret) {
 		wpabuf_free(buf);
 		return ret;
@@ -749,7 +750,7 @@
 		   "WPS UPnP: Subscription %p (SID %s) started with %s",
 		   s, str, callback_urls);
 	/* Schedule sending this */
-	event_send_all_later(sm);
+	wps_upnp_event_send_all_later(sm);
 	return s;
 }
 
@@ -822,7 +823,7 @@
 	}
 	raw_len = pos;
 
-	val = (char *) base64_encode(raw, raw_len, &val_len);
+	val = base64_encode(raw, raw_len, &val_len);
 	if (val == NULL)
 		goto fail;
 
@@ -987,7 +988,7 @@
 
 	advertisement_state_machine_stop(sm, 1);
 
-	event_send_stop_all(sm);
+	wps_upnp_event_send_stop_all(sm);
 	os_free(sm->wlanevent);
 	sm->wlanevent = NULL;
 	os_free(sm->ip_addr_text);
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
index 94aae75..d7e6edc 100644
--- a/src/wps/wps_upnp_event.c
+++ b/src/wps/wps_upnp_event.c
@@ -96,8 +96,8 @@
 }
 
 
-/* event_delete_all -- delete entire event queue and current event */
-void event_delete_all(struct subscription *s)
+/* wps_upnp_event_delete_all -- delete entire event queue and current event */
+void wps_upnp_event_delete_all(struct subscription *s)
 {
 	struct wps_event_ *e;
 	while ((e = event_dequeue(s)) != NULL)
@@ -134,11 +134,11 @@
 		event_delete(e);
 		s->last_event_failed = 1;
 		if (!dl_list_empty(&s->event_queue))
-			event_send_all_later(s->sm);
+			wps_upnp_event_send_all_later(s->sm);
 		return;
 	}
 	dl_list_add(&s->event_queue, &e->list);
-	event_send_all_later(sm);
+	wps_upnp_event_send_all_later(sm);
 }
 
 
@@ -228,7 +228,7 @@
 
 		/* Schedule sending more if there is more to send */
 		if (!dl_list_empty(&s->event_queue))
-			event_send_all_later(s->sm);
+			wps_upnp_event_send_all_later(s->sm);
 		break;
 	case HTTP_CLIENT_FAILED:
 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
@@ -328,19 +328,19 @@
 
 	if (nerrors) {
 		/* Try again later */
-		event_send_all_later(sm);
+		wps_upnp_event_send_all_later(sm);
 	}
 }
 
 
-/* event_send_all_later -- schedule sending events to all subscribers
+/* wps_upnp_event_send_all_later -- schedule sending events to all subscribers
  * that need it.
  * This avoids two problems:
  * -- After getting a subscription, we should not send the first event
  *      until after our reply is fully queued to be sent back,
  * -- Possible stack depth or infinite recursion issues.
  */
-void event_send_all_later(struct upnp_wps_device_sm *sm)
+void wps_upnp_event_send_all_later(struct upnp_wps_device_sm *sm)
 {
 	/*
 	 * The exact time in the future isn't too important. Waiting a bit
@@ -354,8 +354,8 @@
 }
 
 
-/* event_send_stop_all -- cleanup */
-void event_send_stop_all(struct upnp_wps_device_sm *sm)
+/* wps_upnp_event_send_stop_all -- cleanup */
+void wps_upnp_event_send_stop_all(struct upnp_wps_device_sm *sm)
 {
 	if (sm->event_send_all_queued)
 		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
@@ -364,13 +364,14 @@
 
 
 /**
- * event_add - Add a new event to a queue
+ * wps_upnp_event_add - Add a new event to a queue
  * @s: Subscription
  * @data: Event data (is copied; caller retains ownership)
  * @probereq: Whether this is a Probe Request event
  * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
  */
-int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
+int wps_upnp_event_add(struct subscription *s, const struct wpabuf *data,
+		       int probereq)
 {
 	struct wps_event_ *e;
 	unsigned int len;
@@ -416,6 +417,6 @@
 	wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
 		   "(queue len %u)", e, s, len + 1);
 	dl_list_add_tail(&s->event_queue, &e->list);
-	event_send_all_later(s->sm);
+	wps_upnp_event_send_all_later(s->sm);
 	return 0;
 }
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index 6a7c627..e87a932 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -177,10 +177,11 @@
 void web_listener_stop(struct upnp_wps_device_sm *sm);
 
 /* wps_upnp_event.c */
-int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
-void event_delete_all(struct subscription *s);
-void event_send_all_later(struct upnp_wps_device_sm *sm);
-void event_send_stop_all(struct upnp_wps_device_sm *sm);
+int wps_upnp_event_add(struct subscription *s, const struct wpabuf *data,
+		       int probereq);
+void wps_upnp_event_delete_all(struct subscription *s);
+void wps_upnp_event_send_all_later(struct upnp_wps_device_sm *sm);
+void wps_upnp_event_send_stop_all(struct upnp_wps_device_sm *sm);
 
 /* wps_upnp_ap.c */
 int upnp_er_set_selected_registrar(struct wps_registrar *reg,
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
index 7548e84..3c5a97c 100644
--- a/src/wps/wps_upnp_web.c
+++ b/src/wps/wps_upnp_web.c
@@ -765,8 +765,8 @@
 
 	if (reply) {
 		size_t len;
-		replydata = (char *) base64_encode(wpabuf_head(reply),
-						   wpabuf_len(reply), &len);
+		replydata = base64_encode(wpabuf_head(reply), wpabuf_len(reply),
+					  &len);
 	} else
 		replydata = NULL;
 
diff --git a/tests/test-eapol.c b/tests/test-eapol.c
deleted file mode 100644
index 0a0844b..0000000
--- a/tests/test-eapol.c
+++ /dev/null
@@ -1,606 +0,0 @@
-/*
- * Testing tool for EAPOL-Key Supplicant/Authenticator routines
- * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "utils/includes.h"
-
-#include "utils/common.h"
-#include "utils/eloop.h"
-#include "rsn_supp/wpa.h"
-#include "ap/wpa_auth.h"
-
-
-struct wpa {
-	enum { AUTH, SUPP } test_peer;
-	enum { READ, WRITE } test_oper;
-	FILE *f;
-	int wpa1;
-
-	u8 auth_addr[ETH_ALEN];
-	u8 supp_addr[ETH_ALEN];
-	u8 psk[PMK_LEN];
-
-	/* from authenticator */
-	u8 *auth_eapol;
-	size_t auth_eapol_len;
-
-	/* from supplicant */
-	u8 *supp_eapol;
-	size_t supp_eapol_len;
-
-	struct wpa_sm *supp;
-	struct wpa_authenticator *auth_group;
-	struct wpa_state_machine *auth;
-
-	u8 supp_ie[80];
-	size_t supp_ie_len;
-
-	int key_request_done;
-	int key_request_done1;
-	int auth_sent;
-};
-
-
-const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
-
-
-static int auth_read_msg(struct wpa *wpa);
-static void supp_eapol_key_request(void *eloop_data, void *user_ctx);
-
-
-static void usage(void) {
-	wpa_printf(MSG_INFO,
-		   "usage: test-eapol <auth/supp> <read/write> <file>");
-	exit(-1);
-}
-
-
-static void write_msg(FILE *f, const u8 *msg, size_t msg_len)
-{
-	u8 len[2];
-
-	wpa_printf(MSG_DEBUG, "TEST: Write message to file (msg_len=%u)",
-		   (unsigned int) msg_len);
-	WPA_PUT_BE16(len, msg_len);
-	fwrite(len, 2, 1, f);
-	fwrite(msg, msg_len, 1, f);
-}
-
-
-static u8 * read_msg(FILE *f, size_t *ret_len)
-{
-	u8 len[2];
-	u16 msg_len;
-	u8 *msg;
-
-	if (fread(len, 2, 1, f) != 1) {
-		wpa_printf(MSG_ERROR, "TEST-ERROR: Could not read msg len");
-		eloop_terminate();
-		return NULL;
-	}
-	msg_len = WPA_GET_BE16(len);
-
-	msg = os_malloc(msg_len);
-	if (!msg)
-		return NULL;
-	if (msg_len > 0 && fread(msg, msg_len, 1, f) != 1) {
-		wpa_printf(MSG_ERROR, "TEST-ERROR: Truncated msg (msg_len=%u)",
-			   msg_len);
-		os_free(msg);
-		eloop_terminate();
-		return NULL;
-	}
-	wpa_hexdump(MSG_DEBUG, "TEST: Read message from file", msg, msg_len);
-
-	*ret_len = msg_len;
-	return msg;
-}
-
-
-static int supp_get_bssid(void *ctx, u8 *bssid)
-{
-	struct wpa *wpa = ctx;
-	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-	os_memcpy(bssid, wpa->auth_addr, ETH_ALEN);
-	return 0;
-}
-
-
-static void supp_set_state(void *ctx, enum wpa_states state)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state);
-}
-
-
-static void auth_eapol_rx(void *eloop_data, void *user_ctx)
-{
-	struct wpa *wpa = eloop_data;
-
-	wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame");
-	wpa->auth_sent = 0;
-	wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol,
-		    wpa->supp_eapol_len);
-	if (!wpa->auth_sent && wpa->test_peer == SUPP &&
-	    wpa->test_oper == READ) {
-		/* Speed up process by not going through retransmit timeout */
-		wpa_printf(MSG_DEBUG,
-			   "AUTH: No response was sent - process next message");
-		auth_read_msg(wpa);
-	}
-	if (wpa->wpa1 && wpa->key_request_done && !wpa->key_request_done1) {
-		wpa->key_request_done1 = 1;
-		eloop_register_timeout(0, 0, supp_eapol_key_request,
-				       wpa, NULL);
-	}
-
-}
-
-
-static void supp_eapol_rx(void *eloop_data, void *user_ctx)
-{
-	struct wpa *wpa = eloop_data;
-
-	wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame");
-	wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol,
-			wpa->auth_eapol_len);
-}
-
-
-static int supp_read_msg(struct wpa *wpa)
-{
-	os_free(wpa->auth_eapol);
-	wpa->auth_eapol = read_msg(wpa->f, &wpa->auth_eapol_len);
-	if (!wpa->auth_eapol)
-		return -1;
-	eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL);
-	return 0;
-}
-
-
-static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
-			   size_t len)
-{
-	struct wpa *wpa = ctx;
-
-	wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
-		   "len=%lu)",
-		   __func__, MAC2STR(dest), proto, (unsigned long) len);
-
-	if (wpa->test_peer == SUPP && wpa->test_oper == WRITE)
-		write_msg(wpa->f, buf, len);
-
-	if (wpa->test_peer == AUTH && wpa->test_oper == READ)
-		return supp_read_msg(wpa);
-
-	os_free(wpa->supp_eapol);
-	wpa->supp_eapol = os_malloc(len);
-	if (!wpa->supp_eapol)
-		return -1;
-	os_memcpy(wpa->supp_eapol, buf, len);
-	wpa->supp_eapol_len = len;
-	eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL);
-
-	return 0;
-}
-
-
-static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
-			     u16 data_len, size_t *msg_len, void **data_pos)
-{
-	struct ieee802_1x_hdr *hdr;
-
-	wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
-		   __func__, type, data_len);
-
-	*msg_len = sizeof(*hdr) + data_len;
-	hdr = os_malloc(*msg_len);
-	if (hdr == NULL)
-		return NULL;
-
-	hdr->version = 2;
-	hdr->type = type;
-	hdr->length = host_to_be16(data_len);
-
-	if (data)
-		os_memcpy(hdr + 1, data, data_len);
-	else
-		os_memset(hdr + 1, 0, data_len);
-
-	if (data_pos)
-		*data_pos = hdr + 1;
-
-	return (u8 *) hdr;
-}
-
-
-static int supp_get_beacon_ie(void *ctx)
-{
-	struct wpa *wpa = ctx;
-	const u8 *ie;
-	size_t ielen;
-
-	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-
-	ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen);
-	if (ie == NULL || ielen < 1)
-		return -1;
-	if (ie[0] == WLAN_EID_RSN)
-		return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]);
-	return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]);
-}
-
-
-static int supp_set_key(void *ctx, enum wpa_alg alg,
-			const u8 *addr, int key_idx, int set_tx,
-			const u8 *seq, size_t seq_len,
-			const u8 *key, size_t key_len)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
-		   "set_tx=%d)",
-		   __func__, alg, MAC2STR(addr), key_idx, set_tx);
-	wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
-	wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
-	return 0;
-}
-
-
-static int supp_mlme_setprotection(void *ctx, const u8 *addr,
-				   int protection_type, int key_type)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
-		   "key_type=%d)",
-		   __func__, MAC2STR(addr), protection_type, key_type);
-	return 0;
-}
-
-
-static void supp_cancel_auth_timeout(void *ctx)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-}
-
-
-static void * supp_get_network_ctx(void *ctx)
-{
-	return (void *) 1;
-}
-
-
-static void supp_deauthenticate(void *ctx, int reason_code)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s(%d)", __func__, reason_code);
-}
-
-
-static enum wpa_states supp_get_state(void *ctx)
-{
-	return WPA_COMPLETED;
-}
-
-
-static int supp_init(struct wpa *wpa)
-{
-	struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
-
-	if (!ctx)
-		return -1;
-
-	ctx->ctx = wpa;
-	ctx->msg_ctx = wpa;
-	ctx->set_state = supp_set_state;
-	ctx->get_bssid = supp_get_bssid;
-	ctx->ether_send = supp_ether_send;
-	ctx->get_beacon_ie = supp_get_beacon_ie;
-	ctx->alloc_eapol = supp_alloc_eapol;
-	ctx->set_key = supp_set_key;
-	ctx->mlme_setprotection = supp_mlme_setprotection;
-	ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
-	ctx->get_network_ctx = supp_get_network_ctx;
-	ctx->deauthenticate = supp_deauthenticate;
-	ctx->get_state = supp_get_state;
-	wpa->supp = wpa_sm_init(ctx);
-	if (!wpa->supp) {
-		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
-		return -1;
-	}
-
-	wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr);
-	if (wpa->wpa1) {
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 0);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_WPA);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE,
-				 WPA_CIPHER_TKIP);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_TKIP);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT,
-				 WPA_KEY_MGMT_PSK);
-	} else {
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE,
-				 WPA_CIPHER_CCMP);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT,
-				 WPA_KEY_MGMT_PSK);
-		wpa_sm_set_param(wpa->supp, WPA_PARAM_MFP,
-				 MGMT_FRAME_PROTECTION_OPTIONAL);
-	}
-	wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN, NULL, NULL);
-
-	wpa->supp_ie_len = sizeof(wpa->supp_ie);
-	if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie,
-					    &wpa->supp_ie_len) < 0) {
-		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
-			   " failed");
-		return -1;
-	}
-
-	wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr);
-
-	return 0;
-}
-
-
-static void auth_logger(void *ctx, const u8 *addr, logger_level level,
-			const char *txt)
-{
-	if (addr)
-		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
-			   MAC2STR(addr), txt);
-	else
-		wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
-}
-
-
-static int auth_read_msg(struct wpa *wpa)
-{
-	os_free(wpa->supp_eapol);
-	wpa->supp_eapol = read_msg(wpa->f, &wpa->supp_eapol_len);
-	if (!wpa->supp_eapol)
-		return -1;
-	eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL);
-	return 0;
-}
-
-
-static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
-			   size_t data_len, int encrypt)
-{
-	struct wpa *wpa = ctx;
-
-	wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
-		   "encrypt=%d)",
-		   __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
-	wpa->auth_sent = 1;
-
-	if (wpa->test_peer == AUTH && wpa->test_oper == WRITE)
-		write_msg(wpa->f, data, data_len);
-
-	if (wpa->test_peer == SUPP && wpa->test_oper == READ)
-		return auth_read_msg(wpa);
-
-	os_free(wpa->auth_eapol);
-	wpa->auth_eapol = os_malloc(data_len);
-	if (!wpa->auth_eapol)
-		return -1;
-	os_memcpy(wpa->auth_eapol, data, data_len);
-	wpa->auth_eapol_len = data_len;
-	eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL);
-
-	return 0;
-}
-
-
-static const u8 * auth_get_psk(void *ctx, const u8 *addr,
-			       const u8 *p2p_dev_addr, const u8 *prev_psk,
-			       size_t *psk_len)
-{
-	struct wpa *wpa = ctx;
-
-	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
-		   __func__, MAC2STR(addr), prev_psk);
-	if (psk_len)
-		*psk_len = PMK_LEN;
-	if (prev_psk)
-		return NULL;
-	return wpa->psk;
-}
-
-
-static void supp_eapol_key_request(void *eloop_data, void *user_ctx)
-{
-	struct wpa *wpa = eloop_data;
-
-	wpa_printf(MSG_DEBUG, "SUPP: EAPOL-Key Request trigger");
-	if (wpa->test_peer == SUPP && wpa->test_oper == READ) {
-		if (!eloop_is_timeout_registered(auth_eapol_rx, wpa, NULL))
-			auth_read_msg(wpa);
-	} else {
-		wpa_sm_key_request(wpa->supp, 0, 1);
-	}
-}
-
-
-static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
-			const u8 *addr, int idx, u8 *key,
-			size_t key_len)
-{
-	struct wpa *wpa = ctx;
-
-	wpa_printf(MSG_DEBUG, "AUTH: %s (vlan_id=%d alg=%d idx=%d key_len=%d)",
-		   __func__, vlan_id, alg, idx, (int) key_len);
-	if (addr)
-		wpa_printf(MSG_DEBUG, "AUTH: addr=" MACSTR, MAC2STR(addr));
-
-	if (alg != WPA_ALG_NONE && idx == 0 && key_len > 0 &&
-	    !wpa->key_request_done) {
-		wpa_printf(MSG_DEBUG, "Test EAPOL-Key Request");
-		wpa->key_request_done = 1;
-		if (!wpa->wpa1)
-			eloop_register_timeout(0, 0, supp_eapol_key_request,
-					       wpa, NULL);
-	}
-
-	return 0;
-}
-
-
-static int auth_init_group(struct wpa *wpa)
-{
-	struct wpa_auth_config conf;
-	struct wpa_auth_callbacks cb;
-
-	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
-
-	os_memset(&conf, 0, sizeof(conf));
-	if (wpa->wpa1) {
-		conf.wpa = 1;
-		conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
-		conf.wpa_pairwise = WPA_CIPHER_TKIP;
-		conf.wpa_group = WPA_CIPHER_TKIP;
-	} else {
-		conf.wpa = 2;
-		conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
-		conf.wpa_pairwise = WPA_CIPHER_CCMP;
-		conf.rsn_pairwise = WPA_CIPHER_CCMP;
-		conf.wpa_group = WPA_CIPHER_CCMP;
-		conf.ieee80211w = 2;
-		conf.group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
-	}
-	conf.eapol_version = 2;
-	conf.wpa_group_update_count = 4;
-	conf.wpa_pairwise_update_count = 4;
-
-	os_memset(&cb, 0, sizeof(cb));
-	cb.logger = auth_logger;
-	cb.send_eapol = auth_send_eapol;
-	cb.get_psk = auth_get_psk;
-	cb.set_key = auth_set_key,
-
-	wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb, wpa);
-	if (!wpa->auth_group) {
-		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int auth_init(struct wpa *wpa)
-{
-	if (wpa->test_peer == AUTH && wpa->test_oper == READ)
-		return supp_read_msg(wpa);
-
-	wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL);
-	if (!wpa->auth) {
-		wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
-		return -1;
-	}
-
-	if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie,
-				wpa->supp_ie_len, NULL, 0, NULL, 0) !=
-	    WPA_IE_OK) {
-		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
-		return -1;
-	}
-
-	wpa_auth_sm_event(wpa->auth, WPA_ASSOC);
-
-	wpa_auth_sta_associated(wpa->auth_group, wpa->auth);
-
-	return 0;
-}
-
-
-static void deinit(struct wpa *wpa)
-{
-	wpa_auth_sta_deinit(wpa->auth);
-	wpa_sm_deinit(wpa->supp);
-	wpa_deinit(wpa->auth_group);
-	os_free(wpa->auth_eapol);
-	wpa->auth_eapol = NULL;
-	os_free(wpa->supp_eapol);
-	wpa->supp_eapol = NULL;
-}
-
-
-int main(int argc, char *argv[])
-{
-	const char *file;
-	int ret;
-	struct wpa wpa;
-
-	if (os_program_init())
-		return -1;
-
-	wpa_debug_level = 0;
-	wpa_debug_show_keys = 1;
-	os_memset(&wpa, 0, sizeof(wpa));
-
-	if (argc < 4)
-		usage();
-
-	if (os_strcmp(argv[1], "auth") == 0) {
-		wpa.test_peer = AUTH;
-	} else if (os_strcmp(argv[1], "auth1") == 0) {
-		wpa.test_peer = AUTH;
-		wpa.wpa1 = 1;
-	} else if (os_strcmp(argv[1], "supp") == 0) {
-		wpa.test_peer = SUPP;
-	} else if (os_strcmp(argv[1], "supp1") == 0) {
-		wpa.test_peer = SUPP;
-		wpa.wpa1 = 1;
-	} else {
-		usage();
-	}
-
-	if (os_strcmp(argv[2], "read") == 0)
-		wpa.test_oper = READ;
-	else if (os_strcmp(argv[2], "write") == 0)
-		wpa.test_oper = WRITE;
-	else
-		usage();
-
-	file = argv[3];
-
-	wpa.f = fopen(file, wpa.test_oper == READ ? "r" : "w");
-	if (!wpa.f)
-		return -1;
-
-	os_memset(wpa.auth_addr, 0x12, ETH_ALEN);
-	os_memset(wpa.supp_addr, 0x32, ETH_ALEN);
-	os_memset(wpa.psk, 0x44, PMK_LEN);
-
-	if (eloop_init()) {
-		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
-		goto fail;
-	}
-
-	if (auth_init_group(&wpa) < 0)
-		goto fail;
-
-	if (supp_init(&wpa) < 0)
-		goto fail;
-
-	if (auth_init(&wpa) < 0)
-		goto fail;
-
-	wpa_printf(MSG_DEBUG, "Starting eloop");
-	eloop_run();
-	wpa_printf(MSG_DEBUG, "eloop done");
-
-	ret = 0;
-fail:
-	deinit(&wpa);
-	fclose(wpa.f);
-
-	eloop_destroy();
-
-	os_program_deinit();
-
-	return ret;
-}
diff --git a/tests/test-json.c b/tests/test-json.c
deleted file mode 100644
index c7cb460..0000000
--- a/tests/test-json.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * JSON parser - test program
- * Copyright (c) 2019, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "utils/includes.h"
-#include "utils/os.h"
-#include "utils/json.h"
-
-
-int main(int argc, char *argv[])
-{
-	char *buf;
-	size_t len;
-	struct json_token *root;
-
-	if (argc < 2)
-		return -1;
-
-	buf = os_readfile(argv[1], &len);
-	if (!buf)
-		return -1;
-
-	root = json_parse(buf, len);
-	os_free(buf);
-	if (root) {
-		size_t buflen = 10000;
-
-		buf = os_zalloc(buflen);
-		if (buf) {
-			json_print_tree(root, buf, buflen);
-			printf("%s\n", buf);
-			os_free(buf);
-		}
-		json_free(root);
-	} else {
-		printf("JSON parsing failed\n");
-	}
-
-	return 0;
-}
diff --git a/tests/test-tls.c b/tests/test-tls.c
deleted file mode 100644
index 9941fb5..0000000
--- a/tests/test-tls.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Testing tool for TLSv1 client/server routines
- * Copyright (c) 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 "crypto/tls.h"
-
-
-static void usage(void) {
-	wpa_printf(MSG_INFO,
-		   "usage: test-tls <server/client> <read/write> <file>");
-	exit(-1);
-}
-
-
-static void write_msg(FILE *f, struct wpabuf *msg)
-{
-	u8 len[2];
-
-	wpa_printf(MSG_DEBUG, "TEST: Write message to file (msg_len=%u)",
-		   (unsigned int) wpabuf_len(msg));
-	WPA_PUT_BE16(len, wpabuf_len(msg));
-	fwrite(len, 2, 1, f);
-	fwrite(wpabuf_head(msg), wpabuf_len(msg), 1, f);
-}
-
-
-static struct wpabuf * read_msg(FILE *f)
-{
-	u8 len[2];
-	u16 msg_len;
-	struct wpabuf *msg;
-
-	if (fread(len, 2, 1, f) != 1) {
-		wpa_printf(MSG_ERROR, "TEST-ERROR: Could not read msg len");
-		return NULL;
-	}
-	msg_len = WPA_GET_BE16(len);
-
-	msg = wpabuf_alloc(msg_len);
-	if (!msg)
-		return NULL;
-	if (msg_len > 0 &&
-	    fread(wpabuf_put(msg, msg_len), msg_len, 1, f) != 1) {
-		wpa_printf(MSG_ERROR, "TEST-ERROR: Truncated msg (msg_len=%u)",
-			   msg_len);
-		wpabuf_free(msg);
-		return NULL;
-	}
-	wpa_hexdump_buf(MSG_DEBUG, "TEST: Read message from file", msg);
-
-	return msg;
-}
-
-
-int main(int argc, char *argv[])
-{
-	struct tls_config conf;
-	void *tls_server, *tls_client;
-	struct tls_connection_params params;
-	struct tls_connection *conn_server = NULL, *conn_client = NULL;
-	int ret = -1;
-	struct wpabuf *in = NULL, *out = NULL, *appl;
-	enum { SERVER, CLIENT } test_peer;
-	enum { READ, WRITE } test_oper;
-	const char *file;
-	FILE *f;
-
-	wpa_debug_level = 0;
-	wpa_debug_show_keys = 1;
-
-	if (argc < 4)
-		usage();
-
-	if (os_strcmp(argv[1], "server") == 0)
-		test_peer = SERVER;
-	else if (os_strcmp(argv[1], "client") == 0)
-		test_peer = CLIENT;
-	else
-		usage();
-
-	if (os_strcmp(argv[2], "read") == 0)
-		test_oper = READ;
-	else if (os_strcmp(argv[2], "write") == 0)
-		test_oper = WRITE;
-	else
-		usage();
-
-	file = argv[3];
-
-	f = fopen(file, test_oper == READ ? "r" : "w");
-	if (!f)
-		return -1;
-
-	os_memset(&conf, 0, sizeof(conf));
-	tls_server = tls_init(&conf);
-	tls_client = tls_init(&conf);
-	if (!tls_server || !tls_client)
-		goto fail;
-
-	os_memset(&params, 0, sizeof(params));
-	params.ca_cert = "hwsim/auth_serv/ca.pem";
-	params.client_cert = "hwsim/auth_serv/server.pem";
-	params.private_key = "hwsim/auth_serv/server.key";
-	params.dh_file = "hwsim/auth_serv/dh.conf";
-
-	if (tls_global_set_params(tls_server, &params)) {
-		wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
-		goto fail;
-	}
-
-	conn_server = tls_connection_init(tls_server);
-	conn_client = tls_connection_init(tls_client);
-	if (!conn_server || !conn_client)
-		goto fail;
-
-	in = NULL;
-	for (;;) {
-		appl = NULL;
-		if (test_peer == CLIENT && test_oper == READ)
-			out = read_msg(f);
-		else
-			out = tls_connection_handshake(tls_client, conn_client,
-						       in, &appl);
-		wpabuf_free(in);
-		in = NULL;
-		if (!out)
-			goto fail;
-		if (test_peer == CLIENT && test_oper == WRITE &&
-		    wpabuf_len(out) > 0)
-			write_msg(f, out);
-		if (!(test_peer == CLIENT && test_oper == READ) &&
-		    tls_connection_get_failed(tls_client, conn_client)) {
-			wpa_printf(MSG_ERROR, "TLS handshake failed");
-			goto fail;
-		}
-		if (((test_peer == CLIENT && test_oper == READ) ||
-		     tls_connection_established(tls_client, conn_client)) &&
-		    ((test_peer == SERVER && test_oper == READ) ||
-		     tls_connection_established(tls_server, conn_server)))
-			break;
-
-		appl = NULL;
-		if (test_peer == SERVER && test_oper == READ)
-			in = read_msg(f);
-		else
-			in = tls_connection_server_handshake(tls_server,
-							     conn_server,
-							     out, &appl);
-		wpabuf_free(out);
-		out = NULL;
-		if (!in)
-			goto fail;
-		if (test_peer == SERVER && test_oper == WRITE)
-			write_msg(f, in);
-		if (!(test_peer == SERVER && test_oper == READ) &&
-		    tls_connection_get_failed(tls_server, conn_server)) {
-			wpa_printf(MSG_ERROR, "TLS handshake failed");
-			goto fail;
-		}
-		if (((test_peer == CLIENT && test_oper == READ) ||
-		     tls_connection_established(tls_client, conn_client)) &&
-		    ((test_peer == SERVER && test_oper == READ) ||
-		     tls_connection_established(tls_server, conn_server)))
-			break;
-	}
-
-	wpabuf_free(in);
-	in = wpabuf_alloc(100);
-	if (!in)
-		goto fail;
-	wpabuf_put_str(in, "PING");
-	wpabuf_free(out);
-	if (test_peer == CLIENT && test_oper == READ)
-		out = read_msg(f);
-	else
-		out = tls_connection_encrypt(tls_client, conn_client, in);
-	wpabuf_free(in);
-	in = NULL;
-	if (!out)
-		goto fail;
-	if (test_peer == CLIENT && test_oper == WRITE)
-		write_msg(f, out);
-
-	if (!(test_peer == SERVER && test_oper == READ)) {
-		in = tls_connection_decrypt(tls_server, conn_server, out);
-		wpabuf_free(out);
-		out = NULL;
-		if (!in)
-			goto fail;
-		wpa_hexdump_buf(MSG_DEBUG, "Server decrypted ApplData", in);
-	}
-
-	wpabuf_free(in);
-	in = wpabuf_alloc(100);
-	if (!in)
-		goto fail;
-	wpabuf_put_str(in, "PONG");
-	wpabuf_free(out);
-	if (test_peer == SERVER && test_oper == READ)
-		out = read_msg(f);
-	else
-		out = tls_connection_encrypt(tls_server, conn_server, in);
-	wpabuf_free(in);
-	in = NULL;
-	if (!out)
-		goto fail;
-	if (test_peer == SERVER && test_oper == WRITE)
-		write_msg(f, out);
-
-	if (!(test_peer == CLIENT && test_oper == READ)) {
-		in = tls_connection_decrypt(tls_client, conn_client, out);
-		wpabuf_free(out);
-		out = NULL;
-		if (!in)
-			goto fail;
-		wpa_hexdump_buf(MSG_DEBUG, "Client decrypted ApplData", in);
-	}
-
-	ret = 0;
-fail:
-	if (tls_server) {
-		if (conn_server)
-			tls_connection_deinit(tls_server, conn_server);
-		tls_deinit(tls_server);
-	}
-	if (tls_client) {
-		if (conn_client)
-			tls_connection_deinit(tls_server, conn_client);
-		tls_deinit(tls_client);
-	}
-	wpabuf_free(in);
-	wpabuf_free(out);
-	fclose(f);
-
-	return ret;
-}
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 8bcd7ef..309127e 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -95,7 +95,6 @@
 INCLUDES += $(LOCAL_PATH)/src/eapol_supp
 INCLUDES += $(LOCAL_PATH)/src/eap_peer
 INCLUDES += $(LOCAL_PATH)/src/eap_server
-INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw
 INCLUDES += $(LOCAL_PATH)/src/l2_packet
 INCLUDES += $(LOCAL_PATH)/src/radius
 INCLUDES += $(LOCAL_PATH)/src/rsn_supp
@@ -124,6 +123,7 @@
 OBJS += src/utils/wpa_debug.c
 OBJS += src/utils/wpabuf.c
 OBJS += src/utils/bitfield.c
+OBJS += src/utils/ip_addr.c
 OBJS += wmm_ac.c
 OBJS += op_classes.c
 OBJS += rrm.c
@@ -229,8 +229,6 @@
 
 ifdef CONFIG_SUITEB
 L_CFLAGS += -DCONFIG_SUITEB
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_SUITEB192
@@ -241,25 +239,15 @@
 ifdef CONFIG_OCV
 L_CFLAGS += -DCONFIG_OCV
 OBJS += src/common/ocv.c
-CONFIG_IEEE80211W=y
-endif
-
-ifdef CONFIG_IEEE80211W
-L_CFLAGS += -DCONFIG_IEEE80211W
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_IEEE80211R
 L_CFLAGS += -DCONFIG_IEEE80211R
 OBJS += src/rsn_supp/wpa_ft.c
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_MESH
 NEED_80211_COMMON=y
-NEED_SHA256=y
 NEED_AES_SIV=y
 CONFIG_SAE=y
 CONFIG_AP=y
@@ -274,6 +262,10 @@
 OBJS += src/common/sae.c
 NEED_ECC=y
 NEED_DH_GROUPS=y
+NEED_DRAGONFLY=y
+ifdef CONFIG_TESTING_OPTIONS
+NEED_DH_GROUPS_ALL=y
+endif
 endif
 
 ifdef CONFIG_DPP
@@ -284,9 +276,9 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
+NEED_ECC=y
 NEED_JSON=y
 NEED_GAS_SERVER=y
 NEED_BASE64=y
@@ -301,11 +293,14 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
 endif
 
+ifdef CONFIG_WAPI_INTERFACE
+L_CFLAGS += -DCONFIG_WAPI_INTERFACE
+endif
+
 ifdef CONFIG_FILS
 L_CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
@@ -328,8 +323,6 @@
 ifdef CONFIG_TDLS
 L_CFLAGS += -DCONFIG_TDLS
 OBJS += src/rsn_supp/tdls.c
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_TDLS_TESTING
@@ -394,7 +387,6 @@
 OBJS += hs20_supplicant.c
 L_CFLAGS += -DCONFIG_HS20
 CONFIG_INTERWORKING=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_INTERWORKING
@@ -461,7 +453,6 @@
 
 ifdef CONFIG_ERP
 L_CFLAGS += -DCONFIG_ERP
-NEED_SHA256=y
 NEED_HMAC_SHA256_KDF=y
 endif
 
@@ -616,7 +607,6 @@
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_AES=y
-NEED_AES_OMAC1=y
 NEED_AES_ENCBLOCK=y
 NEED_AES_EAX=y
 endif
@@ -649,7 +639,6 @@
 else
 L_CFLAGS += -DEAP_AKA_PRIME
 endif
-NEED_SHA256=y
 endif
 
 ifdef CONFIG_EAP_SIM_COMMON
@@ -674,6 +663,25 @@
 NEED_T_PRF=y
 endif
 
+ifdef CONFIG_EAP_TEAP
+# EAP-TEAP
+ifeq ($(CONFIG_EAP_TEAP), dyn)
+L_CFLAGS += -DEAP_YEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_teap.so
+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_common/eap_teap_common.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_TLS_PRF_SHA256=y
+NEED_TLS_PRF_SHA384=y
+endif
+
 ifdef CONFIG_EAP_PAX
 # EAP-PAX
 ifeq ($(CONFIG_EAP_PAX), dyn)
@@ -711,16 +719,14 @@
 ifdef CONFIG_EAP_GPSK_SHA256
 L_CFLAGS += -DEAP_GPSK_SHA256
 endif
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_EAP_PWD
 L_CFLAGS += -DEAP_PWD
 OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
 CONFIG_IEEE8021X_EAPOL=y
-NEED_SHA256=y
 NEED_ECC=y
+NEED_DRAGONFLY=y
 endif
 
 ifdef CONFIG_EAP_EKE
@@ -735,7 +741,6 @@
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_DH_GROUPS_ALL=y
-NEED_SHA256=y
 NEED_AES_CBC=y
 endif
 
@@ -755,7 +760,6 @@
 OBJS += src/wps/wps_registrar.c
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
@@ -872,7 +876,6 @@
 OBJS += src/ap/utils.c
 OBJS += src/ap/authsrv.c
 OBJS += src/ap/ap_config.c
-OBJS += src/utils/ip_addr.c
 OBJS += src/ap/sta_info.c
 OBJS += src/ap/tkip_countermeasures.c
 OBJS += src/ap/ap_mlme.c
@@ -941,8 +944,12 @@
 ifdef CONFIG_DPP
 OBJS += src/ap/dpp_hostapd.c
 OBJS += src/ap/gas_query_ap.c
+NEED_AP_GAS_SERV=y
 endif
 ifdef CONFIG_INTERWORKING
+NEED_AP_GAS_SERV=y
+endif
+ifdef NEED_AP_GAS_SERV
 OBJS += src/ap/gas_serv.c
 endif
 ifdef CONFIG_HS20
@@ -1010,6 +1017,10 @@
 L_CFLAGS += -DCONFIG_SMARTCARD
 endif
 
+ifdef NEED_DRAGONFLY
+OBJS += src/common/dragonfly.c
+endif
+
 ifdef MS_FUNCS
 OBJS += src/crypto/ms_funcs.c
 NEED_DES=y
@@ -1042,7 +1053,6 @@
 
 ifdef CONFIG_TLSV12
 L_CFLAGS += -DCONFIG_TLSV12
-NEED_SHA256=y
 endif
 
 ifeq ($(CONFIG_TLS), openssl)
@@ -1057,7 +1067,6 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += src/crypto/fips_prf_openssl.c
 endif
-NEED_SHA256=y
 NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_p += -lcrypto
@@ -1120,7 +1129,6 @@
 OBJS += src/tls/pkcs1.c
 OBJS += src/tls/pkcs5.c
 OBJS += src/tls/pkcs8.c
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
 ifdef CONFIG_TLSV12
@@ -1240,12 +1248,10 @@
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_SIV
 AESOBJS += src/crypto/aes-siv.c
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += src/crypto/aes-ctr.c
@@ -1253,14 +1259,12 @@
 ifdef NEED_AES_ENCBLOCK
 AESOBJS += src/crypto/aes-encblock.c
 endif
-ifdef NEED_AES_OMAC1
 NEED_AES_ENC=y
 ifdef CONFIG_OPENSSL_CMAC
 L_CFLAGS += -DCONFIG_OPENSSL_CMAC
 else
 AESOBJS += src/crypto/aes-omac1.c
 endif
-endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
 ifdef NEED_INTERNAL_AES_WRAP
@@ -1353,7 +1357,6 @@
 endif
 
 SHA256OBJS = # none by default
-ifdef NEED_SHA256
 L_CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), gnutls)
@@ -1375,6 +1378,9 @@
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += src/crypto/sha256-tlsprf.c
 endif
+ifdef NEED_TLS_PRF_SHA384
+SHA256OBJS += src/crypto/sha384-tlsprf.c
+endif
 ifdef NEED_HMAC_SHA256_KDF
 L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 SHA256OBJS += src/crypto/sha256-kdf.c
@@ -1388,7 +1394,6 @@
 SHA256OBJS += src/crypto/sha512-kdf.c
 endif
 OBJS += $(SHA256OBJS)
-endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
 ifneq ($(CONFIG_TLS), openssl)
@@ -1479,6 +1484,7 @@
 ifdef CONFIG_CTRL_IFACE_HIDL
 WPA_SUPPLICANT_USE_HIDL=y
 L_CFLAGS += -DCONFIG_HIDL -DCONFIG_CTRL_IFACE_HIDL
+HIDL_INTERFACE_VERSION := 1.3
 endif
 
 ifdef CONFIG_READLINE
@@ -1640,9 +1646,6 @@
 OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c
 OBJS_t += src/radius/radius_client.c
 OBJS_t += src/radius/radius.c
-ifndef CONFIG_AP
-OBJS_t += src/utils/ip_addr.c
-endif
 OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c
 OBJS += $(CONFIG_MAIN).c
 
@@ -1733,8 +1736,13 @@
 LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.0
 LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.1
 LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.2
-LOCAL_SHARED_LIBRARIES += libhidlbase libhidltransport libhwbinder libutils libbase
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.3
+LOCAL_SHARED_LIBRARIES += libhidlbase libutils libbase
 LOCAL_STATIC_LIBRARIES += libwpa_hidl
+LOCAL_VINTF_FRAGMENTS := hidl/$(HIDL_INTERFACE_VERSION)/manifest.xml
+ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)
+LOCAL_INIT_RC=hidl/$(HIDL_INTERFACE_VERSION)/android.hardware.wifi.supplicant-service.rc
+endif
 endif
 include $(BUILD_EXECUTABLE)
 
@@ -1783,7 +1791,6 @@
 LOCAL_CPPFLAGS := $(L_CPPFLAGS)
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_C_INCLUDES := $(INCLUDES)
-HIDL_INTERFACE_VERSION = 1.2
 LOCAL_SRC_FILES := \
     hidl/$(HIDL_INTERFACE_VERSION)/hidl.cpp \
     hidl/$(HIDL_INTERFACE_VERSION)/hidl_manager.cpp \
@@ -1797,9 +1804,9 @@
     android.hardware.wifi.supplicant@1.0 \
     android.hardware.wifi.supplicant@1.1 \
     android.hardware.wifi.supplicant@1.2 \
+    android.hardware.wifi.supplicant@1.3 \
     libbase \
     libhidlbase \
-    libhidltransport \
     libutils \
     liblog \
     libssl
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index bf4daaa..f82e5e0 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,103 @@
 ChangeLog for wpa_supplicant
 
+2019-08-07 - v2.9
+	* SAE changes
+	  - disable use of groups using Brainpool curves
+	  - improved protection against side channel attacks
+	  [https://w1.fi/security/2019-6/]
+	* EAP-pwd changes
+	  - disable use of groups using Brainpool curves
+	  - allow the set of groups to be configured (eap_pwd_groups)
+	  - improved protection against side channel attacks
+	  [https://w1.fi/security/2019-6/]
+	* fixed FT-EAP initial mobility domain association using PMKSA caching
+	  (disabled by default for backwards compatibility; can be enabled
+	  with ft_eap_pmksa_caching=1)
+	* fixed a regression in OpenSSL 1.1+ engine loading
+	* added validation of RSNE in (Re)Association Response frames
+	* fixed DPP bootstrapping URI parser of channel list
+	* extended EAP-SIM/AKA fast re-authentication to allow use with FILS
+	* extended ca_cert_blob to support PEM format
+	* improved robustness of P2P Action frame scheduling
+	* added support for EAP-SIM/AKA using anonymous@realm identity
+	* fixed Hotspot 2.0 credential selection based on roaming consortium
+	  to ignore credentials without a specific EAP method
+	* added experimental support for EAP-TEAP peer (RFC 7170)
+	* added experimental support for EAP-TLS peer with TLS v1.3
+	* fixed a regression in WMM parameter configuration for a TDLS peer
+	* fixed a regression in operation with drivers that offload 802.1X
+	  4-way handshake
+	* fixed an ECDH operation corner case with OpenSSL
+
+2019-04-21 - v2.8
+	* SAE changes
+	  - added support for SAE Password Identifier
+	  - changed default configuration to enable only groups 19, 20, 21
+	    (i.e., disable groups 25 and 26) and disable all unsuitable groups
+	    completely based on REVmd changes
+	  - do not regenerate PWE unnecessarily when the AP uses the
+	    anti-clogging token mechanisms
+	  - fixed some association cases where both SAE and FT-SAE were enabled
+	    on both the station and the selected AP
+	  - started to prefer FT-SAE over SAE AKM if both are enabled
+	  - started to prefer FT-SAE over FT-PSK if both are enabled
+	  - fixed FT-SAE when SAE PMKSA caching is used
+	  - reject use of unsuitable groups based on new implementation guidance
+	    in REVmd (allow only FFC groups with prime >= 3072 bits and ECC
+	    groups with prime >= 256)
+	  - minimize timing and memory use differences in PWE derivation
+	    [https://w1.fi/security/2019-1/] (CVE-2019-9494)
+	* EAP-pwd changes
+	  - minimize timing and memory use differences in PWE derivation
+	    [https://w1.fi/security/2019-2/] (CVE-2019-9495)
+	  - verify server scalar/element
+	    [https://w1.fi/security/2019-4/] (CVE-2019-9499)
+	  - fix message reassembly issue with unexpected fragment
+	    [https://w1.fi/security/2019-5/]
+	  - enforce rand,mask generation rules more strictly
+	  - fix a memory leak in PWE derivation
+	  - disallow ECC groups with a prime under 256 bits (groups 25, 26, and
+	    27)
+	* fixed CONFIG_IEEE80211R=y (FT) build without CONFIG_FILS=y
+	* Hotspot 2.0 changes
+	  - do not indicate release number that is higher than the one
+	    AP supports
+	  - added support for release number 3
+	  - enable PMF automatically for network profiles created from
+	    credentials
+	* fixed OWE network profile saving
+	* fixed DPP network profile saving
+	* added support for RSN operating channel validation
+	  (CONFIG_OCV=y and network profile parameter ocv=1)
+	* added Multi-AP backhaul STA support
+	* fixed build with LibreSSL
+	* number of MKA/MACsec fixes and extensions
+	* extended domain_match and domain_suffix_match to allow list of values
+	* fixed dNSName matching in domain_match and domain_suffix_match when
+	  using wolfSSL
+	* started to prefer FT-EAP-SHA384 over WPA-EAP-SUITE-B-192 AKM if both
+	  are enabled
+	* extended nl80211 Connect and external authentication to support
+	  SAE, FT-SAE, FT-EAP-SHA384
+	* fixed KEK2 derivation for FILS+FT
+	* extended client_cert file to allow loading of a chain of PEM
+	  encoded certificates
+	* extended beacon reporting functionality
+	* extended D-Bus interface with number of new properties
+	* fixed a regression in FT-over-DS with mac80211-based drivers
+	* OpenSSL: allow systemwide policies to be overridden
+	* extended driver flags indication for separate 802.1X and PSK
+	  4-way handshake offload capability
+	* added support for random P2P Device/Interface Address use
+	* extended PEAP to derive EMSK to enable use with ERP/FILS
+	* extended WPS to allow SAE configuration to be added automatically
+	  for PSK (wps_cred_add_sae=1)
+	* removed support for the old D-Bus interface (CONFIG_CTRL_IFACE_DBUS)
+	* extended domain_match and domain_suffix_match to allow list of values
+	* added a RSN workaround for misbehaving PMF APs that advertise
+	  IGTK/BIP KeyID using incorrect byte order
+	* fixed PTK rekeying with FILS and FT
+
 2018-12-02 - v2.7
 	* fixed WPA packet number reuse with replayed messages and key
 	  reinstallation
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index e81238e..68ef5a2 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -103,6 +103,7 @@
 OBJS += ../src/utils/wpa_debug.o
 OBJS += ../src/utils/wpabuf.o
 OBJS += ../src/utils/bitfield.o
+OBJS += ../src/utils/ip_addr.o
 OBJS += op_classes.o
 OBJS += rrm.o
 OBJS_p = wpa_passphrase.o
@@ -230,8 +231,6 @@
 
 ifdef CONFIG_SUITEB
 CFLAGS += -DCONFIG_SUITEB
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_SUITEB192
@@ -242,25 +241,15 @@
 ifdef CONFIG_OCV
 CFLAGS += -DCONFIG_OCV
 OBJS += ../src/common/ocv.o
-CONFIG_IEEE80211W=y
-endif
-
-ifdef CONFIG_IEEE80211W
-CFLAGS += -DCONFIG_IEEE80211W
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R
 OBJS += ../src/rsn_supp/wpa_ft.o
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_MESH
 NEED_80211_COMMON=y
-NEED_SHA256=y
 NEED_AES_SIV=y
 CONFIG_SAE=y
 CONFIG_AP=y
@@ -275,6 +264,10 @@
 OBJS += ../src/common/sae.o
 NEED_ECC=y
 NEED_DH_GROUPS=y
+NEED_DRAGONFLY=y
+ifdef CONFIG_TESTING_OPTIONS
+NEED_DH_GROUPS_ALL=y
+endif
 endif
 
 ifdef CONFIG_DPP
@@ -285,9 +278,9 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
+NEED_ECC=y
 NEED_JSON=y
 NEED_GAS_SERVER=y
 NEED_BASE64=y
@@ -302,11 +295,14 @@
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_HMAC_SHA512_KDF=y
-NEED_SHA256=y
 NEED_SHA384=y
 NEED_SHA512=y
 endif
 
+ifdef CONFIG_WAPI_INTERFACE
+L_CFLAGS += -DCONFIG_WAPI_INTERFACE
+endif
+
 ifdef CONFIG_FILS
 CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
@@ -329,8 +325,6 @@
 ifdef CONFIG_TDLS
 CFLAGS += -DCONFIG_TDLS
 OBJS += ../src/rsn_supp/tdls.o
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_TDLS_TESTING
@@ -403,7 +397,6 @@
 OBJS += hs20_supplicant.o
 CFLAGS += -DCONFIG_HS20
 CONFIG_INTERWORKING=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_INTERWORKING
@@ -457,7 +450,6 @@
 
 ifdef CONFIG_ERP
 CFLAGS += -DCONFIG_ERP
-NEED_SHA256=y
 NEED_HMAC_SHA256_KDF=y
 endif
 
@@ -612,7 +604,6 @@
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_AES=y
-NEED_AES_OMAC1=y
 NEED_AES_ENCBLOCK=y
 NEED_AES_EAX=y
 endif
@@ -645,7 +636,6 @@
 else
 CFLAGS += -DEAP_AKA_PRIME
 endif
-NEED_SHA256=y
 endif
 
 ifdef CONFIG_EAP_SIM_COMMON
@@ -670,6 +660,25 @@
 NEED_T_PRF=y
 endif
 
+ifdef CONFIG_EAP_TEAP
+# EAP-TEAP
+ifeq ($(CONFIG_EAP_TEAP), dyn)
+CFLAGS += -DEAP_TEAP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_teap.so
+EAPDYN += ../src/eap_common/eap_teap_common.o
+else
+CFLAGS += -DEAP_TEAP
+OBJS += ../src/eap_peer/eap_teap.o ../src/eap_peer/eap_teap_pac.o
+OBJS += ../src/eap_common/eap_teap_common.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_TLS_PRF_SHA256=y
+NEED_TLS_PRF_SHA384=y
+endif
+
 ifdef CONFIG_EAP_PAX
 # EAP-PAX
 ifeq ($(CONFIG_EAP_PAX), dyn)
@@ -707,8 +716,6 @@
 ifdef CONFIG_EAP_GPSK_SHA256
 CFLAGS += -DEAP_GPSK_SHA256
 endif
-NEED_SHA256=y
-NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_EAP_PWD
@@ -718,8 +725,8 @@
 endif
 OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
 CONFIG_IEEE8021X_EAPOL=y
-NEED_SHA256=y
 NEED_ECC=y
+NEED_DRAGONFLY=y
 endif
 
 ifdef CONFIG_EAP_EKE
@@ -734,7 +741,6 @@
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_DH_GROUPS_ALL=y
-NEED_SHA256=y
 NEED_AES_CBC=y
 endif
 
@@ -754,7 +760,6 @@
 OBJS += ../src/wps/wps_registrar.o
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
@@ -852,12 +857,14 @@
 NEED_AES_ENCBLOCK=y
 NEED_AES_UNWRAP=y
 NEED_AES_WRAP=y
-NEED_AES_OMAC1=y
 OBJS += wpas_kay.o
 OBJS += ../src/pae/ieee802_1x_cp.o
 OBJS += ../src/pae/ieee802_1x_kay.o
 OBJS += ../src/pae/ieee802_1x_key.o
 OBJS += ../src/pae/ieee802_1x_secy_ops.o
+ifdef CONFIG_AP
+OBJS += ../src/ap/wpa_auth_kay.o
+endif
 endif
 
 ifdef CONFIG_IEEE8021X_EAPOL
@@ -885,7 +892,6 @@
 OBJS += ../src/ap/utils.o
 OBJS += ../src/ap/authsrv.o
 OBJS += ../src/ap/ap_config.o
-OBJS += ../src/utils/ip_addr.o
 OBJS += ../src/ap/sta_info.o
 OBJS += ../src/ap/tkip_countermeasures.o
 OBJS += ../src/ap/ap_mlme.o
@@ -954,8 +960,12 @@
 ifdef CONFIG_DPP
 OBJS += ../src/ap/dpp_hostapd.o
 OBJS += ../src/ap/gas_query_ap.o
+NEED_AP_GAS_SERV=y
 endif
 ifdef CONFIG_INTERWORKING
+NEED_AP_GAS_SERV=y
+endif
+ifdef NEED_AP_GAS_SERV
 OBJS += ../src/ap/gas_serv.o
 endif
 ifdef CONFIG_HS20
@@ -1023,6 +1033,10 @@
 CFLAGS += -DCONFIG_SMARTCARD
 endif
 
+ifdef NEED_DRAGONFLY
+OBJS += ../src/common/dragonfly.o
+endif
+
 ifdef MS_FUNCS
 OBJS += ../src/crypto/ms_funcs.o
 NEED_DES=y
@@ -1035,7 +1049,8 @@
 
 ifdef TLS_FUNCS
 NEED_DES=y
-# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, EAP_FAST, and
+# EAP_TEAP)
 OBJS += ../src/eap_peer/eap_tls_common.o
 ifndef CONFIG_FIPS
 NEED_TLS_PRF=y
@@ -1054,7 +1069,6 @@
 
 ifdef CONFIG_TLSV12
 CFLAGS += -DCONFIG_TLSV12
-NEED_SHA256=y
 endif
 
 ifeq ($(CONFIG_TLS), wolfssl)
@@ -1085,7 +1099,6 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_openssl.o
 endif
-NEED_SHA256=y
 NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_p += -lcrypto
@@ -1149,7 +1162,6 @@
 OBJS += ../src/tls/pkcs1.o
 OBJS += ../src/tls/pkcs5.o
 OBJS += ../src/tls/pkcs8.o
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
 ifdef CONFIG_TLSV12
@@ -1229,7 +1241,6 @@
 OBJS += ../src/tls/pkcs1.o
 OBJS += ../src/tls/pkcs5.o
 OBJS += ../src/tls/pkcs8.o
-NEED_SHA256=y
 NEED_BASE64=y
 NEED_TLS_PRF=y
 ifdef CONFIG_TLSV12
@@ -1315,12 +1326,10 @@
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_SIV
 AESOBJS += ../src/crypto/aes-siv.o
 NEED_AES_CTR=y
-NEED_AES_OMAC1=y
 endif
 ifdef NEED_AES_CTR
 AESOBJS += ../src/crypto/aes-ctr.o
@@ -1328,7 +1337,6 @@
 ifdef NEED_AES_ENCBLOCK
 AESOBJS += ../src/crypto/aes-encblock.o
 endif
-ifdef NEED_AES_OMAC1
 NEED_AES_ENC=y
 ifdef CONFIG_OPENSSL_CMAC
 CFLAGS += -DCONFIG_OPENSSL_CMAC
@@ -1339,7 +1347,6 @@
 endif
 endif
 endif
-endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
 ifdef NEED_INTERNAL_AES_WRAP
@@ -1448,7 +1455,6 @@
 endif
 
 SHA256OBJS = # none by default
-ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
@@ -1474,6 +1480,9 @@
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += ../src/crypto/sha256-tlsprf.o
 endif
+ifdef NEED_TLS_PRF_SHA384
+SHA256OBJS += ../src/crypto/sha384-tlsprf.o
+endif
 ifdef NEED_HMAC_SHA256_KDF
 CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 OBJS += ../src/crypto/sha256-kdf.o
@@ -1487,7 +1496,6 @@
 OBJS += ../src/crypto/sha512-kdf.o
 endif
 OBJS += $(SHA256OBJS)
-endif
 ifdef NEED_SHA384
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
@@ -1781,9 +1789,6 @@
 OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
 OBJS_t += ../src/radius/radius_client.o
 OBJS_t += ../src/radius/radius.o
-ifndef CONFIG_AP
-OBJS_t += ../src/utils/ip_addr.o
-endif
 OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
 
 OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
diff --git a/wpa_supplicant/README-DPP b/wpa_supplicant/README-DPP
index 6496733..457e32e 100644
--- a/wpa_supplicant/README-DPP
+++ b/wpa_supplicant/README-DPP
@@ -28,7 +28,6 @@
 Enable DPP and protected management frame in wpa_supplicant build config
 file
 
-CONFIG_IEEE80211W=y
 CONFIG_DPP=y
 
 hostapd build config
@@ -36,7 +35,6 @@
 
 Enable DPP and protected management frame in hostapd build config file
 
-CONFIG_IEEE80211W=y
 CONFIG_DPP=y
 
 Configurator build config
@@ -100,7 +98,7 @@
 Generate QR code for the device. Store the qr code id returned by the
 command.
 
-> dpp_bootstrap_gen type=qrcode mac=<mac-address-of-device> chan=<operating-channel> key=<key of the device>
+> dpp_bootstrap_gen type=qrcode mac=<mac-address-of-device> chan=<operating-class/operating-channel> key=<key of the device>
 (returns bootstrapping info id)
 
 Get QR Code of device using the bootstrap info id.
@@ -120,7 +118,9 @@
 
 Send provisioning request to enrollee. (conf is ap-dpp if enrollee is an
 AP. conf is sta-dpp if enrollee is a client)
-> dpp_auth_init peer=<qr-code-id> conf=<ap-dpp|sta-dpp> configurator=<configurator-id>
+> dpp_auth_init peer=<qr-code-id> conf=<ap-dpp|sta-dpp> ssid=<SSID hexdump> configurator=<configurator-id>
+or for legacy (PSK/SAE) provisioning for a station Enrollee:
+> dpp_auth_init peer=<qr-code-id> conf=sta-psk ssid=<SSID hexdump> pass=<passphrase hexdump>
 
 The DPP values will be printed in the console. Save this values into the
 config file. If the enrollee is an AP, we need to manually write these
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index b9b5d9d..d05141f 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -270,10 +270,6 @@
 # bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
 #CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
 
-# IEEE 802.11w (management frame protection), also known as PMF
-# Driver support is also needed for IEEE 802.11w.
-CONFIG_IEEE80211W=y
-
 # Support Operating Channel Validation
 #CONFIG_OCV=y
 
@@ -507,10 +503,13 @@
 #CONFIG_FST=y
 
 # Support Multi Band Operation
-#CONFIG_MBO=y
+CONFIG_MBO=y
 
 # Fast Initial Link Setup (FILS) (IEEE 802.11ai)
-#CONFIG_FILS=y
+CONFIG_FILS=y
+
+# EAP Re-authentication protocol
+CONFIG_ERP=y
 
 # Support RSN on IBSS networks
 # This is needed to be able to use mode=1 network profile with proto=RSN and
@@ -539,8 +538,9 @@
 # Experimental implementation of draft-harkins-owe-07.txt
 CONFIG_OWE=y
 
-# Easy Connect (Device Provisioning Protocol - DPP)
+# Easy Connect (Device Provisioning Protocol - DPP R1 and R2)
 CONFIG_DPP=y
+CONFIG_DPP2=y
 
 # WPA3-Personal (SAE)
 CONFIG_SAE=y
@@ -549,4 +549,9 @@
 CONFIG_SUITEB=y
 CONFIG_SUITEB192=y
 
+# WLAN Authentication and Privacy Infrastructure (WAPI): interface only.
+# Configure the building of the interface which allows WAPI configuration.
+# Note: does not configure WAPI implementation itself.
+#CONFIG_WAPI_INTERFACE=y
+
 include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 4e19169..e552306 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -67,7 +67,7 @@
 
 	if (!ssid->p2p_group) {
 		if (!ssid->vht_center_freq1 ||
-		    conf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+		    conf->vht_oper_chwidth == CHANWIDTH_USE_HT)
 			goto no_vht;
 		ieee80211_freq_to_chan(ssid->vht_center_freq1,
 				       &conf->vht_oper_centr_freq_seg0_idx);
@@ -78,14 +78,14 @@
 
 #ifdef CONFIG_P2P
 	switch (conf->vht_oper_chwidth) {
-	case VHT_CHANWIDTH_80MHZ:
-	case VHT_CHANWIDTH_80P80MHZ:
+	case CHANWIDTH_80MHZ:
+	case CHANWIDTH_80P80MHZ:
 		center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
 		wpa_printf(MSG_DEBUG,
 			   "VHT center channel %u for 80 or 80+80 MHz bandwidth",
 			   center_chan);
 		break;
-	case VHT_CHANWIDTH_160MHZ:
+	case CHANWIDTH_160MHZ:
 		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
 		wpa_printf(MSG_DEBUG,
 			   "VHT center channel %u for 160 MHz bandwidth",
@@ -97,14 +97,14 @@
 		 * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
 		 * not supported.
 		 */
-		conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+		conf->vht_oper_chwidth = CHANWIDTH_160MHZ;
 		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
 		if (center_chan) {
 			wpa_printf(MSG_DEBUG,
 				   "VHT center channel %u for auto-selected 160 MHz bandwidth",
 				   center_chan);
 		} else {
-			conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			conf->vht_oper_chwidth = CHANWIDTH_80MHZ;
 			center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
 								channel);
 			wpa_printf(MSG_DEBUG,
@@ -128,7 +128,7 @@
 		   conf->channel);
 	conf->vht_oper_centr_freq_seg0_idx =
 		conf->channel + conf->secondary_channel * 2;
-	conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+	conf->vht_oper_chwidth = CHANWIDTH_USE_HT;
 }
 #endif /* CONFIG_IEEE80211N */
 
@@ -239,6 +239,11 @@
 				conf->vht_capab |= mode->vht_capab;
 				wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
 			}
+
+			if (mode->he_capab[wpas_mode_to_ieee80211_mode(
+					    ssid->mode)].he_supported &&
+			    ssid->he)
+				conf->ieee80211ax = 1;
 		}
 	}
 
@@ -376,7 +381,9 @@
 	else
 		bss->wpa_key_mgmt = ssid->key_mgmt;
 	bss->wpa_pairwise = ssid->pairwise_cipher;
-	if (ssid->psk_set) {
+	if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) && ssid->passphrase) {
+		bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
+	} else if (ssid->psk_set) {
 		bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk));
 		bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
 		if (bss->ssid.wpa_psk == NULL)
@@ -402,6 +409,34 @@
 		wep->idx = ssid->wep_tx_keyidx;
 		wep->keys_set = 1;
 	}
+#ifdef CONFIG_SAE
+	if (ssid->sae_password) {
+		struct sae_password_entry *pw;
+
+		pw = os_zalloc(sizeof(*pw));
+		if (!pw)
+			return -1;
+		os_memset(pw->peer_addr, 0xff, ETH_ALEN);
+		pw->password = os_strdup(ssid->sae_password);
+		if (!pw->password) {
+			os_free(pw);
+			return -1;
+		}
+		if (ssid->sae_password_id) {
+			pw->identifier = os_strdup(ssid->sae_password_id);
+			if (!pw->identifier) {
+				str_clear_free(pw->password);
+				os_free(pw);
+				return -1;
+			}
+		}
+
+		pw->next = bss->sae_passwords;
+		bss->sae_passwords = pw;
+	}
+
+	bss->sae_pwe = wpa_s->conf->sae_pwe;
+#endif /* CONFIG_SAE */
 
 	if (wpa_s->conf->go_interworking) {
 		wpa_printf(MSG_DEBUG,
@@ -495,10 +530,8 @@
 		bss->wpa_group_rekey = 86400;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
 		bss->ieee80211w = ssid->ieee80211w;
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_OCV
 	bss->ocv = ssid->ocv;
@@ -742,6 +775,21 @@
 		ssid->frequency = 2462; /* default channel 11 */
 	params.freq.freq = ssid->frequency;
 
+	if ((ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO) &&
+	    ssid->enable_edmg) {
+		u8 primary_channel;
+
+		if (ieee80211_freq_to_chan(ssid->frequency, &primary_channel) ==
+		    NUM_HOSTAPD_MODES) {
+			wpa_printf(MSG_WARNING,
+				   "EDMG: Failed to get the primary channel");
+			return -1;
+		}
+
+		hostapd_encode_edmg_chan(ssid->enable_edmg, ssid->edmg_channel,
+					 primary_channel, &params.freq.edmg);
+	}
+
 	params.wpa_proto = ssid->proto;
 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
 		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -880,6 +928,8 @@
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 	os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
 	wpa_s->assoc_freq = ssid->frequency;
+	wpa_s->ap_iface->conf->enable_edmg = ssid->enable_edmg;
+	wpa_s->ap_iface->conf->edmg_channel = ssid->edmg_channel;
 
 #if defined(CONFIG_P2P) && defined(CONFIG_ACS)
 	if (wpa_s->p2p_go_do_acs) {
@@ -1387,7 +1437,7 @@
 
 
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
-		       int offset, int width, int cf1, int cf2)
+		       int offset, int width, int cf1, int cf2, int finished)
 {
 	struct hostapd_iface *iface = wpa_s->ap_iface;
 
@@ -1399,7 +1449,7 @@
 	if (wpa_s->current_ssid)
 		wpa_s->current_ssid->frequency = freq;
 	hostapd_event_ch_switch(iface->bss[0], freq, ht,
-				offset, width, cf1, cf2);
+				offset, width, cf1, cf2, finished);
 }
 
 
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 447b551..6c6e94c 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -54,7 +54,7 @@
 		      struct csa_settings *settings);
 int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr);
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
-		       int offset, int width, int cf1, int cf2);
+		       int offset, int width, int cf1, int cf2, int finished);
 struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 					     int ndef);
 #ifdef CONFIG_AP
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 9b19f37..943a340 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -431,6 +431,7 @@
 				    struct os_reltime *fetch_time)
 {
 	struct wpa_bss *bss;
+	char extra[50];
 
 	bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
 	if (bss == NULL)
@@ -456,10 +457,15 @@
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
 	wpa_s->num_bss++;
+	if (!is_zero_ether_addr(bss->hessid))
+		os_snprintf(extra, sizeof(extra), " HESSID " MACSTR,
+			    MAC2STR(bss->hessid));
+	else
+		extra[0] = '\0';
 	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
-		" SSID '%s' freq %d",
+		" SSID '%s' freq %d%s",
 		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
-		bss->freq);
+		bss->freq, extra);
 	wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
 	return bss;
 }
@@ -1032,23 +1038,30 @@
 
 #ifdef CONFIG_P2P
 /**
- * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
+ * wpa_bss_get_p2p_dev_addr - Fetch the latest BSS table entry based on P2P Device Addr
  * @wpa_s: Pointer to wpa_supplicant data
  * @dev_addr: P2P Device Address of the GO
  * Returns: Pointer to the BSS entry or %NULL if not found
+ *
+ * This function tries to find the entry that has the most recent update. This
+ * can help in finding the correct entry in cases where the SSID of the P2P
+ * Device may have changed recently.
  */
 struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
 					  const u8 *dev_addr)
 {
-	struct wpa_bss *bss;
+	struct wpa_bss *bss, *found = NULL;
 	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
 		u8 addr[ETH_ALEN];
 		if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
-				       addr) == 0 &&
-		    os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
-			return bss;
+				       addr) != 0 ||
+		    os_memcmp(addr, dev_addr, ETH_ALEN) != 0)
+			continue;
+		if (!found ||
+		    os_reltime_before(&found->last_update, &bss->last_update))
+			found = bss;
 	}
-	return NULL;
+	return found;
 }
 #endif /* CONFIG_P2P */
 
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index a7ca41c..2d1c126 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration parser and common functions
- * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "utils/uuid.h"
 #include "utils/ip_addr.h"
 #include "common/ieee802_1x_defs.h"
+#include "common/sae.h"
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
@@ -740,12 +741,10 @@
 			val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
 #endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 		else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
 			val |= WPA_KEY_MGMT_PSK_SHA256;
 		else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WPS
 		else if (os_strcmp(start, "WPS") == 0)
 			val |= WPA_KEY_MGMT_WPS;
@@ -910,7 +909,6 @@
 #endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef CONFIG_IEEE80211W
 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
 				  pos == buf ? "" : " ");
@@ -930,7 +928,6 @@
 		}
 		pos += ret;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_WPS
 	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
@@ -1614,7 +1611,7 @@
 #ifdef CONFIG_EXT_PASSWORD
 	if (os_strncmp(value, "ext:", 4) == 0) {
 		char *name = os_strdup(value + 4);
-		if (name == NULL)
+		if (!name)
 			return -1;
 		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 		ssid->eap.password = (u8 *) name;
@@ -1630,9 +1627,9 @@
 		size_t res_len;
 
 		tmp = wpa_config_parse_string(value, &res_len);
-		if (tmp == NULL) {
-			wpa_printf(MSG_ERROR, "Line %d: failed to parse "
-				   "password.", line);
+		if (!tmp) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: failed to parse password.", line);
 			return -1;
 		}
 		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
@@ -1650,13 +1647,14 @@
 
 	/* NtPasswordHash: hash:<32 hex digits> */
 	if (os_strlen(value + 5) != 2 * 16) {
-		wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
-			   "(expected 32 hex digits)", line);
+		wpa_printf(MSG_ERROR,
+			   "Line %d: Invalid password hash length (expected 32 hex digits)",
+			   line);
 		return -1;
 	}
 
 	hash = os_malloc(16);
-	if (hash == NULL)
+	if (!hash)
 		return -1;
 
 	if (hexstr2bin(value + 5, hash, 16)) {
@@ -1683,19 +1681,118 @@
 }
 
 
+static int wpa_config_parse_machine_password(const struct parse_data *data,
+					     struct wpa_ssid *ssid, int line,
+					     const char *value)
+{
+	u8 *hash;
+
+	if (os_strcmp(value, "NULL") == 0) {
+		if (!ssid->eap.machine_password)
+			return 1; /* Already unset */
+		wpa_printf(MSG_DEBUG,
+			   "Unset configuration string 'machine_password'");
+		bin_clear_free(ssid->eap.machine_password,
+			       ssid->eap.machine_password_len);
+		ssid->eap.machine_password = NULL;
+		ssid->eap.machine_password_len = 0;
+		return 0;
+	}
+
+#ifdef CONFIG_EXT_PASSWORD
+	if (os_strncmp(value, "ext:", 4) == 0) {
+		char *name = os_strdup(value + 4);
+
+		if (!name)
+			return -1;
+		bin_clear_free(ssid->eap.machine_password,
+			       ssid->eap.machine_password_len);
+		ssid->eap.machine_password = (u8 *) name;
+		ssid->eap.machine_password_len = os_strlen(name);
+		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
+		ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
+		return 0;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
+	if (os_strncmp(value, "hash:", 5) != 0) {
+		char *tmp;
+		size_t res_len;
+
+		tmp = wpa_config_parse_string(value, &res_len);
+		if (!tmp) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: failed to parse machine_password.",
+				   line);
+			return -1;
+		}
+		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+				      (u8 *) tmp, res_len);
+
+		bin_clear_free(ssid->eap.machine_password,
+			       ssid->eap.machine_password_len);
+		ssid->eap.machine_password = (u8 *) tmp;
+		ssid->eap.machine_password_len = res_len;
+		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
+		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
+
+		return 0;
+	}
+
+
+	/* NtPasswordHash: hash:<32 hex digits> */
+	if (os_strlen(value + 5) != 2 * 16) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: Invalid machine_password hash length (expected 32 hex digits)",
+			   line);
+		return -1;
+	}
+
+	hash = os_malloc(16);
+	if (!hash)
+		return -1;
+
+	if (hexstr2bin(value + 5, hash, 16)) {
+		os_free(hash);
+		wpa_printf(MSG_ERROR, "Line %d: Invalid machine_password hash",
+			   line);
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
+
+	if (ssid->eap.machine_password &&
+	    ssid->eap.machine_password_len == 16 &&
+	    os_memcmp(ssid->eap.machine_password, hash, 16) == 0 &&
+	    (ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) {
+		bin_clear_free(hash, 16);
+		return 1;
+	}
+	bin_clear_free(ssid->eap.machine_password,
+		       ssid->eap.machine_password_len);
+	ssid->eap.machine_password = hash;
+	ssid->eap.machine_password_len = 16;
+	ssid->eap.flags |= EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
+	ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
+
+	return 0;
+}
+
+
 #ifndef NO_CONFIG_WRITE
+
 static char * wpa_config_write_password(const struct parse_data *data,
 					struct wpa_ssid *ssid)
 {
 	char *buf;
 
-	if (ssid->eap.password == NULL)
+	if (!ssid->eap.password)
 		return NULL;
 
 #ifdef CONFIG_EXT_PASSWORD
 	if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
 		buf = os_zalloc(4 + ssid->eap.password_len + 1);
-		if (buf == NULL)
+		if (!buf)
 			return NULL;
 		os_memcpy(buf, "ext:", 4);
 		os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
@@ -1709,7 +1806,7 @@
 	}
 
 	buf = os_malloc(5 + 32 + 1);
-	if (buf == NULL)
+	if (!buf)
 		return NULL;
 
 	os_memcpy(buf, "hash:", 5);
@@ -1717,6 +1814,44 @@
 
 	return buf;
 }
+
+
+static char * wpa_config_write_machine_password(const struct parse_data *data,
+						struct wpa_ssid *ssid)
+{
+	char *buf;
+
+	if (!ssid->eap.machine_password)
+		return NULL;
+
+#ifdef CONFIG_EXT_PASSWORD
+	if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD) {
+		buf = os_zalloc(4 + ssid->eap.machine_password_len + 1);
+		if (!buf)
+			return NULL;
+		os_memcpy(buf, "ext:", 4);
+		os_memcpy(buf + 4, ssid->eap.machine_password,
+			  ssid->eap.machine_password_len);
+		return buf;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
+	if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) {
+		return wpa_config_write_string(
+			ssid->eap.machine_password,
+			ssid->eap.machine_password_len);
+	}
+
+	buf = os_malloc(5 + 32 + 1);
+	if (!buf)
+		return NULL;
+
+	os_memcpy(buf, "hash:", 5);
+	wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.machine_password, 16);
+
+	return buf;
+}
+
 #endif /* NO_CONFIG_WRITE */
 #endif /* IEEE8021X_EAPOL */
 
@@ -2141,23 +2276,24 @@
 /* STR: Define a string variable for an ASCII string; f = field name */
 #ifdef NO_CONFIG_WRITE
 #define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
-#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
+#define _STRe(f, m) #f, wpa_config_parse_str, OFFSET(eap.m)
 #else /* NO_CONFIG_WRITE */
 #define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
-#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
+#define _STRe(f, m) #f, wpa_config_parse_str, wpa_config_write_str, \
+		OFFSET(eap.m)
 #endif /* NO_CONFIG_WRITE */
 #define STR(f) _STR(f), NULL, NULL, NULL, 0
-#define STRe(f) _STRe(f), NULL, NULL, NULL, 0
+#define STRe(f, m) _STRe(f, m), NULL, NULL, NULL, 0
 #define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
-#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
+#define STR_KEYe(f, m) _STRe(f, m), NULL, NULL, NULL, 1
 
 /* STR_LEN: Define a string variable with a separate variable for storing the
  * data length. Unlike STR(), this can be used to store arbitrary binary data
  * (i.e., even nul termination character). */
 #define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
-#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
+#define _STR_LENe(f, m) _STRe(f, m), OFFSET(eap.m ## _len)
 #define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
-#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
+#define STR_LENe(f, m) _STR_LENe(f, m), NULL, NULL, 0
 #define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
 
 /* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
@@ -2168,17 +2304,17 @@
 
 #ifdef NO_CONFIG_WRITE
 #define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
-#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
+#define _INTe(f, m) #f, wpa_config_parse_int, OFFSET(eap.m), (void *) 0
 #else /* NO_CONFIG_WRITE */
 #define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
 	OFFSET(f), (void *) 0
-#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
-	OFFSET(eap.f), (void *) 0
+#define _INTe(f, m) #f, wpa_config_parse_int, wpa_config_write_int,	\
+	OFFSET(eap.m), (void *) 0
 #endif /* NO_CONFIG_WRITE */
 
 /* INT: Define an integer variable */
 #define INT(f) _INT(f), NULL, NULL, 0
-#define INTe(f) _INTe(f), NULL, NULL, 0
+#define INTe(f, m) _INTe(f, m), NULL, NULL, 0
 
 /* INT_RANGE: Define an integer variable with allowed value range */
 #define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
@@ -2240,57 +2376,80 @@
 	{ INT_RANGE(ht, 0, 1) },
 	{ INT_RANGE(vht, 0, 1) },
 	{ INT_RANGE(ht40, -1, 1) },
-	{ INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
-		    VHT_CHANWIDTH_80P80MHZ) },
+	{ INT_RANGE(max_oper_chwidth, CHANWIDTH_USE_HT,
+		    CHANWIDTH_80P80MHZ) },
 	{ INT(vht_center_freq1) },
 	{ INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
 	{ FUNC(eap) },
-	{ STR_LENe(identity) },
-	{ STR_LENe(anonymous_identity) },
-	{ STR_LENe(imsi_identity) },
+	{ STR_LENe(identity, identity) },
+	{ STR_LENe(anonymous_identity, anonymous_identity) },
+	{ STR_LENe(imsi_identity, imsi_identity) },
+	{ STR_LENe(machine_identity, machine_identity) },
 	{ FUNC_KEY(password) },
-	{ STRe(ca_cert) },
-	{ STRe(ca_path) },
-	{ STRe(client_cert) },
-	{ STRe(private_key) },
-	{ STR_KEYe(private_key_passwd) },
-	{ STRe(dh_file) },
-	{ STRe(subject_match) },
-	{ STRe(check_cert_subject) },
-	{ STRe(altsubject_match) },
-	{ STRe(domain_suffix_match) },
-	{ STRe(domain_match) },
-	{ STRe(ca_cert2) },
-	{ STRe(ca_path2) },
-	{ STRe(client_cert2) },
-	{ STRe(private_key2) },
-	{ STR_KEYe(private_key2_passwd) },
-	{ STRe(dh_file2) },
-	{ STRe(subject_match2) },
-	{ STRe(check_cert_subject2) },
-	{ STRe(altsubject_match2) },
-	{ STRe(domain_suffix_match2) },
-	{ STRe(domain_match2) },
-	{ STRe(phase1) },
-	{ STRe(phase2) },
-	{ STRe(pcsc) },
-	{ STR_KEYe(pin) },
-	{ STRe(engine_id) },
-	{ STRe(key_id) },
-	{ STRe(cert_id) },
-	{ STRe(ca_cert_id) },
-	{ STR_KEYe(pin2) },
-	{ STRe(engine2_id) },
-	{ STRe(key2_id) },
-	{ STRe(cert2_id) },
-	{ STRe(ca_cert2_id) },
-	{ INTe(engine) },
-	{ INTe(engine2) },
+	{ FUNC_KEY(machine_password) },
+	{ STRe(ca_cert, cert.ca_cert) },
+	{ STRe(ca_path, cert.ca_path) },
+	{ STRe(client_cert, cert.client_cert) },
+	{ STRe(private_key, cert.private_key) },
+	{ STR_KEYe(private_key_passwd, cert.private_key_passwd) },
+	{ STRe(dh_file, cert.dh_file) },
+	{ STRe(subject_match, cert.subject_match) },
+	{ STRe(check_cert_subject, cert.check_cert_subject) },
+	{ STRe(altsubject_match, cert.altsubject_match) },
+	{ STRe(domain_suffix_match, cert.domain_suffix_match) },
+	{ STRe(domain_match, cert.domain_match) },
+	{ STRe(ca_cert2, phase2_cert.ca_cert) },
+	{ STRe(ca_path2, phase2_cert.ca_path) },
+	{ STRe(client_cert2, phase2_cert.client_cert) },
+	{ STRe(private_key2, phase2_cert.private_key) },
+	{ STR_KEYe(private_key2_passwd, phase2_cert.private_key_passwd) },
+	{ STRe(dh_file2, phase2_cert.dh_file) },
+	{ STRe(subject_match2, phase2_cert.subject_match) },
+	{ STRe(check_cert_subject2, phase2_cert.check_cert_subject) },
+	{ STRe(altsubject_match2, phase2_cert.altsubject_match) },
+	{ STRe(domain_suffix_match2, phase2_cert.domain_suffix_match) },
+	{ STRe(domain_match2, phase2_cert.domain_match) },
+	{ STRe(phase1, phase1) },
+	{ STRe(phase2, phase2) },
+	{ STRe(machine_phase2, machine_phase2) },
+	{ STRe(pcsc, pcsc) },
+	{ STR_KEYe(pin, cert.pin) },
+	{ STRe(engine_id, cert.engine_id) },
+	{ STRe(key_id, cert.key_id) },
+	{ STRe(cert_id, cert.cert_id) },
+	{ STRe(ca_cert_id, cert.ca_cert_id) },
+	{ STR_KEYe(pin2, phase2_cert.pin) },
+	{ STRe(engine_id2, phase2_cert.engine_id) },
+	{ STRe(key_id2, phase2_cert.key_id) },
+	{ STRe(cert_id2, phase2_cert.cert_id) },
+	{ STRe(ca_cert_id2, phase2_cert.ca_cert_id) },
+	{ INTe(engine, cert.engine) },
+	{ INTe(engine2, phase2_cert.engine) },
+	{ STRe(machine_ca_cert, machine_cert.ca_cert) },
+	{ STRe(machine_ca_path, machine_cert.ca_path) },
+	{ STRe(machine_client_cert, machine_cert.client_cert) },
+	{ STRe(machine_private_key, machine_cert.private_key) },
+	{ STR_KEYe(machine_private_key_passwd,
+		   machine_cert.private_key_passwd) },
+	{ STRe(machine_dh_file, machine_cert.dh_file) },
+	{ STRe(machine_subject_match, machine_cert.subject_match) },
+	{ STRe(machine_check_cert_subject, machine_cert.check_cert_subject) },
+	{ STRe(machine_altsubject_match, machine_cert.altsubject_match) },
+	{ STRe(machine_domain_suffix_match,
+	       machine_cert.domain_suffix_match) },
+	{ STRe(machine_domain_match, machine_cert.domain_match) },
+	{ STR_KEYe(machine_pin, machine_cert.pin) },
+	{ STRe(machine_engine_id, machine_cert.engine_id) },
+	{ STRe(machine_key_id, machine_cert.key_id) },
+	{ STRe(machine_cert_id, machine_cert.cert_id) },
+	{ STRe(machine_ca_cert_id, machine_cert.ca_cert_id) },
+	{ INTe(machine_engine, machine_cert.engine) },
+	{ INTe(machine_ocsp, machine_cert.ocsp) },
 	{ INT(eapol_flags) },
-	{ INTe(sim_num) },
-	{ STRe(openssl_ciphers) },
-	{ INTe(erp) },
+	{ INTe(sim_num, sim_num) },
+	{ STRe(openssl_ciphers, openssl_ciphers) },
+	{ INTe(erp, erp) },
 #endif /* IEEE8021X_EAPOL */
 	{ FUNC_KEY(wep_key0) },
 	{ FUNC_KEY(wep_key1) },
@@ -2300,9 +2459,10 @@
 	{ INT(priority) },
 #ifdef IEEE8021X_EAPOL
 	{ INT(eap_workaround) },
-	{ STRe(pac_file) },
-	{ INTe(fragment_size) },
-	{ INTe(ocsp) },
+	{ STRe(pac_file, pac_file) },
+	{ INTe(fragment_size, fragment_size) },
+	{ INTe(ocsp, cert.ocsp) },
+	{ INTe(ocsp2, phase2_cert.ocsp) },
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_MESH
 	{ INT_RANGE(mode, 0, 5) },
@@ -2314,16 +2474,16 @@
 	{ INT_RANGE(proactive_key_caching, 0, 1) },
 	{ INT_RANGE(disabled, 0, 2) },
 	{ STR(id_str) },
-#ifdef CONFIG_IEEE80211W
 	{ INT_RANGE(ieee80211w, 0, 2) },
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	{ FUNC(ocv) },
 #endif /* CONFIG_OCV */
 	{ FUNC(peerkey) /* obsolete - removed */ },
 	{ INT_RANGE(mixed_cell, 0, 1) },
-	{ INT_RANGE(frequency, 0, 65000) },
+	{ INT_RANGE(frequency, 0, 70200) },
 	{ INT_RANGE(fixed_freq, 0, 1) },
+	{ INT_RANGE(enable_edmg, 0, 1) },
+	{ INT_RANGE(edmg_channel, 9, 13) },
 #ifdef CONFIG_ACS
 	{ INT_RANGE(acs, 0, 1) },
 #endif /* CONFIG_ACS */
@@ -2407,6 +2567,7 @@
 	{ INT_RANGE(owe_group, 0, 65535) },
 	{ INT_RANGE(owe_only, 0, 1) },
 	{ INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
+	{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
 };
 
 #undef OFFSET
@@ -2513,48 +2674,44 @@
 
 
 #ifdef IEEE8021X_EAPOL
+
+static void eap_peer_config_free_cert(struct eap_peer_cert_config *cert)
+{
+	os_free(cert->ca_cert);
+	os_free(cert->ca_path);
+	os_free(cert->client_cert);
+	os_free(cert->private_key);
+	str_clear_free(cert->private_key_passwd);
+	os_free(cert->dh_file);
+	os_free(cert->subject_match);
+	os_free(cert->check_cert_subject);
+	os_free(cert->altsubject_match);
+	os_free(cert->domain_suffix_match);
+	os_free(cert->domain_match);
+	str_clear_free(cert->pin);
+	os_free(cert->engine_id);
+	os_free(cert->key_id);
+	os_free(cert->cert_id);
+	os_free(cert->ca_cert_id);
+}
+
+
 static void eap_peer_config_free(struct eap_peer_config *eap)
 {
 	os_free(eap->eap_methods);
 	bin_clear_free(eap->identity, eap->identity_len);
 	os_free(eap->anonymous_identity);
 	os_free(eap->imsi_identity);
+	os_free(eap->machine_identity);
 	bin_clear_free(eap->password, eap->password_len);
-	os_free(eap->ca_cert);
-	os_free(eap->ca_path);
-	os_free(eap->client_cert);
-	os_free(eap->private_key);
-	str_clear_free(eap->private_key_passwd);
-	os_free(eap->dh_file);
-	os_free(eap->subject_match);
-	os_free(eap->check_cert_subject);
-	os_free(eap->altsubject_match);
-	os_free(eap->domain_suffix_match);
-	os_free(eap->domain_match);
-	os_free(eap->ca_cert2);
-	os_free(eap->ca_path2);
-	os_free(eap->client_cert2);
-	os_free(eap->private_key2);
-	str_clear_free(eap->private_key2_passwd);
-	os_free(eap->dh_file2);
-	os_free(eap->subject_match2);
-	os_free(eap->check_cert_subject2);
-	os_free(eap->altsubject_match2);
-	os_free(eap->domain_suffix_match2);
-	os_free(eap->domain_match2);
+	bin_clear_free(eap->machine_password, eap->machine_password_len);
+	eap_peer_config_free_cert(&eap->cert);
+	eap_peer_config_free_cert(&eap->phase2_cert);
+	eap_peer_config_free_cert(&eap->machine_cert);
 	os_free(eap->phase1);
 	os_free(eap->phase2);
+	os_free(eap->machine_phase2);
 	os_free(eap->pcsc);
-	str_clear_free(eap->pin);
-	os_free(eap->engine_id);
-	os_free(eap->key_id);
-	os_free(eap->cert_id);
-	os_free(eap->ca_cert_id);
-	os_free(eap->key2_id);
-	os_free(eap->cert2_id);
-	os_free(eap->ca_cert2_id);
-	str_clear_free(eap->pin2);
-	os_free(eap->engine2_id);
 	os_free(eap->otp);
 	os_free(eap->pending_req_otp);
 	os_free(eap->pac_file);
@@ -2562,6 +2719,7 @@
 	str_clear_free(eap->external_sim_resp);
 	os_free(eap->openssl_ciphers);
 }
+
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -2608,6 +2766,9 @@
 		dl_list_del(&psk->list);
 		bin_clear_free(psk, sizeof(*psk));
 	}
+#ifdef CONFIG_SAE
+	sae_deinit_pt(ssid->pt);
+#endif /* CONFIG_SAE */
 	bin_clear_free(ssid, sizeof(*ssid));
 }
 
@@ -2726,6 +2887,8 @@
 #ifdef CONFIG_MBO
 	os_free(config->non_pref_chan);
 #endif /* CONFIG_MBO */
+	os_free(config->dpp_name);
+	os_free(config->dpp_mud_url);
 
 	os_free(config);
 }
@@ -2900,9 +3063,7 @@
 	ssid->vht_tx_mcs_nss_8 = -1;
 #endif /* CONFIG_VHT_OVERRIDES */
 	ssid->proactive_key_caching = -1;
-#ifdef CONFIG_IEEE80211W
 	ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_MACSEC
 	ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
 #endif /* CONFIG_MACSEC */
@@ -2947,6 +3108,15 @@
 			}
 			ret = -1;
 		}
+#ifdef CONFIG_SAE
+		if (os_strcmp(var, "ssid") == 0 ||
+		    os_strcmp(var, "psk") == 0 ||
+		    os_strcmp(var, "sae_password") == 0 ||
+		    os_strcmp(var, "sae_password_id") == 0) {
+			sae_deinit_pt(ssid->pt);
+			ssid->pt = NULL;
+		}
+#endif /* CONFIG_SAE */
 		break;
 	}
 	if (i == NUM_SSID_FIELDS) {
@@ -4780,6 +4950,7 @@
 	{ INT(p2p_go_ht40), 0 },
 	{ INT(p2p_go_vht), 0 },
 	{ INT(p2p_go_he), 0 },
+	{ INT(p2p_go_edmg), 0 },
 	{ INT(p2p_disabled), 0 },
 	{ INT_RANGE(p2p_go_ctwindow, 0, 127), 0 },
 	{ INT(p2p_no_group_iface), 0 },
@@ -4826,6 +4997,8 @@
 	{ INT(okc), 0 },
 	{ INT(pmf), 0 },
 	{ FUNC(sae_groups), 0 },
+	{ INT_RANGE(sae_pwe, 0, 2), 0 },
+	{ INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 },
 	{ INT(dtim_period), 0 },
 	{ INT(beacon_int), 0 },
 	{ FUNC(ap_vendor_elements), 0 },
@@ -4865,9 +5038,16 @@
 	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
 	{ INT(gas_rand_addr_lifetime), 0 },
 	{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
+#ifdef CONFIG_DPP
 	{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
+	{ STR(dpp_name), 0 },
+	{ STR(dpp_mud_url), 0 },
+#endif /* CONFIG_DPP */
 	{ INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
 	{ INT_RANGE(bss_no_flush_when_down, 0, 1), 0 },
+#ifdef CONFIG_WNM
+	{ INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM },
+#endif /* CONFIG_WNM */
 };
 
 #undef FUNC
@@ -4983,6 +5163,7 @@
 					   "AC item", line);
 				return -1;
 			}
+			return ret;
 		}
 #endif /* CONFIG_AP */
 		if (line < 0)
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 95817ff..d00c4ec 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -374,6 +374,7 @@
 #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
 #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
 #define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
+#define CFG_CHANGED_DISABLE_BTM BIT(19)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -1089,6 +1090,16 @@
 	int p2p_go_vht;
 
 	/**
+	 * p2p_go_edmg - Default mode for EDMG enable when operating as GO
+	 *
+	 * This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
+	 * Note that regulatory constraints and driver capabilities are
+	 * consulted anyway, so setting it to 1 can't do real harm.
+	 * By default: 0 (disabled)
+	 */
+	int p2p_go_edmg;
+
+	/**
 	 * p2p_go_he - Default mode for 11ax HE enable when operating as GO
 	 *
 	 * This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
@@ -1164,6 +1175,19 @@
 	int *sae_groups;
 
 	/**
+	 * sae_pwe - SAE mechanism for PWE derivation
+	 * 0 = hunting-and-pecking loop only
+	 * 1 = hash-to-element only
+	 * 2 = both hunting-and-pecking loop and hash-to-element enabled
+	 */
+	int sae_pwe;
+
+	/**
+	 * sae_pmkid_in_assoc - Whether to include PMKID in SAE Assoc Req
+	 */
+	int sae_pmkid_in_assoc;
+
+	/**
 	 * dtim_period - Default DTIM period in Beacon intervals
 	 *
 	 * This parameter can be used to set the default value for network
@@ -1491,6 +1515,16 @@
 	int dpp_config_processing;
 
 	/**
+	 * dpp_name - Name for Enrollee's DPP Configuration Request
+	 */
+	char *dpp_name;
+
+	/**
+	 * dpp_mud_url - MUD URL for Enrollee's DPP Configuration Request
+	 */
+	char *dpp_mud_url;
+
+	/**
 	 * coloc_intf_reporting - Colocated interference reporting
 	 *
 	 * dot11CoLocIntfReportingActivated
@@ -1535,6 +1569,15 @@
 	 * 1 = Do not flush BSS entries when the interface becomes disabled
 	 */
 	int bss_no_flush_when_down;
+
+	/**
+	 * disable_btm - Disable BSS transition management in STA
+	 * - Set to 0 to enable BSS transition management
+	 * - Set to 1 to disable BSS transition management
+	 *
+	 * By default BSS transition management is enabled
+	 */
+	int disable_btm;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 2c6035c..305291e 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration backend: text file
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -296,7 +296,7 @@
 {
 	struct wpa_config_blob *blob;
 	char buf[256], *pos;
-	unsigned char *encoded = NULL, *nencoded;
+	char *encoded = NULL, *nencoded;
 	int end = 0;
 	size_t encoded_len = 0, len;
 
@@ -745,9 +745,9 @@
 
 #define STR(t) write_str(f, #t, ssid)
 #define INT(t) write_int(f, #t, ssid->t, 0)
-#define INTe(t) write_int(f, #t, ssid->eap.t, 0)
+#define INTe(t, m) write_int(f, #t, ssid->eap.m, 0)
 #define INT_DEF(t, def) write_int(f, #t, ssid->t, def)
-#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def)
+#define INT_DEFe(t, m, def) write_int(f, #t, ssid->eap.m, def)
 
 	STR(ssid);
 	INT(scan_ssid);
@@ -774,7 +774,9 @@
 	STR(identity);
 	STR(anonymous_identity);
 	STR(imsi_identity);
+	STR(machine_identity);
 	STR(password);
+	STR(machine_password);
 	STR(ca_cert);
 	STR(ca_path);
 	STR(client_cert);
@@ -797,8 +799,20 @@
 	STR(altsubject_match2);
 	STR(domain_suffix_match2);
 	STR(domain_match2);
+	STR(machine_ca_cert);
+	STR(machine_ca_path);
+	STR(machine_client_cert);
+	STR(machine_private_key);
+	STR(machine_private_key_passwd);
+	STR(machine_dh_file);
+	STR(machine_subject_match);
+	STR(machine_check_cert_subject);
+	STR(machine_altsubject_match);
+	STR(machine_domain_suffix_match);
+	STR(machine_domain_match);
 	STR(phase1);
 	STR(phase2);
+	STR(machine_phase2);
 	STR(pcsc);
 	STR(pin);
 	STR(engine_id);
@@ -810,11 +824,12 @@
 	STR(engine2_id);
 	STR(cert2_id);
 	STR(ca_cert2_id);
-	INTe(engine);
-	INTe(engine2);
+	INTe(engine, cert.engine);
+	INTe(engine2, phase2_cert.engine);
+	INTe(machine_engine, machine_cert.engine);
 	INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
 	STR(openssl_ciphers);
-	INTe(erp);
+	INTe(erp, erp);
 #endif /* IEEE8021X_EAPOL */
 	for (i = 0; i < 4; i++)
 		write_wep_key(f, i, ssid);
@@ -823,13 +838,17 @@
 #ifdef IEEE8021X_EAPOL
 	INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
 	STR(pac_file);
-	INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
-	INTe(ocsp);
-	INT_DEFe(sim_num, DEFAULT_USER_SELECTED_SIM);
+	INT_DEFe(fragment_size, fragment_size, DEFAULT_FRAGMENT_SIZE);
+	INTe(ocsp, cert.ocsp);
+	INTe(ocsp2, phase2_cert.ocsp);
+	INTe(machine_ocsp, machine_cert.ocsp);
+	INT_DEFe(sim_num, sim_num, DEFAULT_USER_SELECTED_SIM);
 #endif /* IEEE8021X_EAPOL */
 	INT(mode);
 	INT(no_auto_peer);
 	INT(frequency);
+	INT(enable_edmg);
+	INT(edmg_channel);
 	INT(fixed_freq);
 #ifdef CONFIG_ACS
 	INT(acs);
@@ -846,10 +865,8 @@
 	INT(pbss);
 	INT(wps_disabled);
 	INT(fils_dh_group);
-#ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
-#endif /* CONFIG_IEEE80211W */
 	STR(id_str);
 #ifdef CONFIG_P2P
 	write_go_p2p_dev_addr(f, ssid);
@@ -894,6 +911,7 @@
 	INT(owe_group);
 	INT(owe_only);
 	INT(multi_ap_backhaul_sta);
+	INT(ft_eap_pmksa_caching);
 #ifdef CONFIG_HT_OVERRIDES
 	INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
 	INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
@@ -1080,7 +1098,7 @@
 #ifndef CONFIG_NO_CONFIG_BLOBS
 static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
 {
-	unsigned char *encoded;
+	char *encoded;
 
 	encoded = base64_encode(blob->data, blob->len, NULL);
 	if (encoded == NULL)
@@ -1269,6 +1287,8 @@
 		fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht);
 	if (config->p2p_go_he)
 		fprintf(f, "p2p_go_he=%d\n", config->p2p_go_he);
+	if (config->p2p_go_edmg)
+		fprintf(f, "p2p_go_edmg=%d\n", config->p2p_go_edmg);
 	if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW)
 		fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow);
 	if (config->p2p_disabled)
@@ -1389,6 +1409,13 @@
 		fprintf(f, "\n");
 	}
 
+	if (config->sae_pwe)
+		fprintf(f, "sae_pwe=%d\n", config->sae_pwe);
+
+	if (config->sae_pmkid_in_assoc)
+		fprintf(f, "sae_pmkid_in_assoc=%d\n",
+			config->sae_pmkid_in_assoc);
+
 	if (config->ap_vendor_elements) {
 		int i, len = wpabuf_len(config->ap_vendor_elements);
 		const u8 *p = wpabuf_head_u8(config->ap_vendor_elements);
@@ -1547,7 +1574,8 @@
 	if (config->bss_no_flush_when_down)
 		fprintf(f, "bss_no_flush_when_down=%d\n",
 			config->bss_no_flush_when_down);
-
+	if (config->disable_btm)
+		fprintf(f, "disable_btm=1\n");
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 1b2b1f1..af8317b 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -13,7 +13,6 @@
 #include "utils/list.h"
 #include "eap_peer/eap_config.h"
 
-
 #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
 #define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
 			     EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
@@ -48,6 +47,15 @@
 	u8 p2p;
 };
 
+enum wpas_mode {
+	WPAS_MODE_INFRA = 0,
+	WPAS_MODE_IBSS = 1,
+	WPAS_MODE_AP = 2,
+	WPAS_MODE_P2P_GO = 3,
+	WPAS_MODE_P2P_GROUP_FORMATION = 4,
+	WPAS_MODE_MESH = 5,
+};
+
 /**
  * struct wpa_ssid - Network configuration data
  *
@@ -204,6 +212,8 @@
 	 */
 	char *sae_password_id;
 
+	struct sae_pt *pt;
+
 	/**
 	 * ext_psk - PSK/passphrase name in external storage
 	 *
@@ -394,14 +404,7 @@
 	 * CCMP, but not both), and psk must also be set (either directly or
 	 * using ASCII passphrase).
 	 */
-	enum wpas_mode {
-		WPAS_MODE_INFRA = 0,
-		WPAS_MODE_IBSS = 1,
-		WPAS_MODE_AP = 2,
-		WPAS_MODE_P2P_GO = 3,
-		WPAS_MODE_P2P_GROUP_FORMATION = 4,
-		WPAS_MODE_MESH = 5,
-	} mode;
+	enum wpas_mode mode;
 
 	/**
 	 * pbss - Whether to use PBSS. Relevant to DMG networks only.
@@ -444,7 +447,6 @@
 	 */
 	char *id_str;
 
-#ifdef CONFIG_IEEE80211W
 	/**
 	 * ieee80211w - Whether management frame protection is enabled
 	 *
@@ -458,7 +460,6 @@
 	 * followed).
 	 */
 	enum mfp_options ieee80211w;
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_OCV
 	/**
@@ -484,6 +485,23 @@
 	int frequency;
 
 	/**
+	 * enable_edmg - Enable EDMG feature in STA/AP mode
+	 *
+	 * This flag is used for enabling the EDMG capability in STA/AP mode.
+	 */
+	int enable_edmg;
+
+	/**
+	 * edmg_channel - EDMG channel number
+	 *
+	 * This value is used to configure the EDMG channel bonding feature.
+	 * In AP mode it defines the EDMG channel to start the AP on.
+	 * in STA mode it defines the EDMG channel to use for connection
+	 * (if supported by AP).
+	 */
+	u8 edmg_channel;
+
+	/**
 	 * fixed_freq - Use fixed frequency for IBSS
 	 */
 	int fixed_freq;
@@ -1005,6 +1023,16 @@
 	 * 1 = Multi-AP backhaul station
 	 */
 	int multi_ap_backhaul_sta;
+
+	/**
+	 * ft_eap_pmksa_caching - Whether FT-EAP PMKSA caching is allowed
+	 * 0 = do not try to use PMKSA caching with FT-EAP
+	 * 1 = try to use PMKSA caching with FT-EAP
+	 *
+	 * This controls whether to try to use PMKSA caching with FT-EAP for the
+	 * FT initial mobility domain association.
+	 */
+	int ft_eap_pmksa_caching;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 6328e91..0f2a30a 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration backend: Windows registry
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -868,9 +868,9 @@
 
 #define STR(t) write_str(netw, #t, ssid)
 #define INT(t) write_int(netw, #t, ssid->t, 0)
-#define INTe(t) write_int(netw, #t, ssid->eap.t, 0)
+#define INTe(t, m) write_int(netw, #t, ssid->eap.m, 0)
 #define INT_DEF(t, def) write_int(netw, #t, ssid->t, def)
-#define INT_DEFe(t, def) write_int(netw, #t, ssid->eap.t, def)
+#define INT_DEFe(t, m, def) write_int(netw, #t, ssid->eap.m, def)
 
 	STR(ssid);
 	INT(scan_ssid);
@@ -920,8 +920,8 @@
 	STR(engine2_id);
 	STR(cert2_id);
 	STR(ca_cert2_id);
-	INTe(engine);
-	INTe(engine2);
+	INTe(engine, cert.engine);
+	INTe(engine2, phase2_cert.engine);
 	INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
 #endif /* IEEE8021X_EAPOL */
 	for (i = 0; i < 4; i++)
@@ -931,21 +931,20 @@
 #ifdef IEEE8021X_EAPOL
 	INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
 	STR(pac_file);
-	INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+	INT_DEFe(fragment_size, fragment_size, DEFAULT_FRAGMENT_SIZE);
 #endif /* IEEE8021X_EAPOL */
 	INT(mode);
 	write_int(netw, "proactive_key_caching", ssid->proactive_key_caching,
 		  -1);
 	INT(disabled);
-#ifdef CONFIG_IEEE80211W
 	write_int(netw, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
-#endif /* CONFIG_IEEE80211W */
 	STR(id_str);
 #ifdef CONFIG_HS20
 	INT(update_identifier);
 #endif /* CONFIG_HS20 */
 	INT(group_rekey);
+	INT(ft_eap_pmksa_caching);
 
 #undef STR
 #undef INT
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 198ac56..be5ea17 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -8,7 +8,6 @@
 
 #include "utils/includes.h"
 #ifdef CONFIG_TESTING_OPTIONS
-#include <net/ethernet.h>
 #include <netinet/ip.h>
 #endif /* CONFIG_TESTING_OPTIONS */
 
@@ -58,6 +57,12 @@
 #include "dpp_supplicant.h"
 #include "sme.h"
 
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#elif !defined(__CYGWIN__) && !defined(CONFIG_NATIVE_WINDOWS)
+#include <net/ethernet.h>
+#endif
+
 static int wpa_supplicant_global_iface_list(struct wpa_global *global,
 					    char *buf, int len);
 static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
@@ -658,6 +663,42 @@
 		wpa_s->ignore_assoc_disallow = !!atoi(value);
 		wpa_drv_ignore_assoc_disallow(wpa_s,
 					      wpa_s->ignore_assoc_disallow);
+	} else if (os_strcasecmp(cmd, "ignore_sae_h2e_only") == 0) {
+		wpa_s->ignore_sae_h2e_only = !!atoi(value);
+	} else if (os_strcasecmp(cmd, "extra_sae_rejected_groups") == 0) {
+		char *pos;
+
+		os_free(wpa_s->extra_sae_rejected_groups);
+		wpa_s->extra_sae_rejected_groups = NULL;
+		pos = value;
+		while (pos && pos[0]) {
+			int group;
+
+			group = atoi(pos);
+			wpa_printf(MSG_DEBUG,
+				   "TESTING: Extra rejection of SAE group %d",
+				   group);
+			if (group)
+				int_array_add_unique(
+					&wpa_s->extra_sae_rejected_groups,
+					group);
+			pos = os_strchr(pos, ' ');
+			if (!pos)
+				break;
+			pos++;
+		}
+	} else if (os_strcasecmp(cmd, "rsnxe_override_assoc") == 0) {
+		wpabuf_free(wpa_s->rsnxe_override_assoc);
+		if (os_strcmp(value, "NULL") == 0)
+			wpa_s->rsnxe_override_assoc = NULL;
+		else
+			wpa_s->rsnxe_override_assoc = wpabuf_parse_bin(value);
+	} else if (os_strcasecmp(cmd, "rsnxe_override_eapol") == 0) {
+		wpabuf_free(wpa_s->rsnxe_override_eapol);
+		if (os_strcmp(value, "NULL") == 0)
+			wpa_s->rsnxe_override_eapol = NULL;
+		else
+			wpa_s->rsnxe_override_eapol = wpabuf_parse_bin(value);
 	} else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) {
 		wpa_s->reject_btm_req_reason = atoi(value);
 	} else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) {
@@ -2654,7 +2695,6 @@
 		pos += ret;
 	}
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
 				  pos == start ? "" : "+");
@@ -2669,7 +2709,6 @@
 			return pos;
 		pos += ret;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_SUITEB
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
@@ -2873,6 +2912,15 @@
 	}
 	if (bss_is_dmg(bss)) {
 		const char *s;
+
+		if (get_ie_ext((const u8 *) (bss + 1), bss->ie_len,
+			       WLAN_EID_EXT_EDMG_OPERATION)) {
+			ret = os_snprintf(pos, end - pos, "[EDMG]");
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
 		ret = os_snprintf(pos, end - pos, "[DMG]");
 		if (os_snprintf_error(end - pos, ret))
 			return -1;
@@ -3129,6 +3177,49 @@
 	return wpas_mesh_peer_add(wpa_s, addr, duration);
 }
 
+
+static int wpa_supplicant_ctrl_iface_mesh_link_probe(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	struct ether_header *eth;
+	u8 addr[ETH_ALEN];
+	u8 *buf;
+	char *pos;
+	size_t payload_len = 0, len;
+	int ret = -1;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = os_strstr(cmd, " payload=");
+	if (pos) {
+		pos = pos + 9;
+		payload_len = os_strlen(pos);
+		if (payload_len & 1)
+			return -1;
+
+		payload_len /= 2;
+	}
+
+	len = ETH_HLEN + payload_len;
+	buf = os_malloc(len);
+	if (!buf)
+		return -1;
+
+	eth = (struct ether_header *) buf;
+	os_memcpy(eth->ether_dhost, addr, ETH_ALEN);
+	os_memcpy(eth->ether_shost, wpa_s->own_addr, ETH_ALEN);
+	eth->ether_type = htons(ETH_P_802_3);
+
+	if (payload_len && hexstr2bin(pos, buf + ETH_HLEN, payload_len) < 0)
+		goto fail;
+
+	ret = wpa_drv_mesh_link_probe(wpa_s, addr, buf, len);
+fail:
+	os_free(buf);
+	return -ret;
+}
+
 #endif /* CONFIG_MESH */
 
 
@@ -5198,10 +5289,8 @@
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
-#ifdef CONFIG_IEEE80211W
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
-#endif /* CONFIG_IEEE80211W */
 
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
 			0);
@@ -5548,17 +5637,17 @@
 	if (freq2 < 0)
 		return -1;
 	if (freq2)
-		return VHT_CHANWIDTH_80P80MHZ;
+		return CHANWIDTH_80P80MHZ;
 
 	switch (chwidth) {
 	case 0:
 	case 20:
 	case 40:
-		return VHT_CHANWIDTH_USE_HT;
+		return CHANWIDTH_USE_HT;
 	case 80:
-		return VHT_CHANWIDTH_80MHZ;
+		return CHANWIDTH_80MHZ;
 	case 160:
-		return VHT_CHANWIDTH_160MHZ;
+		return CHANWIDTH_160MHZ;
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
 			   chwidth);
@@ -5584,6 +5673,7 @@
 	int freq = 0;
 	int pd;
 	int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
+	int edmg;
 	u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL;
 	size_t group_ssid_len = 0;
 	int he;
@@ -5599,7 +5689,7 @@
 	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
 	 * [persistent|persistent=<network id>]
 	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
-	 * [ht40] [vht] [he] [auto] [ssid=<hexdump>] */
+	 * [ht40] [vht] [he] [edmg] [auto] [ssid=<hexdump>] */
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -5631,6 +5721,7 @@
 	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
 		vht;
 	he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
+	edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg;
 
 	pos2 = os_strstr(pos, " go_intent=");
 	if (pos2) {
@@ -5701,7 +5792,7 @@
 	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,
+				   pd, ht40, vht, max_oper_chwidth, he, edmg,
 				   group_ssid, group_ssid_len);
 	if (new_pin == -2) {
 		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
@@ -6258,6 +6349,7 @@
 	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;
 
 	id = atoi(cmd);
 	pos = os_strstr(cmd, " peer=");
@@ -6295,6 +6387,7 @@
 	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
 		vht;
 	he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
+	edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg;
 
 	pos = os_strstr(cmd, "freq2=");
 	if (pos)
@@ -6309,7 +6402,7 @@
 		return -1;
 
 	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
-			       max_oper_chwidth, pref_freq, he);
+			       max_oper_chwidth, pref_freq, he, edmg);
 }
 
 
@@ -6358,7 +6451,7 @@
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
 					 int id, int freq, int vht_center_freq2,
 					 int ht40, int vht, int vht_chwidth,
-					 int he)
+					 int he, int edmg)
 {
 	struct wpa_ssid *ssid;
 
@@ -6372,7 +6465,8 @@
 
 	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
 					     vht_center_freq2, 0, ht40, vht,
-					     vht_chwidth, he, NULL, 0, 0);
+					     vht_chwidth, he, edmg,
+					     NULL, 0, 0);
 }
 
 
@@ -6382,6 +6476,7 @@
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
 	int he = wpa_s->conf->p2p_go_he;
+	int edmg = wpa_s->conf->p2p_go_edmg;
 	int max_oper_chwidth, chwidth = 0, freq2 = 0;
 	char *token, *context = NULL;
 #ifdef CONFIG_ACS
@@ -6406,6 +6501,8 @@
 			ht40 = 1;
 		} else if (os_strcmp(token, "he") == 0) {
 			he = 1;
+		} else if (os_strcmp(token, "edmg") == 0) {
+			edmg = 1;
 		} else if (os_strcmp(token, "persistent") == 0) {
 			persistent = 1;
 		} else {
@@ -6443,10 +6540,11 @@
 	if (group_id >= 0)
 		return p2p_ctrl_group_add_persistent(wpa_s, group_id,
 						     freq, freq2, ht40, vht,
-						     max_oper_chwidth, he);
+						     max_oper_chwidth, he,
+						     edmg);
 
 	return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
-				  max_oper_chwidth, he);
+				  max_oper_chwidth, he, edmg);
 }
 
 
@@ -7992,12 +8090,19 @@
 	wpa_s->ignore_auth_resp = 0;
 	wpa_s->ignore_assoc_disallow = 0;
 	wpa_s->testing_resend_assoc = 0;
+	wpa_s->ignore_sae_h2e_only = 0;
 	wpa_s->reject_btm_req_reason = 0;
 	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
 	os_free(wpa_s->get_pref_freq_list_override);
 	wpa_s->get_pref_freq_list_override = NULL;
 	wpabuf_free(wpa_s->sae_commit_override);
 	wpa_s->sae_commit_override = NULL;
+	os_free(wpa_s->extra_sae_rejected_groups);
+	wpa_s->extra_sae_rejected_groups = NULL;
+	wpabuf_free(wpa_s->rsnxe_override_assoc);
+	wpa_s->rsnxe_override_assoc = NULL;
+	wpabuf_free(wpa_s->rsnxe_override_eapol);
+	wpa_s->rsnxe_override_eapol = NULL;
 #ifdef CONFIG_DPP
 	os_free(wpa_s->dpp_config_obj_override);
 	wpa_s->dpp_config_obj_override = NULL;
@@ -9412,16 +9517,16 @@
 
 		if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
 		    nr_len < NR_IE_MIN_LEN) {
-			wpa_printf(MSG_DEBUG,
-				   "CTRL: Invalid Neighbor Report element: id=%u len=%u",
-				   data[0], nr_len);
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"CTRL: Invalid Neighbor Report element: id=%u len=%u",
+				data[0], nr_len);
 			goto out;
 		}
 
 		if (2U + nr_len > len) {
-			wpa_printf(MSG_DEBUG,
-				   "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
-				   data[0], len, nr_len);
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
+				data[0], len, nr_len);
 			goto out;
 		}
 		pos += 2;
@@ -9491,8 +9596,8 @@
 	ssid_s = os_strstr(cmd, "ssid=");
 	if (ssid_s) {
 		if (ssid_parse(ssid_s + 5, &ssid)) {
-			wpa_printf(MSG_ERROR,
-				   "CTRL: Send Neighbor Report: bad SSID");
+			wpa_msg(wpa_s, MSG_INFO,
+				"CTRL: Send Neighbor Report: bad SSID");
 			return -1;
 		}
 
@@ -9583,59 +9688,10 @@
 		return -1;
 	}
 
-	if (!enable) {
-		wpas_mac_addr_rand_scan_clear(wpa_s, type);
-		if (wpa_s->pno) {
-			if (type & MAC_ADDR_RAND_PNO) {
-				wpas_stop_pno(wpa_s);
-				wpas_start_pno(wpa_s);
-			}
-		} else if (wpa_s->sched_scanning &&
-			   (type & MAC_ADDR_RAND_SCHED_SCAN)) {
-			wpas_scan_restart_sched_scan(wpa_s);
-		}
-		return 0;
-	}
+	if (!enable)
+		return wpas_disable_mac_addr_randomization(wpa_s, type);
 
-	if ((addr && !mask) || (!addr && mask)) {
-		wpa_printf(MSG_INFO,
-			   "CTRL: MAC_RAND_SCAN invalid addr/mask combination");
-		return -1;
-	}
-
-	if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
-		wpa_printf(MSG_INFO,
-			   "CTRL: MAC_RAND_SCAN cannot allow multicast address");
-		return -1;
-	}
-
-	if (type & MAC_ADDR_RAND_SCAN) {
-		if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
-					    addr, mask))
-			return -1;
-	}
-
-	if (type & MAC_ADDR_RAND_SCHED_SCAN) {
-		if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
-					    addr, mask))
-			return -1;
-
-		if (wpa_s->sched_scanning && !wpa_s->pno)
-			wpas_scan_restart_sched_scan(wpa_s);
-	}
-
-	if (type & MAC_ADDR_RAND_PNO) {
-		if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO,
-					    addr, mask))
-			return -1;
-
-		if (wpa_s->pno) {
-			wpas_stop_pno(wpa_s);
-			wpas_start_pno(wpa_s);
-		}
-	}
-
-	return 0;
+	return wpas_enable_mac_addr_randomization(wpa_s, type, addr, mask);
 }
 
 
@@ -10171,6 +10227,9 @@
 	} else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) {
 		if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "MESH_LINK_PROBE ", 16) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_link_probe(wpa_s, buf + 16))
+			reply_len = -1;
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
@@ -10627,12 +10686,10 @@
 	} else if (os_strcmp(buf, "RESEND_ASSOC") == 0) {
 		if (wpas_ctrl_resend_assoc(wpa_s) < 0)
 			reply_len = -1;
-#ifdef CONFIG_IEEE80211W
 	} else if (os_strcmp(buf, "UNPROT_DEAUTH") == 0) {
 		sme_event_unprot_disconnect(
 			wpa_s, wpa_s->bssid, NULL,
 			WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
-#endif /* CONFIG_IEEE80211W */
 #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)
@@ -10673,6 +10730,17 @@
 			if (os_snprintf_error(reply_size, reply_len))
 				reply_len = -1;
 		}
+	} else if (os_strncmp(buf, "DPP_NFC_URI ", 12) == 0) {
+		int res;
+
+		res = wpas_dpp_nfc_uri(wpa_s, buf + 12);
+		if (res < 0) {
+			reply_len = -1;
+		} else {
+			reply_len = os_snprintf(reply, reply_size, "%d", res);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
 		int res;
 
@@ -10745,6 +10813,16 @@
 	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
 		if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0)
 			reply_len = -1;
+#ifdef CONFIG_DPP2
+	} else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
+		if (wpas_dpp_controller_start(wpa_s, buf + 20) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
+		if (wpas_dpp_controller_start(wpa_s, NULL) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
+		dpp_controller_stop(wpa_s->dpp);
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index fc2fc2e..e9e77bd 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2855,30 +2855,6 @@
 	  NULL,
 	  NULL
 	},
-	{
-	  "RoamTime", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
-	  wpas_dbus_getter_roam_time,
-	  NULL,
-	  NULL
-	},
-	{
-	  "RoamComplete", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
-	  wpas_dbus_getter_roam_complete,
-	  NULL,
-	  NULL
-	},
-	{
-	  "SessionLength", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
-	  wpas_dbus_getter_session_length,
-	  NULL,
-	  NULL
-	},
-	{
-	  "BSSTMStatus", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
-	  wpas_dbus_getter_bss_tm_status,
-	  NULL,
-	  NULL
-	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -3786,6 +3762,30 @@
 	  NULL,
 	  NULL
 	},
+	{
+	  "RoamTime", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+	  wpas_dbus_getter_roam_time,
+	  NULL,
+	  NULL
+	},
+	{
+	  "RoamComplete", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+	  wpas_dbus_getter_roam_complete,
+	  NULL,
+	  NULL
+	},
+	{
+	  "SessionLength", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+	  wpas_dbus_getter_session_length,
+	  NULL,
+	  NULL
+	},
+	{
+	  "BSSTMStatus", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+	  wpas_dbus_getter_bss_tm_status,
+	  NULL,
+	  NULL
+	},
 #ifdef CONFIG_MESH
 	{ "MeshPeers", WPAS_DBUS_NEW_IFACE_MESH, "aay",
 	  wpas_dbus_getter_mesh_peers,
@@ -3803,6 +3803,12 @@
 	  NULL,
 	  NULL
 	},
+	{ "MACAddressRandomizationMask", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  "a{say}",
+	  wpas_dbus_getter_mac_address_randomization_mask,
+	  wpas_dbus_setter_mac_address_randomization_mask,
+	  NULL
+	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -4791,8 +4797,8 @@
 
 	if (!wpa_s->dbus_groupobj_path) {
 		wpa_printf(MSG_DEBUG,
-			   "%s: Group object '%s' already unregistered",
-			   __func__, wpa_s->dbus_groupobj_path);
+			   "%s: Group object has already unregistered",
+			   __func__);
 		return;
 	}
 
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 6c36d91..4b6dabc 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -137,8 +137,15 @@
 
 static const char * const dont_quote[] = {
 	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
-	"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
-	"bssid", "scan_freq", "freq_list", NULL
+	"bssid", "scan_freq", "freq_list", "scan_ssid", "bssid_hint",
+	"bssid_blacklist", "bssid_whitelist", "group_mgmt",
+#ifdef CONFIG_MESH
+	"mesh_basic_rates",
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_P2P
+	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
+#endif /* CONFIG_P2P */
+	NULL
 };
 
 static dbus_bool_t should_quote_opt(const char *key)
@@ -984,8 +991,7 @@
 	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
-	const char *capabilities[10] = { NULL, NULL, NULL, NULL, NULL, NULL,
-					NULL, NULL, NULL, NULL };
+	const char *capabilities[11];
 	size_t num_items = 0;
 #ifdef CONFIG_FILS
 	struct wpa_global *global = user_data;
@@ -1012,9 +1018,7 @@
 #ifdef CONFIG_INTERWORKING
 	capabilities[num_items++] = "interworking";
 #endif /* CONFIG_INTERWORKING */
-#ifdef CONFIG_IEEE80211W
 	capabilities[num_items++] = "pmf";
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_MESH
 	capabilities[num_items++] = "mesh";
 #endif /* CONFIG_MESH */
@@ -1030,6 +1034,9 @@
 #ifdef CONFIG_SHA384
 	capabilities[num_items++] = "sha384";
 #endif /* CONFIG_SHA384 */
+#ifdef CONFIG_OWE
+	capabilities[num_items++] = "owe";
+#endif /* CONFIG_OWE */
 
 	return wpas_dbus_simple_array_property_getter(iter,
 						      DBUS_TYPE_STRING,
@@ -2753,11 +2760,9 @@
 				goto nomem;
 
 /* TODO: Ensure that driver actually supports sha256 encryption. */
-#ifdef CONFIG_IEEE80211W
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "wpa-eap-sha256"))
 				goto nomem;
-#endif /* CONFIG_IEEE80211W */
 		}
 
 		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
@@ -2771,11 +2776,9 @@
 				goto nomem;
 
 /* TODO: Ensure that driver actually supports sha256 encryption. */
-#ifdef CONFIG_IEEE80211W
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "wpa-psk-sha256"))
 				goto nomem;
-#endif /* CONFIG_IEEE80211W */
 		}
 
 		if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
@@ -3990,6 +3993,173 @@
 
 
 /**
+ * wpas_dbus_setter_mac_address_randomization_mask - Set masks used for
+ * MAC address randomization
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "MACAddressRandomizationMask" property.
+ */
+dbus_bool_t wpas_dbus_setter_mac_address_randomization_mask(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
+	const char *key;
+	unsigned int rand_type = 0;
+	const u8 *mask;
+	int mask_len;
+	unsigned int rand_types_to_disable = MAC_ADDR_RAND_ALL;
+
+	dbus_message_iter_recurse(iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) {
+		dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+				     "invalid message format");
+		return FALSE;
+	}
+	dbus_message_iter_recurse(&variant_iter, &dict_iter);
+	while (dbus_message_iter_get_arg_type(&dict_iter) ==
+	       DBUS_TYPE_DICT_ENTRY) {
+		dbus_message_iter_recurse(&dict_iter, &entry_iter);
+		if (dbus_message_iter_get_arg_type(&entry_iter) !=
+		    DBUS_TYPE_STRING) {
+			dbus_set_error(error, DBUS_ERROR_FAILED,
+				       "%s: key not a string", __func__);
+			return FALSE;
+		}
+		dbus_message_iter_get_basic(&entry_iter, &key);
+		dbus_message_iter_next(&entry_iter);
+		if (dbus_message_iter_get_arg_type(&entry_iter) !=
+		    DBUS_TYPE_ARRAY ||
+		    dbus_message_iter_get_element_type(&entry_iter) !=
+		    DBUS_TYPE_BYTE) {
+			dbus_set_error(error, DBUS_ERROR_FAILED,
+				       "%s: mask was not a byte array",
+				       __func__);
+			return FALSE;
+		}
+		dbus_message_iter_recurse(&entry_iter, &array_iter);
+		dbus_message_iter_get_fixed_array(&array_iter, &mask,
+						  &mask_len);
+
+		if (os_strcmp(key, "scan") == 0) {
+			rand_type = MAC_ADDR_RAND_SCAN;
+		} else if (os_strcmp(key, "sched_scan") == 0) {
+			rand_type = MAC_ADDR_RAND_SCHED_SCAN;
+		} else if (os_strcmp(key, "pno") == 0) {
+			rand_type = MAC_ADDR_RAND_PNO;
+		} else {
+			dbus_set_error(error, DBUS_ERROR_FAILED,
+				       "%s: bad scan type \"%s\"",
+				       __func__, key);
+			return FALSE;
+		}
+
+		if (mask_len != ETH_ALEN) {
+			dbus_set_error(error, DBUS_ERROR_FAILED,
+				       "%s: malformed MAC mask given",
+				       __func__);
+			return FALSE;
+		}
+
+		if (wpas_enable_mac_addr_randomization(
+			    wpa_s, rand_type, wpa_s->perm_addr, mask)) {
+			dbus_set_error(error, DBUS_ERROR_FAILED,
+				       "%s: failed to set up MAC address randomization for %s",
+				       __func__, key);
+			return FALSE;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "%s: Enabled MAC address randomization for %s with mask: "
+			   MACSTR, wpa_s->ifname, key, MAC2STR(mask));
+		rand_types_to_disable &= ~rand_type;
+		dbus_message_iter_next(&dict_iter);
+	}
+
+	if (rand_types_to_disable &&
+	    wpas_disable_mac_addr_randomization(wpa_s, rand_types_to_disable)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: failed to disable MAC address randomization",
+			       __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_mac_address_randomization_mask(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
+	unsigned int i;
+	u8 mask_buf[ETH_ALEN];
+	/* Read docs on dbus_message_iter_append_fixed_array() for why this
+	 * is necessary... */
+	u8 *mask = mask_buf;
+	static const struct {
+		const char *key;
+		unsigned int type;
+	} types[] = {
+		{ "scan", MAC_ADDR_RAND_SCAN },
+		{ "sched_scan", MAC_ADDR_RAND_SCHED_SCAN },
+		{ "pno", MAC_ADDR_RAND_PNO }
+	};
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{say}", &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      "{say}", &dict_iter)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(types); i++) {
+		if (wpas_mac_addr_rand_scan_get_mask(wpa_s, types[i].type,
+						     mask))
+			continue;
+
+		if (!dbus_message_iter_open_container(&dict_iter,
+						      DBUS_TYPE_DICT_ENTRY,
+						      NULL, &entry_iter) ||
+		    !dbus_message_iter_append_basic(&entry_iter,
+						    DBUS_TYPE_STRING,
+						    &types[i].key) ||
+		    !dbus_message_iter_open_container(&entry_iter,
+						      DBUS_TYPE_ARRAY,
+						      DBUS_TYPE_BYTE_AS_STRING,
+						      &array_iter) ||
+		    !dbus_message_iter_append_fixed_array(&array_iter,
+							  DBUS_TYPE_BYTE,
+							  &mask,
+							  ETH_ALEN) ||
+		    !dbus_message_iter_close_container(&entry_iter,
+						       &array_iter) ||
+		    !dbus_message_iter_close_container(&dict_iter,
+						       &entry_iter)) {
+			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+					     "no memory");
+			return FALSE;
+		}
+	}
+
+	if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
  * wpas_dbus_getter_sta_address - Return the address of a connected station
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -4497,7 +4667,7 @@
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
 	const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
-	const char *key_mgmt[15]; /* max 15 key managements may be supported */
+	const char *key_mgmt[16]; /* max 16 key managements may be supported */
 	int n;
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
@@ -4550,6 +4720,10 @@
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_SAE)
 		key_mgmt[n++] = "ft-sae";
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_OWE
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_OWE)
+		key_mgmt[n++] = "owe";
+#endif /* CONFIG_OWE */
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE)
 		key_mgmt[n++] = "wpa-none";
 
@@ -4608,11 +4782,9 @@
 	/* Management group (RSN only) */
 	if (ie_data->proto == WPA_PROTO_RSN) {
 		switch (ie_data->mgmt_group_cipher) {
-#ifdef CONFIG_IEEE80211W
 		case WPA_CIPHER_AES_128_CMAC:
 			group = "aes128cmac";
 			break;
-#endif /* CONFIG_IEEE80211W */
 		default:
 			group = "";
 			break;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index d922ce1..afa26ef 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -177,6 +177,8 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_module_path);
 DECLARE_ACCESSOR(wpas_dbus_getter_blobs);
 DECLARE_ACCESSOR(wpas_dbus_getter_stas);
+DECLARE_ACCESSOR(wpas_dbus_getter_mac_address_randomization_mask);
+DECLARE_ACCESSOR(wpas_dbus_setter_mac_address_randomization_mask);
 DECLARE_ACCESSOR(wpas_dbus_getter_sta_address);
 DECLARE_ACCESSOR(wpas_dbus_getter_sta_aid);
 DECLARE_ACCESSOR(wpas_dbus_getter_sta_caps);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 8cdd885..7a65673 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -40,6 +40,14 @@
 }
 
 
+static dbus_bool_t no_p2p_mgmt_interface(DBusError *error)
+{
+	dbus_set_error_const(error, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
+			     "Could not find P2P mgmt interface");
+	return FALSE;
+}
+
+
 /**
  * Parses out the mac address from the peer object path.
  * @peer_path - object path of the form
@@ -78,6 +86,22 @@
 }
 
 
+/**
+ * wpas_dbus_error_no_p2p_mgmt_iface - Return a new InterfaceUnknown error
+ * message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an unknown interface error.
+ */
+static DBusMessage * wpas_dbus_error_no_p2p_mgmt_iface(DBusMessage *message)
+{
+	wpa_printf(MSG_DEBUG, "dbus: Could not find P2P mgmt interface");
+	return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
+				      "Could not find P2P mgmt interface");
+}
+
+
 DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
 					 struct wpa_supplicant *wpa_s)
 {
@@ -145,6 +169,10 @@
 	}
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s) {
+		reply = wpas_dbus_error_no_p2p_mgmt_iface(message);
+		goto error_nop2p;
+	}
 
 	if (wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types,
 			  req_dev_types, NULL, 0, 0, NULL, freq))
@@ -157,8 +185,9 @@
 error_clear:
 	wpa_dbus_dict_entry_clear(&entry);
 error:
-	os_free(req_dev_types);
 	reply = wpas_dbus_error_invalid_args(message, entry.key);
+error_nop2p:
+	os_free(req_dev_types);
 	return reply;
 }
 
@@ -166,7 +195,9 @@
 DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s)
 {
-	wpas_p2p_stop_find(wpa_s->global->p2p_init_wpa_s);
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (wpa_s)
+		wpas_p2p_stop_find(wpa_s);
 	return NULL;
 }
 
@@ -185,6 +216,8 @@
 		return wpas_dbus_error_invalid_args(message, NULL);
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s)
+		return wpas_dbus_error_no_p2p_mgmt_iface(message);
 
 	if (wpas_p2p_reject(wpa_s, peer_addr) < 0)
 		return wpas_dbus_error_unknown_error(message,
@@ -204,6 +237,8 @@
 		return wpas_dbus_error_no_memory(message);
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s)
+		return wpas_dbus_error_no_p2p_mgmt_iface(message);
 
 	if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) {
 		return dbus_message_new_error(message,
@@ -245,6 +280,8 @@
 	}
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s)
+		return wpas_dbus_error_no_p2p_mgmt_iface(message);
 
 	if (wpas_p2p_ext_listen(wpa_s, period, interval))
 		return wpas_dbus_error_unknown_error(
@@ -350,6 +387,10 @@
 	}
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s) {
+		reply = wpas_dbus_error_no_p2p_mgmt_iface(message);
+		goto out;
+	}
 
 	if (pg_object_path != NULL) {
 		char *net_id_str;
@@ -384,14 +425,14 @@
 			goto inv_args;
 
 		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
-						  0, 0, 0, NULL, 0, 0)) {
+						  0, 0, 0, 0, NULL, 0, 0)) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
 			goto out;
 		}
 	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0,
-				      0, 0))
+				      0, 0, 0))
 		goto inv_args;
 
 out:
@@ -433,6 +474,12 @@
 				     "P2P is not available for this interface");
 		return FALSE;
 	}
+	if (!wpa_s->global->p2p_init_wpa_s) {
+		if (out_reply)
+			*out_reply = wpas_dbus_error_no_p2p_mgmt_iface(
+				message);
+		return no_p2p_mgmt_interface(error);
+	}
 	return TRUE;
 }
 
@@ -605,7 +652,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,
+				   go_intent, freq, 0, -1, 0, 0, 0, 0, 0, 0,
 				   NULL, 0);
 
 	if (new_pin >= 0) {
@@ -763,7 +810,7 @@
 			goto err;
 
 		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0,
-				    0, 0) < 0) {
+				    0, 0, 0) < 0) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -822,6 +869,8 @@
 		return wpas_dbus_error_invalid_args(message, NULL);
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s)
+		return wpas_dbus_error_no_p2p_mgmt_iface(message);
 
 	if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
 			       WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
@@ -1882,6 +1931,8 @@
 
 	wpa_s = peer_args->wpa_s;
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s)
+		return no_p2p_mgmt_interface(error);
 
 	wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr);
 	if (wpa_s_go) {
@@ -1963,6 +2014,9 @@
 	dbus_bool_t success = FALSE;
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s)
+		return no_p2p_mgmt_interface(error);
+
 	if (!wpa_s->parent->dbus_new_path)
 		return FALSE;
 
@@ -2077,6 +2131,11 @@
 	dbus_message_iter_init(message, &iter);
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s) {
+		reply = wpas_dbus_error_no_p2p_mgmt_iface(message);
+		goto err;
+	}
+
 	if (wpa_s->parent->dbus_new_path)
 		ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL) {
@@ -2159,6 +2218,10 @@
 			      DBUS_TYPE_INVALID);
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s) {
+		reply = wpas_dbus_error_no_p2p_mgmt_iface(message);
+		goto out;
+	}
 
 	/*
 	 * Extract the network ID and ensure the network is actually a child of
@@ -2235,6 +2298,8 @@
 	struct wpa_config *config;
 
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s)
+		return wpas_dbus_error_no_p2p_mgmt_iface(message);
 
 	config = wpa_s->conf;
 	ssid = config->ssid;
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index 0115e32..d9009ba 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -98,6 +98,7 @@
 	dbus_error_init(&error);
 	if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
 				       interface, obj_dsc->user_data, &error)) {
+		wpa_dbus_dict_close_write(&iter, &dict_iter);
 		dbus_message_unref(reply);
 		reply = wpas_dbus_reply_new_from_error(
 			message, &error, DBUS_ERROR_INVALID_ARGS,
@@ -741,7 +742,7 @@
 	DBusConnection *con = eloop_ctx;
 	struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
 
-	wpa_printf(MSG_DEBUG,
+	wpa_printf(MSG_MSGDUMP,
 		   "dbus: %s: Timeout - sending changed properties of object %s",
 		   __func__, obj_desc->path);
 	wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
@@ -930,6 +931,7 @@
 			   dbus_error_is_set(&error) ? error.name : "none",
 			   dbus_error_is_set(&error) ? error.message : "none");
 		dbus_error_free(&error);
+		wpa_dbus_dict_close_write(iter, &dict_iter);
 		return FALSE;
 	}
 
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 88cd790..db76c6a 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -111,6 +111,16 @@
 # EAP-FAST
 CONFIG_EAP_FAST=y
 
+# EAP-TEAP
+# Note: The current EAP-TEAP implementation is experimental and should not be
+# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number
+# of conflicting statements and missing details and the implementation has
+# vendor specific workarounds for those and as such, may not interoperate with
+# any other implementation. This should not be used for anything else than
+# experimentation and interoperability testing until those issues has been
+# resolved.
+#CONFIG_EAP_TEAP=y
+
 # EAP-GTC
 CONFIG_EAP_GTC=y
 
@@ -120,6 +130,9 @@
 # EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used)
 #CONFIG_EAP_SIM=y
 
+# Enable SIM simulator (Milenage) for EAP-SIM
+#CONFIG_SIM_SIMULATOR=y
+
 # EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
 #CONFIG_EAP_PSK=y
 
@@ -297,10 +310,6 @@
 # bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
 #CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
 
-# IEEE 802.11w (management frame protection), also known as PMF
-# Driver support is also needed for IEEE 802.11w.
-CONFIG_IEEE80211W=y
-
 # Support Operating Channel Validation
 #CONFIG_OCV=y
 
@@ -353,7 +362,7 @@
 #PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib"
 
 # Add support for new DBus control interface
-# (fi.w1.hostap.wpa_supplicant1)
+# (fi.w1.wpa_supplicant1)
 CONFIG_CTRL_IFACE_DBUS_NEW=y
 
 # Add introspection support for new DBus control interface
@@ -597,6 +606,9 @@
 #CONFIG_OWE=y
 
 # Device Provisioning Protocol (DPP)
-# This requires CONFIG_IEEE80211W=y to be enabled, too. (see
-# wpa_supplicant/README-DPP for details)
 CONFIG_DPP=y
+
+# WLAN Authentication and Privacy Infrastructure (WAPI): interface only.
+# Configure the building of the interface which allows WAPI configuration.
+# Note: does not configure WAPI implementation itself.
+#CONFIG_WAPI_INTERFACE=y
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 8a9e444..73649c2 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -11,6 +11,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/ip_addr.h"
 #include "common/dpp.h"
 #include "common/gas.h"
 #include "common/gas_server.h"
@@ -88,6 +89,24 @@
 }
 
 
+/**
+ * wpas_dpp_nfc_uri - Parse and add DPP bootstrapping info from NFC Tag (URI)
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @cmd: DPP URI read from a NFC Tag (URI NDEF message)
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int wpas_dpp_nfc_uri(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_add_nfc_uri(wpa_s->dpp, cmd);
+	if (!bi)
+		return -1;
+
+	return bi->id;
+}
+
+
 static void wpas_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -148,6 +167,8 @@
 static void wpas_dpp_try_to_connect(struct wpa_supplicant *wpa_s)
 {
 	wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network");
+	wpa_s->suitable_network = 0;
+	wpa_s->no_suitable_network = 0;
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
 	wpa_s->scan_runs = 0;
@@ -157,6 +178,143 @@
 }
 
 
+#ifdef CONFIG_DPP2
+
+static void wpas_dpp_conn_status_result_timeout(void *eloop_ctx,
+						void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	enum dpp_status_error result;
+
+	if (!auth || !auth->conn_status_requested)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Connection timeout - report Connection Status Result");
+	if (wpa_s->suitable_network)
+		result = DPP_STATUS_AUTH_FAILURE;
+	else if (wpa_s->no_suitable_network)
+		result = DPP_STATUS_NO_AP;
+	else
+		result = 255; /* What to report here for unexpected state? */
+	if (wpa_s->wpa_state == WPA_SCANNING)
+		wpas_abort_ongoing_scan(wpa_s);
+	wpas_dpp_send_conn_status_result(wpa_s, result);
+}
+
+
+static char * wpas_dpp_scan_channel_list(struct wpa_supplicant *wpa_s)
+{
+	char *str, *end, *pos;
+	size_t len;
+	unsigned int i;
+	u8 last_op_class = 0;
+	int res;
+
+	if (!wpa_s->last_scan_freqs || !wpa_s->num_last_scan_freqs)
+		return NULL;
+
+	len = wpa_s->num_last_scan_freqs * 8;
+	str = os_zalloc(len);
+	if (!str)
+		return NULL;
+	end = str + len;
+	pos = str;
+
+	for (i = 0; i < wpa_s->num_last_scan_freqs; i++) {
+		enum hostapd_hw_mode mode;
+		u8 op_class, channel;
+
+		mode = ieee80211_freq_to_channel_ext(wpa_s->last_scan_freqs[i],
+						     0, 0, &op_class, &channel);
+		if (mode == NUM_HOSTAPD_MODES)
+			continue;
+		if (op_class == last_op_class)
+			res = os_snprintf(pos, end - pos, ",%d", channel);
+		else
+			res = os_snprintf(pos, end - pos, "%s%d/%d",
+					  pos == str ? "" : ",",
+					  op_class, channel);
+		if (os_snprintf_error(end - pos, res)) {
+			*pos = '\0';
+			break;
+		}
+		pos += res;
+		last_op_class = op_class;
+	}
+
+	if (pos == str) {
+		os_free(str);
+		str = NULL;
+	}
+	return str;
+}
+
+
+void wpas_dpp_send_conn_status_result(struct wpa_supplicant *wpa_s,
+				      enum dpp_status_error result)
+{
+	struct wpabuf *msg;
+	const char *channel_list = NULL;
+	char *channel_list_buf = NULL;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	eloop_cancel_timeout(wpas_dpp_conn_status_result_timeout, wpa_s, NULL);
+
+	if (!auth || !auth->conn_status_requested)
+		return;
+	auth->conn_status_requested = 0;
+	wpa_printf(MSG_DEBUG, "DPP: Report connection status result %d",
+		   result);
+
+	if (result == DPP_STATUS_NO_AP) {
+		channel_list_buf = wpas_dpp_scan_channel_list(wpa_s);
+		channel_list = channel_list_buf;
+	}
+
+	msg = dpp_build_conn_status_result(auth, result,
+					   ssid ? ssid->ssid :
+					   wpa_s->dpp_last_ssid,
+					   ssid ? ssid->ssid_len :
+					   wpa_s->dpp_last_ssid_len,
+					   channel_list);
+	os_free(channel_list_buf);
+	if (!msg) {
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO,
+		DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+		DPP_PA_CONNECTION_STATUS_RESULT);
+	offchannel_send_action(wpa_s, auth->curr_freq,
+			       auth->peer_mac_addr, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       500, wpas_dpp_tx_status, 0);
+	wpabuf_free(msg);
+
+	/* This exchange will be terminated in the TX status handler */
+	auth->remove_on_tx_status = 1;
+
+	return;
+}
+
+
+void wpas_dpp_connected(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (auth && auth->conn_status_requested)
+		wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_OK);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
 static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
 			       unsigned int freq, const u8 *dst,
 			       const u8 *src, const u8 *bssid,
@@ -182,18 +340,30 @@
 
 #ifdef CONFIG_DPP2
 	if (auth->connect_on_tx_status) {
+		auth->connect_on_tx_status = 0;
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Try to connect after completed configuration result");
 		wpas_dpp_try_to_connect(wpa_s);
-		dpp_auth_deinit(wpa_s->dpp_auth);
-		wpa_s->dpp_auth = NULL;
+		if (auth->conn_status_requested) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Start 15 second timeout for reporting connection status result");
+			eloop_cancel_timeout(
+				wpas_dpp_conn_status_result_timeout,
+				wpa_s, NULL);
+			eloop_register_timeout(
+				15, 0, wpas_dpp_conn_status_result_timeout,
+				wpa_s, NULL);
+		} else {
+			dpp_auth_deinit(wpa_s->dpp_auth);
+			wpa_s->dpp_auth = NULL;
+		}
 		return;
 	}
 #endif /* CONFIG_DPP2 */
 
 	if (wpa_s->dpp_auth->remove_on_tx_status) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Terminate authentication exchange due to an earlier error");
+			   "DPP: Terminate authentication exchange due to a request to do so on TX status");
 		eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
 		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
 		eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
@@ -436,8 +606,15 @@
 {
 	const char *pos;
 	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	struct dpp_authentication *auth;
 	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	unsigned int neg_freq = 0;
+	int tcp = 0;
+#ifdef CONFIG_DPP2
+	int tcp_port = DPP_TCP_PORT;
+	struct hostapd_ip_addr ipaddr;
+	char *addr;
+#endif /* CONFIG_DPP2 */
 
 	wpa_s->dpp_gas_client = 0;
 
@@ -452,6 +629,25 @@
 		return -1;
 	}
 
+#ifdef CONFIG_DPP2
+	pos = os_strstr(cmd, " tcp_port=");
+	if (pos) {
+		pos += 10;
+		tcp_port = atoi(pos);
+	}
+
+	addr = get_param(cmd, " tcp_addr=");
+	if (addr) {
+		int res;
+
+		res = hostapd_parse_ip_addr(addr, &ipaddr);
+		os_free(addr);
+		if (res)
+			return -1;
+		tcp = 1;
+	}
+#endif /* CONFIG_DPP2 */
+
 	pos = os_strstr(cmd, " own=");
 	if (pos) {
 		pos += 5;
@@ -487,39 +683,49 @@
 	pos = os_strstr(cmd, " netrole=");
 	if (pos) {
 		pos += 9;
-		wpa_s->dpp_netrole_ap = os_strncmp(pos, "ap", 2) == 0;
+		if (os_strncmp(pos, "ap", 2) == 0)
+			wpa_s->dpp_netrole = DPP_NETROLE_AP;
+		else if (os_strncmp(pos, "configurator", 12) == 0)
+			wpa_s->dpp_netrole = DPP_NETROLE_CONFIGURATOR;
+		else
+			wpa_s->dpp_netrole = DPP_NETROLE_STA;
 	}
 
 	pos = os_strstr(cmd, " neg_freq=");
 	if (pos)
 		neg_freq = atoi(pos + 10);
 
-	if (wpa_s->dpp_auth) {
+	if (!tcp && wpa_s->dpp_auth) {
 		eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
 		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
 		eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
 				     NULL);
 		offchannel_send_action_done(wpa_s);
 		dpp_auth_deinit(wpa_s->dpp_auth);
-	}
-	wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, allowed_roles,
-					neg_freq,
-					wpa_s->hw.modes, wpa_s->hw.num_modes);
-	if (!wpa_s->dpp_auth)
-		goto fail;
-	wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
-	if (dpp_set_configurator(wpa_s->dpp, wpa_s, wpa_s->dpp_auth, cmd) < 0) {
-		dpp_auth_deinit(wpa_s->dpp_auth);
 		wpa_s->dpp_auth = NULL;
+	}
+
+	auth = dpp_auth_init(wpa_s, peer_bi, own_bi, allowed_roles, neg_freq,
+			     wpa_s->hw.modes, wpa_s->hw.num_modes);
+	if (!auth)
+		goto fail;
+	wpas_dpp_set_testing_options(wpa_s, auth);
+	if (dpp_set_configurator(wpa_s->dpp, wpa_s, auth, cmd) < 0) {
+		dpp_auth_deinit(auth);
 		goto fail;
 	}
 
-	wpa_s->dpp_auth->neg_freq = neg_freq;
+	auth->neg_freq = neg_freq;
 
 	if (!is_zero_ether_addr(peer_bi->mac_addr))
-		os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
-			  ETH_ALEN);
+		os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN);
 
+#ifdef CONFIG_DPP2
+	if (tcp)
+		return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port);
+#endif /* CONFIG_DPP2 */
+
+	wpa_s->dpp_auth = auth;
 	return wpas_dpp_auth_init_next(wpa_s);
 fail:
 	return -1;
@@ -634,7 +840,12 @@
 		wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
 			DPP_CAPAB_ENROLLEE;
 	wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
-	wpa_s->dpp_netrole_ap = os_strstr(cmd, " netrole=ap") != NULL;
+	if (os_strstr(cmd, " netrole=ap"))
+		wpa_s->dpp_netrole = DPP_NETROLE_AP;
+	else if (os_strstr(cmd, " netrole=configurator"))
+		wpa_s->dpp_netrole = DPP_NETROLE_CONFIGURATOR;
+	else
+		wpa_s->dpp_netrole = DPP_NETROLE_STA;
 	if (wpa_s->dpp_listen_freq == (unsigned int) freq) {
 		wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz",
 			   freq);
@@ -780,12 +991,13 @@
 
 
 static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s,
-					      struct dpp_authentication *auth)
+					      struct dpp_authentication *auth,
+					      struct dpp_config_obj *conf)
 {
 	struct wpa_ssid *ssid;
 
 #ifdef CONFIG_DPP2
-	if (auth->akm == DPP_AKM_SAE) {
+	if (conf->akm == DPP_AKM_SAE) {
 #ifdef CONFIG_SAE
 		struct wpa_driver_capa capa;
 		int res;
@@ -812,27 +1024,27 @@
 	wpa_config_set_network_defaults(ssid);
 	ssid->disabled = 1;
 
-	ssid->ssid = os_malloc(auth->ssid_len);
+	ssid->ssid = os_malloc(conf->ssid_len);
 	if (!ssid->ssid)
 		goto fail;
-	os_memcpy(ssid->ssid, auth->ssid, auth->ssid_len);
-	ssid->ssid_len = auth->ssid_len;
+	os_memcpy(ssid->ssid, conf->ssid, conf->ssid_len);
+	ssid->ssid_len = conf->ssid_len;
 
-	if (auth->connector) {
+	if (conf->connector) {
 		ssid->key_mgmt = WPA_KEY_MGMT_DPP;
 		ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
-		ssid->dpp_connector = os_strdup(auth->connector);
+		ssid->dpp_connector = os_strdup(conf->connector);
 		if (!ssid->dpp_connector)
 			goto fail;
 	}
 
-	if (auth->c_sign_key) {
-		ssid->dpp_csign = os_malloc(wpabuf_len(auth->c_sign_key));
+	if (conf->c_sign_key) {
+		ssid->dpp_csign = os_malloc(wpabuf_len(conf->c_sign_key));
 		if (!ssid->dpp_csign)
 			goto fail;
-		os_memcpy(ssid->dpp_csign, wpabuf_head(auth->c_sign_key),
-			  wpabuf_len(auth->c_sign_key));
-		ssid->dpp_csign_len = wpabuf_len(auth->c_sign_key);
+		os_memcpy(ssid->dpp_csign, wpabuf_head(conf->c_sign_key),
+			  wpabuf_len(conf->c_sign_key));
+		ssid->dpp_csign_len = wpabuf_len(conf->c_sign_key);
 	}
 
 	if (auth->net_access_key) {
@@ -847,29 +1059,32 @@
 		ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry;
 	}
 
-	if (!auth->connector || dpp_akm_psk(auth->akm) ||
-	    dpp_akm_sae(auth->akm)) {
-		if (!auth->connector)
+	if (!conf->connector || dpp_akm_psk(conf->akm) ||
+	    dpp_akm_sae(conf->akm)) {
+		if (!conf->connector)
 			ssid->key_mgmt = 0;
-		if (dpp_akm_psk(auth->akm))
+		if (dpp_akm_psk(conf->akm))
 			ssid->key_mgmt |= WPA_KEY_MGMT_PSK |
 				WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_FT_PSK;
-		if (dpp_akm_sae(auth->akm))
+		if (dpp_akm_sae(conf->akm))
 			ssid->key_mgmt |= WPA_KEY_MGMT_SAE |
 				WPA_KEY_MGMT_FT_SAE;
 		ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
-		if (auth->passphrase[0]) {
+		if (conf->passphrase[0]) {
 			if (wpa_config_set_quoted(ssid, "psk",
-						  auth->passphrase) < 0)
+						  conf->passphrase) < 0)
 				goto fail;
 			wpa_config_update_psk(ssid);
 			ssid->export_keys = 1;
 		} else {
-			ssid->psk_set = auth->psk_set;
-			os_memcpy(ssid->psk, auth->psk, PMK_LEN);
+			ssid->psk_set = conf->psk_set;
+			os_memcpy(ssid->psk, conf->psk, PMK_LEN);
 		}
 	}
 
+	os_memcpy(wpa_s->dpp_last_ssid, conf->ssid, conf->ssid_len);
+	wpa_s->dpp_last_ssid_len = conf->ssid_len;
+
 	return ssid;
 fail:
 	wpas_notify_network_removed(wpa_s, ssid);
@@ -879,14 +1094,15 @@
 
 
 static int wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
-				   struct dpp_authentication *auth)
+				   struct dpp_authentication *auth,
+				   struct dpp_config_obj *conf)
 {
 	struct wpa_ssid *ssid;
 
 	if (wpa_s->conf->dpp_config_processing < 1)
 		return 0;
 
-	ssid = wpas_dpp_add_network(wpa_s, auth);
+	ssid = wpas_dpp_add_network(wpa_s, auth, conf);
 	if (!ssid)
 		return -1;
 
@@ -903,49 +1119,59 @@
 		wpa_printf(MSG_DEBUG, "DPP: Failed to update configuration");
 #endif /* CONFIG_NO_CONFIG_WRITE */
 
+	return 0;
+}
+
+
+static void wpas_dpp_post_process_config(struct wpa_supplicant *wpa_s,
+					 struct dpp_authentication *auth)
+{
 	if (wpa_s->conf->dpp_config_processing < 2)
-		return 0;
+		return;
 
 #ifdef CONFIG_DPP2
 	if (auth->peer_version >= 2) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Postpone connection attempt to wait for completion of DPP Configuration Result");
 		auth->connect_on_tx_status = 1;
-		return 0;
+		return;
 	}
 #endif /* CONFIG_DPP2 */
 
 	wpas_dpp_try_to_connect(wpa_s);
-	return 0;
 }
 
 
 static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
-				      struct dpp_authentication *auth)
+				      struct dpp_authentication *auth,
+				      struct dpp_config_obj *conf)
 {
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
-	if (auth->ssid_len)
+	if (conf->ssid_len)
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
-			wpa_ssid_txt(auth->ssid, auth->ssid_len));
-	if (auth->connector) {
+			wpa_ssid_txt(conf->ssid, conf->ssid_len));
+	if (conf->ssid_charset)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_SSID_CHARSET "%d",
+			conf->ssid_charset);
+	if (conf->connector) {
 		/* TODO: Save the Connector and consider using a command
 		 * to fetch the value instead of sending an event with
 		 * it. The Connector could end up being larger than what
 		 * most clients are ready to receive as an event
 		 * message. */
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
-			auth->connector);
+			conf->connector);
 	}
-	if (auth->c_sign_key) {
+	if (conf->c_sign_key) {
 		char *hex;
 		size_t hexlen;
 
-		hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+		hexlen = 2 * wpabuf_len(conf->c_sign_key) + 1;
 		hex = os_malloc(hexlen);
 		if (hex) {
 			wpa_snprintf_hex(hex, hexlen,
-					 wpabuf_head(auth->c_sign_key),
-					 wpabuf_len(auth->c_sign_key));
+					 wpabuf_head(conf->c_sign_key),
+					 wpabuf_len(conf->c_sign_key));
 			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_C_SIGN_KEY "%s",
 				hex);
 			os_free(hex);
@@ -973,7 +1199,7 @@
 		}
 	}
 
-	return wpas_dpp_process_config(wpa_s, auth);
+	return wpas_dpp_process_config(wpa_s, auth, conf);
 }
 
 
@@ -987,6 +1213,7 @@
 	struct dpp_authentication *auth = wpa_s->dpp_auth;
 	int res;
 	enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
+	unsigned int i;
 
 	wpa_s->dpp_gas_dialog_token = -1;
 
@@ -1024,9 +1251,14 @@
 		goto fail;
 	}
 
-	res = wpas_dpp_handle_config_obj(wpa_s, auth);
-	if (res < 0)
-		goto fail;
+	for (i = 0; i < auth->num_conf_obj; i++) {
+		res = wpas_dpp_handle_config_obj(wpa_s, auth,
+						 &auth->conf_obj[i]);
+		if (res < 0)
+			goto fail;
+	}
+	if (auth->num_conf_obj)
+		wpas_dpp_post_process_config(wpa_s, auth);
 
 	status = DPP_STATUS_OK;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1075,27 +1307,19 @@
 {
 	struct dpp_authentication *auth = wpa_s->dpp_auth;
 	struct wpabuf *buf;
-	char json[100];
 	int res;
+	int *supp_op_classes;
 
 	wpa_s->dpp_gas_client = 1;
-	os_snprintf(json, sizeof(json),
-		    "{\"name\":\"Test\","
-		    "\"wi-fi_tech\":\"infra\","
-		    "\"netRole\":\"%s\"}",
-		    wpa_s->dpp_netrole_ap ? "ap" : "sta");
-#ifdef CONFIG_TESTING_OPTIONS
-	if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) {
-		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr");
-		json[29] = 'k'; /* replace "infra" with "knfra" */
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-	wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
-
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
 
-	buf = dpp_build_conf_req(auth, json);
+	supp_op_classes = wpas_supp_op_classes(wpa_s);
+	buf = dpp_build_conf_req_helper(auth, wpa_s->conf->dpp_name,
+					wpa_s->dpp_netrole,
+					wpa_s->conf->dpp_mud_url,
+					supp_op_classes);
+	os_free(supp_op_classes);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
@@ -1244,6 +1468,26 @@
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Timeout while waiting for Configuration Result");
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	wpas_notify_dpp_configuration_failure(wpa_s);
+	dpp_auth_deinit(auth);
+	wpa_s->dpp_auth = NULL;
+}
+
+
+static void wpas_dpp_conn_status_result_wait_timeout(void *eloop_ctx,
+						     void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth || !auth->waiting_conn_status_result)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Timeout while waiting for Connection Status Result");
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT "timeout");
+	wpas_notify_dpp_timeout(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
 	dpp_auth_deinit(auth);
 	wpa_s->dpp_auth = NULL;
 }
@@ -1272,17 +1516,95 @@
 
 	status = dpp_conf_result_rx(auth, hdr, buf, len);
 
+	if (status == DPP_STATUS_OK && auth->send_conn_status) {
+		wpa_msg(wpa_s, MSG_INFO,
+			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+		wpas_notify_dpp_config_accepted(wpa_s);
+		eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout,
+				     wpa_s, NULL);
+		auth->waiting_conn_status_result = 1;
+		eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
+				     wpa_s, NULL);
+		eloop_register_timeout(16, 0,
+				       wpas_dpp_conn_status_result_wait_timeout,
+				       wpa_s, NULL);
+		offchannel_send_action_done(wpa_s);
+		wpas_dpp_listen_start(wpa_s, auth->neg_freq ? auth->neg_freq :
+				      auth->curr_freq);
+		return;
+	}
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
-	if (status == DPP_STATUS_OK)
+	if (status == DPP_STATUS_OK) {
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
-	else
+		wpas_notify_dpp_config_sent(wpa_s);
+	}
+	else {
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+		wpas_notify_dpp_config_rejected(wpa_s);
+	}
 	dpp_auth_deinit(auth);
 	wpa_s->dpp_auth = NULL;
 	eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
 }
 
+
+static void wpas_dpp_rx_conn_status_result(struct wpa_supplicant *wpa_s,
+					   const u8 *src, const u8 *hdr,
+					   const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	enum dpp_status_error status;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len = 0;
+	char *channel_list = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+	if (!auth || !auth->waiting_conn_status_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for connection status result - drop");
+		return;
+	}
+
+	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+					   ssid, &ssid_len, &channel_list);
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
+		"result=%d ssid=%s channel_list=%s",
+		status, wpa_ssid_txt(ssid, ssid_len),
+		channel_list ? channel_list : "N/A");
+	wpas_notify_dpp_conn_status(wpa_s, status, wpa_ssid_txt(ssid, ssid_len),
+			channel_list, auth->band_list, auth->band_list_size);
+	os_free(channel_list);
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
+	dpp_auth_deinit(auth);
+	wpa_s->dpp_auth = NULL;
+	eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
+			     wpa_s, NULL);
+}
+
+
+static int wpas_dpp_process_conf_obj(void *ctx,
+				     struct dpp_authentication *auth)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	unsigned int i;
+	int res = -1;
+
+	for (i = 0; i < auth->num_conf_obj; i++) {
+		res = wpas_dpp_handle_config_obj(wpa_s, auth,
+						 &auth->conf_obj[i]);
+		if (res)
+			break;
+	}
+	if (!res)
+		wpas_dpp_post_process_config(wpa_s, auth);
+
+	return res;
+}
+
 #endif /* CONFIG_DPP2 */
 
 
@@ -1353,6 +1675,9 @@
 			   status[0]);
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
 			" status=%u", MAC2STR(src), status[0]);
+#ifdef CONFIG_DPP2
+		wpas_dpp_send_conn_status_result(wpa_s, status[0]);
+#endif /* CONFIG_DPP2 */
 		goto fail;
 	}
 
@@ -1376,6 +1701,9 @@
 			   "DPP: Network Introduction protocol resulted in failure");
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
 			" fail=peer_connector_validation_failed", MAC2STR(src));
+#ifdef CONFIG_DPP2
+		wpas_dpp_send_conn_status_result(wpa_s, res);
+#endif /* CONFIG_DPP2 */
 		goto fail;
 	}
 
@@ -1818,6 +2146,9 @@
 	case DPP_PA_CONFIGURATION_RESULT:
 		wpas_dpp_rx_conf_result(wpa_s, src, hdr, buf, len);
 		break;
+	case DPP_PA_CONNECTION_STATUS_RESULT:
+		wpas_dpp_rx_conn_status_result(wpa_s, src, hdr, buf, len);
+		break;
 #endif /* CONFIG_DPP2 */
 	default:
 		wpa_printf(MSG_DEBUG,
@@ -1853,6 +2184,18 @@
 		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
 		return NULL;
 	}
+
+	if (wpa_s->dpp_auth_ok_on_ack && auth->configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Have not received ACK for Auth Confirm yet - assume it was received based on this GAS request");
+		/* wpas_dpp_auth_success() would normally have been called from
+		 * TX status handler, but since there was no such handler call
+		 * yet, simply send out the event message and proceed with
+		 * exchange. */
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1");
+		wpa_s->dpp_auth_ok_on_ack = 0;
+	}
+
 	wpa_hexdump(MSG_DEBUG,
 		    "DPP: Received Configuration Request (GAS Query Request)",
 		    query, query_len);
@@ -1894,6 +2237,7 @@
 	if (ok && auth->peer_version >= 2 &&
 	    auth->conf_resp_status == DPP_STATUS_OK) {
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+		wpas_notify_dpp_config_sent_wait_response(wpa_s);
 		auth->waiting_conf_result = 1;
 		auth->conf_resp = NULL;
 		wpabuf_free(resp);
@@ -1935,7 +2279,10 @@
 	wpas_dpp_set_testing_options(wpa_s, auth);
 	if (dpp_set_configurator(wpa_s->dpp, wpa_s, auth, cmd) == 0 &&
 	    dpp_configurator_own_config(auth, curve, 0) == 0)
-		ret = wpas_dpp_handle_config_obj(wpa_s, auth);
+		ret = wpas_dpp_handle_config_obj(wpa_s, auth,
+						 &auth->conf_obj[0]);
+	if (!ret)
+		wpas_dpp_post_process_config(wpa_s, auth);
 
 	dpp_auth_deinit(auth);
 	os_free(curve);
@@ -2213,6 +2560,7 @@
 
 int wpas_dpp_init(struct wpa_supplicant *wpa_s)
 {
+	struct dpp_global_config config;
 	u8 adv_proto_id[7];
 
 	adv_proto_id[0] = WLAN_EID_VENDOR_SPECIFIC;
@@ -2225,7 +2573,14 @@
 				sizeof(adv_proto_id), wpas_dpp_gas_req_handler,
 				wpas_dpp_gas_status_handler, wpa_s) < 0)
 		return -1;
-	wpa_s->dpp = dpp_global_init();
+
+	os_memset(&config, 0, sizeof(config));
+	config.msg_ctx = wpa_s;
+	config.cb_ctx = wpa_s;
+#ifdef CONFIG_DPP2
+	config.process_conf_obj = wpas_dpp_process_conf_obj;
+#endif /* CONFIG_DPP2 */
+	wpa_s->dpp = dpp_global_init(&config);
 	return wpa_s->dpp ? 0 : -1;
 }
 
@@ -2250,6 +2605,9 @@
 	eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL);
 #ifdef CONFIG_DPP2
 	eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
+			     wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_conn_status_result_timeout, wpa_s, NULL);
 	dpp_pfs_free(wpa_s->dpp_pfs);
 	wpa_s->dpp_pfs = NULL;
 #endif /* CONFIG_DPP2 */
@@ -2261,3 +2619,23 @@
 	os_free(wpa_s->dpp_configurator_params);
 	wpa_s->dpp_configurator_params = NULL;
 }
+
+
+#ifdef CONFIG_DPP2
+int wpas_dpp_controller_start(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct dpp_controller_config config;
+	const char *pos;
+
+	os_memset(&config, 0, sizeof(config));
+	if (cmd) {
+		pos = os_strstr(cmd, " tcp_port=");
+		if (pos) {
+			pos += 10;
+			config.tcp_port = atoi(pos);
+		}
+	}
+	config.configurator_params = wpa_s->dpp_configurator_params;
+	return dpp_controller_start(wpa_s->dpp, &config);
+}
+#endif /* CONFIG_DPP2 */
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
index ecb7a7d..607036a 100644
--- a/wpa_supplicant/dpp_supplicant.h
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -1,6 +1,7 @@
 /*
  * wpa_supplicant - DPP
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,7 +10,10 @@
 #ifndef DPP_SUPPLICANT_H
 #define DPP_SUPPLICANT_H
 
+enum dpp_status_error;
+
 int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_nfc_uri(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd);
 void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s);
@@ -25,5 +29,9 @@
 void wpas_dpp_deinit(struct wpa_supplicant *wpa_s);
 int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			   struct wpa_bss *bss);
+int wpas_dpp_controller_start(struct wpa_supplicant *wpa_s, const char *cmd);
+void wpas_dpp_connected(struct wpa_supplicant *wpa_s);
+void wpas_dpp_send_conn_status_result(struct wpa_supplicant *wpa_s,
+				      enum dpp_status_error result);
 
 #endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 4a9f472..cf9972a 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -87,6 +87,16 @@
 	return -1;
 }
 
+static inline int wpa_drv_mesh_link_probe(struct wpa_supplicant *wpa_s,
+					  const u8 *addr,
+					  const u8 *eth, size_t len)
+{
+	if (wpa_s->driver->probe_mesh_link)
+		return wpa_s->driver->probe_mesh_link(wpa_s->drv_priv, addr,
+						      eth, len);
+	return -1;
+}
+
 static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
 			       struct wpa_driver_scan_params *params)
 {
@@ -168,7 +178,7 @@
 }
 
 static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
-				     const u8 *addr, int reason_code)
+				     const u8 *addr, u16 reason_code)
 {
 	if (wpa_s->driver->sta_deauth) {
 		return wpa_s->driver->sta_deauth(wpa_s->drv_priv,
@@ -179,7 +189,7 @@
 }
 
 static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
-					 const u8 *addr, int reason_code)
+					 const u8 *addr, u16 reason_code)
 {
 	if (wpa_s->driver->deauthenticate) {
 		return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr,
diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c
index ece5716..3f018c4 100644
--- a/wpa_supplicant/eap_register.c
+++ b/wpa_supplicant/eap_register.c
@@ -102,6 +102,11 @@
 		ret = eap_peer_fast_register();
 #endif /* EAP_FAST */
 
+#ifdef EAP_TEAP
+	if (ret == 0)
+		ret = eap_peer_teap_register();
+#endif /* EAP_TEAP */
+
 #ifdef EAP_PAX
 	if (ret == 0)
 		ret = eap_peer_pax_register();
@@ -237,6 +242,11 @@
 		ret = eap_server_fast_register();
 #endif /* EAP_SERVER_FAST */
 
+#ifdef EAP_SERVER_TEAP
+	if (ret == 0)
+		ret = eap_server_teap_register();
+#endif /* EAP_SERVER_TEAP */
+
 #ifdef EAP_SERVER_WSC
 	if (ret == 0)
 		ret = eap_server_wsc_register();
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 3fd4ce6..53d7585 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -15,6 +15,7 @@
 #include "common.h"
 #include "utils/ext_password.h"
 #include "common/version.h"
+#include "crypto/tls.h"
 #include "config.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "eap_peer/eap.h"
@@ -438,7 +439,7 @@
 static void eapol_test_write_cert(FILE *f, const char *subject,
 				  const struct wpabuf *cert)
 {
-	unsigned char *encoded;
+	char *encoded;
 
 	encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
 	if (encoded == NULL)
@@ -497,45 +498,40 @@
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
 
-static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
-			       const char *altsubject[], int num_altsubject,
-			       const char *cert_hash,
-			       const struct wpabuf *cert)
+static void eapol_test_cert_cb(void *ctx, struct tls_cert_data *cert,
+			       const char *cert_hash)
 {
 	struct eapol_test_data *e = ctx;
+	int i;
 
 	wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
 		"depth=%d subject='%s'%s%s",
-		depth, subject,
+		cert->depth, cert->subject,
 		cert_hash ? " hash=" : "",
 		cert_hash ? cert_hash : "");
 
-	if (cert) {
+	if (cert->cert) {
 		char *cert_hex;
-		size_t len = wpabuf_len(cert) * 2 + 1;
+		size_t len = wpabuf_len(cert->cert) * 2 + 1;
 		cert_hex = os_malloc(len);
 		if (cert_hex) {
-			wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
-					 wpabuf_len(cert));
+			wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert->cert),
+					 wpabuf_len(cert->cert));
 			wpa_msg_ctrl(e->wpa_s, MSG_INFO,
 				     WPA_EVENT_EAP_PEER_CERT
 				     "depth=%d subject='%s' cert=%s",
-				     depth, subject, cert_hex);
+				     cert->depth, cert->subject, cert_hex);
 			os_free(cert_hex);
 		}
 
 		if (e->server_cert_file)
 			eapol_test_write_cert(e->server_cert_file,
-					      subject, cert);
+					      cert->subject, cert->cert);
 	}
 
-	if (altsubject) {
-		int i;
-
-		for (i = 0; i < num_altsubject; i++)
-			wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
-				"depth=%d %s", depth, altsubject[i]);
-	}
+	for (i = 0; i < cert->num_altsubject; i++)
+		wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+			"depth=%d %s", cert->depth, cert->altsubject[i]);
 }
 
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 815b994..4c459cf 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -316,8 +316,9 @@
 	eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
-	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)
+	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP || wpa_s->drv_authorized_port)
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+	wpa_s->drv_authorized_port = 0;
 	wpa_s->ap_ies_from_associnfo = 0;
 	wpa_s->current_ssid = NULL;
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
@@ -326,6 +327,7 @@
 	wpas_rrm_reset(wpa_s);
 	wpa_s->wnmsleep_used = 0;
 	wnm_clear_coloc_intf_reporting(wpa_s);
+	wpa_s->disable_mbo_oce = 0;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	wpa_s->last_tk_alg = WPA_ALG_NONE;
@@ -611,7 +613,6 @@
 			break;
 		}
 
-#ifdef CONFIG_IEEE80211W
 		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
 		    wpas_get_ssid_pmf(wpa_s, ssid) ==
 		    MGMT_FRAME_PROTECTION_REQUIRED) {
@@ -620,7 +621,6 @@
 					"   skip RSN IE - no mgmt frame protection");
 			break;
 		}
-#endif /* CONFIG_IEEE80211W */
 		if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
 		    wpas_get_ssid_pmf(wpa_s, ssid) ==
 		    NO_MGMT_FRAME_PROTECTION) {
@@ -629,17 +629,6 @@
 					"   skip RSN IE - no mgmt frame protection enabled but AP requires it");
 			break;
 		}
-#ifdef CONFIG_MBO
-		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
-		    wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) &&
-		    wpas_get_ssid_pmf(wpa_s, ssid) !=
-		    NO_MGMT_FRAME_PROTECTION) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip RSN IE - no mgmt frame protection enabled on MBO AP");
-			break;
-		}
-#endif /* CONFIG_MBO */
 
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG,
@@ -647,7 +636,6 @@
 		return 1;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED &&
 	    (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) || ssid->owe_only)) {
 		if (debug_print)
@@ -655,7 +643,6 @@
 				"   skip - MFP Required but network not MFP Capable");
 		return 0;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
@@ -863,6 +850,26 @@
 				continue;
 			}
 
+#ifdef CONFIG_SAE
+			if (flagged && ((rate_ie[j] & 0x7f) ==
+					BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
+				if (wpa_s->conf->sae_pwe == 0) {
+					if (debug_print)
+						wpa_dbg(wpa_s, MSG_DEBUG,
+							"   SAE H2E disabled");
+#ifdef CONFIG_TESTING_OPTIONS
+					if (wpa_s->ignore_sae_h2e_only) {
+						wpa_dbg(wpa_s, MSG_DEBUG,
+							"TESTING: Ignore SAE H2E requirement mismatch");
+						continue;
+					}
+#endif /* CONFIG_TESTING_OPTIONS */
+					return 0;
+				}
+				continue;
+			}
+#endif /* CONFIG_SAE */
+
 			if (!flagged)
 				continue;
 
@@ -1222,7 +1229,7 @@
 			continue;
 		}
 
-		if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) &&
+		if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) &&
 		    !bss_is_pbss(bss)) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1246,7 +1253,7 @@
 		}
 
 #ifdef CONFIG_MESH
-		if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 &&
+		if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 &&
 		    ssid->frequency != bss->freq) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1262,6 +1269,19 @@
 			continue;
 		}
 
+#ifdef CONFIG_SAE
+		if (wpa_s->conf->sae_pwe == 1 &&
+		    wpa_key_mgmt_sae(ssid->key_mgmt) &&
+		    (!(ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX)) ||
+		     ie[1] < 1 ||
+		     !(ie[2] & BIT(WLAN_RSNX_CAPAB_SAE_H2E)))) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - SAE H2E required, but not supported by the AP");
+			continue;
+		}
+#endif /* CONFIG_SAE */
+
 #ifndef CONFIG_IBSS_RSN
 		if (ssid->mode == WPAS_MODE_IBSS &&
 		    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
@@ -1615,9 +1635,9 @@
 				continue;
 			}
 #endif /* !CONFIG_IBSS_RSN */
-			if (ssid->mode == IEEE80211_MODE_IBSS ||
-			    ssid->mode == IEEE80211_MODE_AP ||
-			    ssid->mode == IEEE80211_MODE_MESH)
+			if (ssid->mode == WPAS_MODE_IBSS ||
+			    ssid->mode == WPAS_MODE_AP ||
+			    ssid->mode == WPAS_MODE_MESH)
 				return ssid;
 		}
 	}
@@ -1901,7 +1921,7 @@
 	if (wnm_scan_process(wpa_s, 1) > 0)
 		goto scan_work_done;
 
-	if (sme_proc_obss_scan(wpa_s) > 0)
+	if (sme_proc_obss_scan(wpa_s, scan_res) > 0)
 		goto scan_work_done;
 
 	if (own_request && data &&
@@ -1937,6 +1957,21 @@
 		radio_work_done(work);
 	}
 
+	os_free(wpa_s->last_scan_freqs);
+	wpa_s->last_scan_freqs = NULL;
+	wpa_s->num_last_scan_freqs = 0;
+	if (own_request && data &&
+	    data->scan_info.freqs && data->scan_info.num_freqs) {
+		wpa_s->last_scan_freqs = os_malloc(sizeof(int) *
+						   data->scan_info.num_freqs);
+		if (wpa_s->last_scan_freqs) {
+			os_memcpy(wpa_s->last_scan_freqs,
+				  data->scan_info.freqs,
+				  sizeof(int) * data->scan_info.num_freqs);
+			wpa_s->num_last_scan_freqs = data->scan_info.num_freqs;
+		}
+	}
+
 	return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
 
 scan_work_done:
@@ -1990,6 +2025,8 @@
 			return 0;
 		}
 
+		wpa_s->suitable_network++;
+
 		if (ssid != wpa_s->current_ssid &&
 		    wpa_s->wpa_state >= WPA_AUTHENTICATING) {
 			wpa_s->own_disconnect_req = 1;
@@ -2010,6 +2047,7 @@
 		 */
 		return 1;
 	} else {
+		wpa_s->no_suitable_network++;
 		wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
 		ssid = wpa_supplicant_pick_new_network(wpa_s);
 		if (ssid) {
@@ -2380,7 +2418,7 @@
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 					  union wpa_event_data *data)
 {
-	int l, len, found = 0, wpa_found, rsn_found;
+	int l, len, found = 0, found_x = 0, wpa_found, rsn_found;
 	const u8 *p;
 #if defined(CONFIG_IEEE80211R) || defined(CONFIG_OWE)
 	u8 bssid[ETH_ALEN];
@@ -2438,6 +2476,30 @@
 				resp_elems.vht_capabilities;
 			wpa_s->connection_he = req_elems.he_capabilities &&
 				resp_elems.he_capabilities;
+
+			int max_nss_rx_req = get_max_nss_capability(&req_elems, 1);
+			int max_nss_rx_resp = get_max_nss_capability(&resp_elems, 1);
+			wpa_s->connection_max_nss_rx = (max_nss_rx_resp > max_nss_rx_req) ?
+				max_nss_rx_req : max_nss_rx_resp;
+			int max_nss_tx_req = get_max_nss_capability(&req_elems, 0);
+			int max_nss_tx_resp = get_max_nss_capability(&resp_elems, 0);
+			wpa_s->connection_max_nss_tx = (max_nss_tx_resp > max_nss_tx_req) ?
+				max_nss_tx_req : max_nss_tx_resp;
+
+			struct supported_chan_width sta_supported_chan_width =
+				get_supported_channel_width(&req_elems);
+			enum chan_width ap_operation_chan_width =
+				get_operation_channel_width(&resp_elems);
+			if (wpa_s->connection_vht || wpa_s->connection_he) {
+				wpa_s->connection_channel_bandwidth =
+					get_sta_operation_chan_width(ap_operation_chan_width,
+					sta_supported_chan_width);
+			} else if (wpa_s->connection_ht) {
+				wpa_s->connection_channel_bandwidth = (ap_operation_chan_width
+					== CHAN_WIDTH_40) ? CHAN_WIDTH_40 : CHAN_WIDTH_20;
+			} else {
+				wpa_s->connection_channel_bandwidth = CHAN_WIDTH_20;
+			}
 		}
 	}
 
@@ -2452,22 +2514,29 @@
 				    p, l);
 			break;
 		}
-		if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
-		     (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
-		    (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
-		     (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
-		    (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
+		if (!found &&
+		    ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+		      (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+		     (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
+		      (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
+		     (p[0] == WLAN_EID_RSN && p[1] >= 2))) {
 			if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
 				break;
 			found = 1;
 			wpa_find_assoc_pmkid(wpa_s);
-			break;
+		}
+		if (!found_x && p[0] == WLAN_EID_RSNX) {
+			if (wpa_sm_set_assoc_rsnxe(wpa_s->wpa, p, len))
+				break;
+			found_x = 1;
 		}
 		l -= len;
 		p += len;
 	}
 	if (!found && data->assoc_info.req_ies)
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+	if (!found_x && data->assoc_info.req_ies)
+		wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
 
 #ifdef CONFIG_FILS
 #ifdef CONFIG_SME
@@ -2629,14 +2698,19 @@
 			wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
 		}
 
+		if (p[0] == WLAN_EID_RSNX && p[1] >= 1)
+			wpa_sm_set_ap_rsnxe(wpa_s->wpa, p, len);
+
 		l -= len;
 		p += len;
 	}
 
 	if (!wpa_found && data->assoc_info.beacon_ies)
 		wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
-	if (!rsn_found && data->assoc_info.beacon_ies)
+	if (!rsn_found && data->assoc_info.beacon_ies) {
 		wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+		wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0);
+	}
 	if (wpa_found || rsn_found)
 		wpa_s->ap_ies_from_associnfo = 1;
 
@@ -2656,7 +2730,7 @@
 
 static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
 {
-	const u8 *bss_wpa = NULL, *bss_rsn = NULL;
+	const u8 *bss_wpa = NULL, *bss_rsn = NULL, *bss_rsnx = NULL;
 
 	if (!wpa_s->current_bss || !wpa_s->current_ssid)
 		return -1;
@@ -2667,11 +2741,14 @@
 	bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
 					WPA_IE_VENDOR_TYPE);
 	bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
+	bss_rsnx = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSNX);
 
 	if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
 				 bss_wpa ? 2 + bss_wpa[1] : 0) ||
 	    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
-				 bss_rsn ? 2 + bss_rsn[1] : 0))
+				 bss_rsn ? 2 + bss_rsn[1] : 0) ||
+	    wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx,
+				 bss_rsnx ? 2 + bss_rsnx[1] : 0))
 		return -1;
 
 	return 0;
@@ -2723,6 +2800,9 @@
 	u8 bssid[ETH_ALEN];
 	int ft_completed, already_authorized;
 	int new_bss = 0;
+#if defined(CONFIG_FILS) || defined(CONFIG_MBO)
+	struct wpa_bss *bss;
+#endif /* CONFIG_FILS || CONFIG_MBO */
 
 #ifdef CONFIG_AP
 	if (wpa_s->ap_iface) {
@@ -2831,7 +2911,7 @@
 	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed ||
-	    already_authorized)
+	    already_authorized || wpa_s->drv_authorized_port)
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 	/* 802.1X::portControl = Auto */
 	eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
@@ -2839,7 +2919,7 @@
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
 	    (wpa_s->current_ssid &&
-	     wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
+	     wpa_s->current_ssid->mode == WPAS_MODE_IBSS)) {
 		if (wpa_s->current_ssid &&
 		    wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
 		    (wpa_s->drv_flags &
@@ -2890,6 +2970,16 @@
 
 	wpa_s->last_eapol_matches_bssid = 0;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->rsnxe_override_eapol) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: RSNXE EAPOL-Key msg 2/4 override");
+		wpa_sm_set_assoc_rsnxe(wpa_s->wpa,
+				       wpabuf_head(wpa_s->rsnxe_override_eapol),
+				       wpabuf_len(wpa_s->rsnxe_override_eapol));
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	if (wpa_s->pending_eapol_rx) {
 		struct os_reltime now, age;
 		os_get_reltime(&now);
@@ -2946,15 +3036,21 @@
 			wmm_ac_restore_tspecs(wpa_s);
 	}
 
+#if defined(CONFIG_FILS) || defined(CONFIG_MBO)
+	bss = wpa_bss_get_bssid(wpa_s, bssid);
+#endif /* CONFIG_FILS || CONFIG_MBO */
 #ifdef CONFIG_FILS
 	if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) {
-		struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, bssid);
 		const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
 
 		if (fils_cache_id)
 			wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id);
 	}
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_MBO
+	wpas_mbo_check_pmf(wpa_s, bss, wpa_s->current_ssid);
+#endif /* CONFIG_MBO */
 }
 
 
@@ -2999,8 +3095,10 @@
 				 int locally_generated)
 {
 	if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE ||
-	    !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
-		return 0; /* Not in 4-way handshake with PSK */
+	    !wpa_s->new_connection ||
+	    !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+	    wpa_key_mgmt_sae(wpa_s->key_mgmt))
+		return 0; /* Not in initial 4-way handshake with PSK */
 
 	/*
 	 * It looks like connection was lost while trying to go through PSK
@@ -3062,6 +3160,10 @@
 		if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
 			return; /* P2P group removed */
 		wpas_auth_failed(wpa_s, "WRONG_KEY");
+#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 ||
@@ -3510,26 +3612,22 @@
 static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s,
 					       struct unprot_deauth *e)
 {
-#ifdef CONFIG_IEEE80211W
 	wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame "
 		   "dropped: " MACSTR " -> " MACSTR
 		   " (reason code %u)",
 		   MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
 	sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
-#endif /* CONFIG_IEEE80211W */
 }
 
 
 static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
 						 struct unprot_disassoc *e)
 {
-#ifdef CONFIG_IEEE80211W
 	wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame "
 		   "dropped: " MACSTR " -> " MACSTR
 		   " (reason code %u)",
 		   MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
 	sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
-#endif /* CONFIG_IEEE80211W */
 }
 
 
@@ -3596,8 +3694,9 @@
 		ie_len = info->ie_len;
 		reason_code = info->reason_code;
 		locally_generated = info->locally_generated;
-		wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code,
-			locally_generated ? " (locally generated)" : "");
+		wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s", reason_code,
+			reason2str(reason_code),
+			locally_generated ? " locally_generated=1" : "");
 		if (addr)
 			wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
 				MAC2STR(addr));
@@ -3650,9 +3749,9 @@
 		ie_len = info->ie_len;
 		reason_code = info->reason_code;
 		locally_generated = info->locally_generated;
-		wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
-			reason_code,
-			locally_generated ? " (locally generated)" : "");
+		wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s",
+			reason_code, reason2str(reason_code),
+			locally_generated ? " locally_generated=1" : "");
 		if (addr) {
 			wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
 				MAC2STR(addr));
@@ -3786,14 +3885,12 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef CONFIG_IEEE80211W
 #ifdef CONFIG_SME
 	if (category == WLAN_ACTION_SA_QUERY) {
 		sme_sa_query_rx(wpa_s, mgmt->sa, payload, plen);
 		return;
 	}
 #endif /* CONFIG_SME */
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_WNM
 	if (mgmt->u.action.category == WLAN_ACTION_WNM) {
@@ -3934,6 +4031,7 @@
 		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
 		eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
 		eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+		wpa_s->drv_authorized_port = 1;
 	}
 }
 
@@ -4058,9 +4156,18 @@
 				    union wpa_event_data *data)
 {
 	const u8 *bssid = data->assoc_reject.bssid;
+#ifdef CONFIG_MBO
+	struct wpa_bss *reject_bss;
+#endif /* CONFIG_MBO */
 
 	if (!bssid || is_zero_ether_addr(bssid))
 		bssid = wpa_s->pending_bssid;
+#ifdef CONFIG_MBO
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+		reject_bss = wpa_s->current_bss;
+	else
+		reject_bss = wpa_bss_get_bssid(wpa_s, bssid);
+#endif /* CONFIG_MBO */
 
 	if (data->assoc_reject.bssid)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
@@ -4112,8 +4219,7 @@
 #ifdef CONFIG_MBO
 	if (data->assoc_reject.status_code ==
 	    WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
-	    wpa_s->current_bss && data->assoc_reject.bssid &&
-	    data->assoc_reject.resp_ies) {
+	    reject_bss && data->assoc_reject.resp_ies) {
 		const u8 *rssi_rej;
 
 		rssi_rej = mbo_get_attr_from_ies(
@@ -4124,13 +4230,12 @@
 			wpa_printf(MSG_DEBUG,
 				   "OCE: RSSI-based association rejection from "
 				   MACSTR " (Delta RSSI: %u, Retry Delay: %u)",
-				   MAC2STR(data->assoc_reject.bssid),
+				   MAC2STR(reject_bss->bssid),
 				   rssi_rej[2], rssi_rej[3]);
 			wpa_bss_tmp_disallow(wpa_s,
-					     data->assoc_reject.bssid,
+					     reject_bss->bssid,
 					     rssi_rej[3],
-					     rssi_rej[2] +
-					     wpa_s->current_bss->level);
+					     rssi_rej[2] + reject_bss->level);
 		}
 	}
 #endif /* CONFIG_MBO */
@@ -4462,18 +4567,24 @@
 				       data->rx_from_unknown.wds);
 		break;
 #endif /* CONFIG_AP */
+
+	case EVENT_CH_SWITCH_STARTED:
 	case EVENT_CH_SWITCH:
 		if (!data || !wpa_s->current_ssid)
 			break;
 
-		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CHANNEL_SWITCH
-			"freq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
+		wpa_msg(wpa_s, MSG_INFO,
+			"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
+			event == EVENT_CH_SWITCH ? WPA_EVENT_CHANNEL_SWITCH :
+			WPA_EVENT_CHANNEL_SWITCH_STARTED,
 			data->ch_switch.freq,
 			data->ch_switch.ht_enabled,
 			data->ch_switch.ch_offset,
 			channel_width_to_string(data->ch_switch.ch_width),
 			data->ch_switch.cf1,
 			data->ch_switch.cf2);
+		if (event == EVENT_CH_SWITCH_STARTED)
+			break;
 
 		wpa_s->assoc_freq = data->ch_switch.freq;
 		wpa_s->current_ssid->frequency = data->ch_switch.freq;
@@ -4489,13 +4600,12 @@
 					  data->ch_switch.ch_offset,
 					  data->ch_switch.ch_width,
 					  data->ch_switch.cf1,
-					  data->ch_switch.cf2);
+					  data->ch_switch.cf2,
+					  1);
 		}
 #endif /* CONFIG_AP */
 
-#ifdef CONFIG_IEEE80211W
 		sme_event_ch_switch(wpa_s);
-#endif /* CONFIG_IEEE80211W */
 		wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
 		wnm_clear_coloc_intf_reporting(wpa_s);
 		break;
@@ -4702,6 +4812,7 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
 			wpa_supplicant_update_mac_addr(wpa_s);
+			wpa_supplicant_set_default_scan_ies(wpa_s);
 			if (wpa_s->p2p_mgmt) {
 				wpa_supplicant_set_state(wpa_s,
 							 WPA_DISCONNECTED);
diff --git a/wpa_supplicant/hidl/1.2/misc_utils.h b/wpa_supplicant/hidl/1.2/misc_utils.h
deleted file mode 100644
index 1360e6b..0000000
--- a/wpa_supplicant/hidl/1.2/misc_utils.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * hidl interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef MISC_UTILS_H_
-#define MISC_UTILS_H_
-
-extern "C"
-{
-#include "wpabuf.h"
-}
-
-namespace {
-constexpr size_t kWpsPinNumDigits = 8;
-// Custom deleter for wpabuf.
-void freeWpaBuf(wpabuf *ptr) { wpabuf_free(ptr); }
-}  // namespace
-
-namespace android {
-namespace hardware {
-namespace wifi {
-namespace supplicant {
-namespace V1_2 {
-namespace implementation {
-namespace misc_utils {
-using wpabuf_unique_ptr = std::unique_ptr<wpabuf, void (*)(wpabuf *)>;
-
-// Creates a unique_ptr for wpabuf ptr with a custom deleter.
-inline wpabuf_unique_ptr createWpaBufUniquePtr(struct wpabuf *raw_ptr)
-{
-	return {raw_ptr, freeWpaBuf};
-}
-
-// Creates 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()));
-}
-
-// Copies the provided wpabuf contents to a std::vector.
-inline std::vector<uint8_t> convertWpaBufToVector(const struct wpabuf *buf)
-{
-	if (buf) {
-		return std::vector<uint8_t>(
-		    wpabuf_head_u8(buf), wpabuf_head_u8(buf) + wpabuf_len(buf));
-	} else {
-		return std::vector<uint8_t>();
-	}
-}
-
-// Returns a string holding the wps pin.
-inline std::string convertWpsPinToString(int pin)
-{
-	char pin_str[kWpsPinNumDigits + 1];
-	snprintf(pin_str, sizeof(pin_str), "%08d", pin);
-	return pin_str;
-}
-
-}  // namespace misc_utils
-}  // namespace implementation
-}  // namespace V1_2
-}  // namespace supplicant
-}  // namespace wifi
-}  // namespace hardware
-}  // namespace android
-#endif  // MISC_UTILS_H_
diff --git a/wpa_supplicant/hidl/1.3/android.hardware.wifi.supplicant-service.rc b/wpa_supplicant/hidl/1.3/android.hardware.wifi.supplicant-service.rc
new file mode 100644
index 0000000..3cf2500
--- /dev/null
+++ b/wpa_supplicant/hidl/1.3/android.hardware.wifi.supplicant-service.rc
@@ -0,0 +1,15 @@
+service wpa_supplicant /vendor/bin/hw/wpa_supplicant \
+    -O/data/vendor/wifi/wpa/sockets -dd \
+    -g@android:wpa_wlan0
+    #   we will start as root and wpa_supplicant will switch to user wifi
+    #   after setting up the capabilities required for WEXT
+    #   user wifi
+    #   group wifi inet keystore
+    interface android.hardware.wifi.supplicant@1.0::ISupplicant default
+    interface android.hardware.wifi.supplicant@1.1::ISupplicant default
+    interface android.hardware.wifi.supplicant@1.2::ISupplicant default
+    interface android.hardware.wifi.supplicant@1.3::ISupplicant default
+    class main
+    socket wpa_wlan0 dgram 660 wifi wifi
+    disabled
+    oneshot
diff --git a/wpa_supplicant/hidl/1.2/hidl.cpp b/wpa_supplicant/hidl/1.3/hidl.cpp
similarity index 83%
rename from wpa_supplicant/hidl/1.2/hidl.cpp
rename to wpa_supplicant/hidl/1.3/hidl.cpp
index ea1fa98..4c2d434 100644
--- a/wpa_supplicant/hidl/1.2/hidl.cpp
+++ b/wpa_supplicant/hidl/1.3/hidl.cpp
@@ -19,16 +19,20 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/includes.h"
+#include "dpp.h"
 }
 
 using android::hardware::configureRpcThreadpool;
 using android::hardware::handleTransportPoll;
 using android::hardware::setupTransportPolling;
-using android::hardware::wifi::supplicant::V1_2::implementation::HidlManager;
-using namespace android::hardware::wifi::supplicant::V1_2;
+using android::hardware::wifi::supplicant::V1_3::DppFailureCode;
+using android::hardware::wifi::supplicant::V1_3::DppProgressCode;
+using android::hardware::wifi::supplicant::V1_3::DppSuccessCode;
+using android::hardware::wifi::supplicant::V1_3::implementation::HidlManager;
 
 static void wpas_hidl_notify_dpp_failure(struct wpa_supplicant *wpa_s, DppFailureCode code);
 static void wpas_hidl_notify_dpp_progress(struct wpa_supplicant *wpa_s, DppProgressCode code);
+static void wpas_hidl_notify_dpp_success(struct wpa_supplicant *wpa_s, DppSuccessCode code);
 
 void wpas_hidl_sock_handler(
     int sock, void * /* eloop_ctx */, void * /* sock_ctx */)
@@ -666,84 +670,56 @@
 
 void wpas_hidl_notify_dpp_config_sent(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
-	wpa_printf(
-	    MSG_DEBUG,
-	    "Notifying DPP configuration sent");
-
-	HidlManager *hidl_manager = HidlManager::getInstance();
-	if (!hidl_manager)
-		return;
-
-	hidl_manager->notifyDppConfigSent(wpa_s);
+	wpas_hidl_notify_dpp_success(wpa_s, DppSuccessCode::CONFIGURATION_SENT);
 }
 
 /* DPP Progress notifications */
 void wpas_hidl_notify_dpp_auth_success(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
 	wpas_hidl_notify_dpp_progress(wpa_s, DppProgressCode::AUTHENTICATION_SUCCESS);
 }
 
 void wpas_hidl_notify_dpp_resp_pending(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
 	wpas_hidl_notify_dpp_progress(wpa_s, DppProgressCode::RESPONSE_PENDING);
 }
 
 /* DPP Failure notifications */
 void wpas_hidl_notify_dpp_not_compatible(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
 	wpas_hidl_notify_dpp_failure(wpa_s, DppFailureCode::NOT_COMPATIBLE);
 }
 
 void wpas_hidl_notify_dpp_missing_auth(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
+	wpas_hidl_notify_dpp_failure(wpa_s, DppFailureCode::AUTHENTICATION);
 }
 
 void wpas_hidl_notify_dpp_configuration_failure(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
 	wpas_hidl_notify_dpp_failure(wpa_s, DppFailureCode::CONFIGURATION);
 }
 
 void wpas_hidl_notify_dpp_timeout(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
 	wpas_hidl_notify_dpp_failure(wpa_s, DppFailureCode::TIMEOUT);
 }
 
 void wpas_hidl_notify_dpp_auth_failure(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
 	wpas_hidl_notify_dpp_failure(wpa_s, DppFailureCode::AUTHENTICATION);
 }
 
 void wpas_hidl_notify_dpp_fail(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s)
-		return;
-
 	wpas_hidl_notify_dpp_failure(wpa_s, DppFailureCode::FAILURE);
 }
 
+void wpas_hidl_notify_dpp_config_sent_wait_response(struct wpa_supplicant *wpa_s)
+{
+	wpas_hidl_notify_dpp_progress(wpa_s, DppProgressCode::CONFIGURATION_SENT_WAITING_RESPONSE);
+}
+
 /* DPP notification helper functions */
 static void wpas_hidl_notify_dpp_failure(struct wpa_supplicant *wpa_s, DppFailureCode code)
 {
@@ -776,3 +752,126 @@
 
 	hidl_manager->notifyDppProgress(wpa_s, code);
 }
+
+void wpas_hidl_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s)
+{
+	wpas_hidl_notify_dpp_progress(wpa_s, DppProgressCode::CONFIGURATION_ACCEPTED);
+}
+
+static void wpas_hidl_notify_dpp_config_applied(struct wpa_supplicant *wpa_s)
+{
+	wpas_hidl_notify_dpp_success(wpa_s, DppSuccessCode::CONFIGURATION_APPLIED);
+}
+
+static void wpas_hidl_notify_dpp_success(struct wpa_supplicant *wpa_s, DppSuccessCode code)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying DPP progress event %d", code);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyDppSuccess(wpa_s, code);
+}
+
+void wpas_hidl_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s)
+{
+	wpas_hidl_notify_dpp_failure(wpa_s, DppFailureCode::CONFIGURATION_REJECTED);
+}
+
+static void wpas_hidl_notify_dpp_no_ap_failure(struct wpa_supplicant *wpa_s,
+		const char *ssid, const char *channel_list, unsigned short band_list[],
+		int size)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+			"Notifying DPP NO AP event for SSID %s\nTried channels: %s",
+			ssid ? ssid : "N/A", channel_list ? channel_list : "N/A");
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyDppFailure(wpa_s, DppFailureCode::CANNOT_FIND_NETWORK,
+			ssid, channel_list, band_list, size);
+}
+
+void wpas_hidl_notify_dpp_enrollee_auth_failure(struct wpa_supplicant *wpa_s,
+		const char *ssid, unsigned short band_list[], int size)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+			"Notifying DPP Enrollee authentication failure, SSID %s",
+			ssid ? ssid : "N/A");
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyDppFailure(wpa_s, DppFailureCode::ENROLLEE_AUTHENTICATION,
+			ssid, NULL, band_list, size);
+}
+
+
+void wpas_hidl_notify_dpp_conn_status(struct wpa_supplicant *wpa_s, enum dpp_status_error status,
+		const char *ssid, const char *channel_list, unsigned short band_list[], int size)
+{
+	switch (status)
+	{
+	case DPP_STATUS_OK:
+		wpas_hidl_notify_dpp_config_applied(wpa_s);
+		break;
+
+	case DPP_STATUS_NO_AP:
+		wpas_hidl_notify_dpp_no_ap_failure(wpa_s, ssid, channel_list, band_list, size);
+		break;
+
+	case DPP_STATUS_AUTH_FAILURE:
+		wpas_hidl_notify_dpp_enrollee_auth_failure(wpa_s, ssid, band_list, size);
+		break;
+
+	default:
+		break;
+	}
+}
+
+void wpas_hidl_notify_pmk_cache_added(
+    struct wpa_supplicant *wpa_s,
+    struct rsn_pmksa_cache_entry *pmksa_entry)
+{
+	if (!wpa_s || !pmksa_entry)
+		return;
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying PMK cache added event");
+
+	hidl_manager->notifyPmkCacheAdded(wpa_s, pmksa_entry);
+}
+
+void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying BSS transition status");
+
+	hidl_manager->notifyBssTmStatus(wpa_s);
+}
diff --git a/wpa_supplicant/hidl/1.2/hidl.h b/wpa_supplicant/hidl/1.3/hidl.h
similarity index 90%
rename from wpa_supplicant/hidl/1.2/hidl.h
rename to wpa_supplicant/hidl/1.3/hidl.h
index a177f6e..304a4d6 100644
--- a/wpa_supplicant/hidl/1.2/hidl.h
+++ b/wpa_supplicant/hidl/1.3/hidl.h
@@ -111,6 +111,15 @@
 	void wpas_hidl_notify_dpp_timeout(struct wpa_supplicant *wpa_s);
 	void wpas_hidl_notify_dpp_auth_failure(struct wpa_supplicant *wpa_s);
 	void wpas_hidl_notify_dpp_fail(struct wpa_supplicant *wpa_s);
+	void wpas_hidl_notify_dpp_config_sent_wait_response(struct wpa_supplicant *wpa_s);
+	void wpas_hidl_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s);
+	void wpas_hidl_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s);
+	void wpas_hidl_notify_dpp_conn_status(struct wpa_supplicant *wpa_s,
+	    enum dpp_status_error status, const char *ssid,
+	    const char *channel_list, unsigned short band_list[], int size);
+	void wpas_hidl_notify_pmk_cache_added(
+	    struct wpa_supplicant *wpas, struct rsn_pmksa_cache_entry *pmksa_entry);
+	void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s);
 #else   // CONFIG_CTRL_IFACE_HIDL
 static inline int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -239,6 +248,19 @@
 {}
 static void wpas_hidl_notify_dpp_failure(struct wpa_supplicant *wpa_s)
 {}
+void wpas_hidl_notify_dpp_config_sent_wait_response(struct wpa_supplicant *wpa_s)
+{}
+void wpas_hidl_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s)
+{}
+void wpas_hidl_notify_dpp_config_applied(struct wpa_supplicant *wpa_s)
+{}
+void wpas_hidl_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s)
+{}
+static void wpas_hidl_notify_pmk_cache_added(struct wpa_supplicant *wpas,
+					     struct rsn_pmksa_cache_entry *pmksa_entry)
+{}
+void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s)
+{}
 #endif  // CONFIG_CTRL_IFACE_HIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/hidl/1.2/hidl_constants.h b/wpa_supplicant/hidl/1.3/hidl_constants.h
similarity index 100%
rename from wpa_supplicant/hidl/1.2/hidl_constants.h
rename to wpa_supplicant/hidl/1.3/hidl_constants.h
diff --git a/wpa_supplicant/hidl/1.2/hidl_i.h b/wpa_supplicant/hidl/1.3/hidl_i.h
similarity index 100%
rename from wpa_supplicant/hidl/1.2/hidl_i.h
rename to wpa_supplicant/hidl/1.3/hidl_i.h
diff --git a/wpa_supplicant/hidl/1.2/hidl_manager.cpp b/wpa_supplicant/hidl/1.3/hidl_manager.cpp
similarity index 83%
rename from wpa_supplicant/hidl/1.2/hidl_manager.cpp
rename to wpa_supplicant/hidl/1.3/hidl_manager.cpp
index b69fe04..e467da3 100644
--- a/wpa_supplicant/hidl/1.2/hidl_manager.cpp
+++ b/wpa_supplicant/hidl/1.3/hidl_manager.cpp
@@ -8,6 +8,7 @@
  */
 
 #include <algorithm>
+#include <iostream>
 #include <regex>
 
 #include "hidl_manager.h"
@@ -20,7 +21,6 @@
 
 namespace {
 using android::hardware::hidl_array;
-using namespace android::hardware::wifi::supplicant::V1_2;
 
 constexpr uint8_t kWfdDeviceInfoLen = 6;
 // GSM-AUTH:<RAND1>:<RAND2>[:<RAND3>]
@@ -289,13 +289,13 @@
 	}
 }
 
-template <class CallbackTypeV1_0, class CallbackTypeV1_1>
-void callWithEachIfaceCallback_1_1(
+template <class CallbackTypeBase, class CallbackTypeDerived>
+void callWithEachIfaceCallbackDerived(
     const std::string &ifname,
     const std::function<
-	android::hardware::Return<void>(android::sp<CallbackTypeV1_1>)> &method,
+	android::hardware::Return<void>(android::sp<CallbackTypeDerived>)> &method,
     const std::map<
-	const std::string, std::vector<android::sp<CallbackTypeV1_0>>>
+	const std::string, std::vector<android::sp<CallbackTypeBase>>>
 	&callbacks_map)
 {
 	if (ifname.empty())
@@ -306,41 +306,12 @@
 		return;
 	const auto &iface_callback_list = iface_callback_map_iter->second;
 	for (const auto &callback : iface_callback_list) {
-		android::sp<CallbackTypeV1_1> callback_1_1 =
-		    CallbackTypeV1_1::castFrom(callback);
-		if (callback_1_1 == nullptr)
+		android::sp<CallbackTypeDerived> callback_derived =
+		    CallbackTypeDerived::castFrom(callback);
+		if (callback_derived == nullptr)
 			continue;
 
-		if (!method(callback_1_1).isOk()) {
-			wpa_printf(
-			    MSG_ERROR, "Failed to invoke HIDL iface callback");
-		}
-	}
-}
-
-template <class CallbackTypeV1_0, class CallbackTypeV1_2>
-void callWithEachIfaceCallback_1_2(
-    const std::string &ifname,
-    const std::function<
-	android::hardware::Return<void>(android::sp<CallbackTypeV1_2>)> &method,
-    const std::map<
-	const std::string, std::vector<android::sp<CallbackTypeV1_0>>>
-	&callbacks_map)
-{
-	if (ifname.empty())
-		return;
-
-	auto iface_callback_map_iter = callbacks_map.find(ifname);
-	if (iface_callback_map_iter == callbacks_map.end())
-		return;
-	const auto &iface_callback_list = iface_callback_map_iter->second;
-	for (const auto &callback : iface_callback_list) {
-		android::sp<CallbackTypeV1_2> callback_1_2 =
-		    CallbackTypeV1_2::castFrom(callback);
-		if (callback_1_2 == nullptr)
-			continue;
-
-		if (!method(callback_1_2).isOk()) {
+		if (!method(callback_derived).isOk()) {
 			wpa_printf(
 			    MSG_ERROR, "Failed to invoke HIDL iface callback");
 		}
@@ -429,10 +400,8 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
-
-using namespace android::hardware::wifi::supplicant::V1_2;
 using V1_0::ISupplicantStaIfaceCallback;
 
 HidlManager *HidlManager::instance_ = NULL;
@@ -731,13 +700,22 @@
 	} else {
 		bssid = wpa_s->bssid;
 	}
-	callWithEachStaIfaceCallback(
-	    wpa_s->ifname, std::bind(
-			       &ISupplicantStaIfaceCallback::onStateChanged,
-			       std::placeholders::_1,
-			       static_cast<ISupplicantStaIfaceCallback::State>(
-				   wpa_s->wpa_state),
-			       bssid, hidl_network_id, hidl_ssid));
+	bool fils_hlp_sent =
+		(wpa_auth_alg_fils(wpa_s->auth_alg) &&
+		 !dl_list_empty(&wpa_s->fils_hlp_req) &&
+		 (wpa_s->wpa_state == WPA_COMPLETED)) ? true : false;
+
+	// Invoke the |onStateChanged_1_3| method on all registered callbacks.
+	const std::function<
+		Return<void>(android::sp<V1_3::ISupplicantStaIfaceCallback>)>
+		func = std::bind(
+			&V1_3::ISupplicantStaIfaceCallback::onStateChanged_1_3,
+			std::placeholders::_1,
+			static_cast<ISupplicantStaIfaceCallback::State>(
+				wpa_s->wpa_state),
+				bssid, hidl_network_id, hidl_ssid,
+				fils_hlp_sent);
+	callWithEachStaIfaceCallbackDerived(wpa_s->ifname, func);
 	return 0;
 }
 
@@ -1269,6 +1247,16 @@
 	bool hidl_is_go = (client == 0 ? true : false);
 	bool hidl_is_persistent = (persistent == 1 ? true : false);
 
+	// notify the group device again to ensure the framework knowing this device.
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	struct p2p_device *dev = p2p_get_device(p2p, wpa_group_s->go_dev_addr);
+	if (NULL != dev) {
+		wpa_printf(MSG_DEBUG, "P2P: Update GO device on group started.");
+		p2p->cfg->dev_found(p2p->cfg->cb_ctx, wpa_group_s->go_dev_addr,
+				&dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE));
+		dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+	}
+
 	callWithEachP2pIfaceCallback(
 	    wpa_s->ifname,
 	    std::bind(
@@ -1462,27 +1450,14 @@
 
 void HidlManager::notifyEapError(struct wpa_supplicant *wpa_s, int error_code)
 {
-	typedef V1_1::ISupplicantStaIfaceCallback::EapErrorCode EapErrorCode;
-
 	if (!wpa_s)
 		return;
 
-	switch (static_cast<EapErrorCode>(error_code)) {
-	case EapErrorCode::SIM_GENERAL_FAILURE_AFTER_AUTH:
-	case EapErrorCode::SIM_TEMPORARILY_DENIED:
-	case EapErrorCode::SIM_NOT_SUBSCRIBED:
-	case EapErrorCode::SIM_GENERAL_FAILURE_BEFORE_AUTH:
-	case EapErrorCode::SIM_VENDOR_SPECIFIC_EXPIRED_CERT:
-		break;
-	default:
-		return;
-	}
-
-	callWithEachStaIfaceCallback_1_1(
+	callWithEachStaIfaceCallback_1_3(
 	    wpa_s->ifname,
 	    std::bind(
-		&V1_1::ISupplicantStaIfaceCallback::onEapFailure_1_1,
-		std::placeholders::_1, static_cast<EapErrorCode>(error_code)));
+		&V1_3::ISupplicantStaIfaceCallback::onEapFailure_1_3,
+		std::placeholders::_1, error_code));
 }
 
 /**
@@ -1507,7 +1482,9 @@
 		/* Unsupported AKM */
 		wpa_printf(MSG_ERROR, "DPP: Error: Unsupported AKM 0x%X",
 				config->key_mgmt);
-		notifyDppFailure(wpa_s, DppFailureCode::NOT_SUPPORTED);
+		notifyDppFailure(wpa_s,
+				android::hardware::wifi::supplicant::V1_3::DppFailureCode
+				::NOT_SUPPORTED);
 		return;
 	}
 
@@ -1545,13 +1522,29 @@
  * @param ifname Interface name
  * @param code Status code
  */
-void HidlManager::notifyDppFailure(struct wpa_supplicant *wpa_s, DppFailureCode code)
-{
+void HidlManager::notifyDppFailure(struct wpa_supplicant *wpa_s,
+		android::hardware::wifi::supplicant::V1_3::DppFailureCode code) {
 	std::string hidl_ifname = wpa_s->ifname;
 
-	callWithEachStaIfaceCallback_1_2(hidl_ifname,
-			std::bind(&V1_2::ISupplicantStaIfaceCallback::onDppFailure,
-					std::placeholders::_1, code));
+	notifyDppFailure(wpa_s, code, NULL, NULL, NULL, 0);
+}
+
+/**
+ * Notify listener about a DPP failure event
+ *
+ * @param ifname Interface name
+ * @param code Status code
+ */
+void HidlManager::notifyDppFailure(struct wpa_supplicant *wpa_s,
+		android::hardware::wifi::supplicant::V1_3::DppFailureCode code,
+		const char *ssid, const char *channel_list, unsigned short band_list[],
+		int size) {
+	std::string hidl_ifname = wpa_s->ifname;
+	std::vector<uint16_t> band_list_vec(band_list, band_list + size);
+
+	callWithEachStaIfaceCallback_1_3(hidl_ifname,
+			std::bind(&V1_3::ISupplicantStaIfaceCallback::onDppFailure_1_3,
+					std::placeholders::_1, code, ssid, channel_list, band_list_vec));
 }
 
 /**
@@ -1560,16 +1553,209 @@
  * @param ifname Interface name
  * @param code Status code
  */
-void HidlManager::notifyDppProgress(struct wpa_supplicant *wpa_s, DppProgressCode code)
+void HidlManager::notifyDppProgress(struct wpa_supplicant *wpa_s,
+		android::hardware::wifi::supplicant::V1_3::DppProgressCode code) {
+	std::string hidl_ifname = wpa_s->ifname;
+
+	callWithEachStaIfaceCallback_1_3(hidl_ifname,
+			std::bind(&V1_3::ISupplicantStaIfaceCallback::onDppProgress_1_3,
+					std::placeholders::_1, code));
+}
+
+/**
+ * Notify listener about a DPP success event
+ *
+ * @param ifname Interface name
+ * @param code Status code
+ */
+void HidlManager::notifyDppSuccess(struct wpa_supplicant *wpa_s, DppSuccessCode code)
 {
 	std::string hidl_ifname = wpa_s->ifname;
 
-	callWithEachStaIfaceCallback_1_2(hidl_ifname,
-			std::bind(&V1_2::ISupplicantStaIfaceCallback::onDppProgress,
+	callWithEachStaIfaceCallback_1_3(hidl_ifname,
+			std::bind(&V1_3::ISupplicantStaIfaceCallback::onDppSuccess,
 					std::placeholders::_1, code));
 }
 
 /**
+ * Notify listener about a PMK cache added event
+ *
+ * @param ifname Interface name
+ * @param entry PMK cache entry
+ */
+void HidlManager::notifyPmkCacheAdded(
+    struct wpa_supplicant *wpa_s, struct rsn_pmksa_cache_entry *pmksa_entry)
+{
+	std::string hidl_ifname = wpa_s->ifname;
+
+	// Serialize PmkCacheEntry into blob.
+	std::stringstream ss(
+	    std::stringstream::in | std::stringstream::out | std::stringstream::binary);
+	misc_utils::serializePmkCacheEntry(ss, pmksa_entry);
+	std::vector<uint8_t> serializedEntry(
+		std::istreambuf_iterator<char>(ss), {});
+
+	const std::function<
+	    Return<void>(android::sp<V1_3::ISupplicantStaIfaceCallback>)>
+	    func = std::bind(
+		&V1_3::ISupplicantStaIfaceCallback::onPmkCacheAdded,
+		std::placeholders::_1, pmksa_entry->expiration, serializedEntry);
+	callWithEachStaIfaceCallbackDerived(hidl_ifname, func);
+}
+
+#ifdef CONFIG_WNM
+V1_3::ISupplicantStaIfaceCallback::BssTmStatusCode convertSupplicantBssTmStatusToHidl(
+    enum bss_trans_mgmt_status_code bss_tm_status)
+{
+	switch (bss_tm_status) {
+		case WNM_BSS_TM_ACCEPT:
+			return V1_3::ISupplicantStaIfaceCallback::BssTmStatusCode::ACCEPT;
+		case WNM_BSS_TM_REJECT_UNSPECIFIED:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_UNSPECIFIED;
+		case WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_INSUFFICIENT_BEACON;
+		case WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_INSUFFICIENT_CAPABITY;
+		case WNM_BSS_TM_REJECT_UNDESIRED:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_BSS_TERMINATION_UNDESIRED;
+		case WNM_BSS_TM_REJECT_DELAY_REQUEST:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_BSS_TERMINATION_DELAY_REQUEST;
+		case WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_STA_CANDIDATE_LIST_PROVIDED;
+		case WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_NO_SUITABLE_CANDIDATES;
+		case WNM_BSS_TM_REJECT_LEAVING_ESS:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_LEAVING_ESS;
+		default:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_UNSPECIFIED;
+	}
+}
+
+uint32_t setBssTmDataFlagsMask(struct wpa_supplicant *wpa_s)
+{
+	uint32_t flags = 0;
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_BSS_TERMINATION_INCLUDED;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_ESS_DISASSOCIATION_IMMINENT;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_DISASSOCIATION_IMMINENT;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ABRIDGED) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_ABRIDGED;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_PREFERRED_CANDIDATE_LIST_INCLUDED;
+	}
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_assoc_retry_delay_present) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::MBO_ASSOC_RETRY_DELAY_INCLUDED;
+	}
+	if (wpa_s->wnm_mbo_trans_reason_present) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::MBO_TRANSITION_REASON_CODE_INCLUDED;
+	}
+	if (wpa_s->wnm_mbo_cell_pref_present) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::MBO_CELLULAR_DATA_CONNECTION_PREFERENCE_INCLUDED;
+	}
+#endif
+	return flags;
+}
+
+uint32_t getBssTmDataAssocRetryDelayMs(struct wpa_supplicant *wpa_s)
+{
+	uint32_t beacon_int;
+	uint32_t duration_ms = 0;
+
+	if (wpa_s->current_bss)
+		beacon_int = wpa_s->current_bss->beacon_int;
+	else
+		beacon_int = 100; /* best guess */
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		// number of tbtts to milliseconds
+		duration_ms = wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+		//wnm_bss_termination_duration contains 12 bytes of BSS
+		//termination duration subelement. Format of IE is
+		// Sub eid | Length | BSS termination TSF | Duration
+		//    1         1             8                2
+		// Duration indicates number of minutes for which BSS is not
+		// present.
+		duration_ms = WPA_GET_LE16(wpa_s->wnm_bss_termination_duration + 10);
+		// minutes to milliseconds
+		duration_ms = duration_ms * 60 * 1000;
+	}
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_assoc_retry_delay_present) {
+		// number of seconds to milliseconds
+		duration_ms = wpa_s->wnm_mbo_assoc_retry_delay_sec * 1000;
+	}
+#endif
+
+	return duration_ms;
+}
+#endif
+
+/**
+ * Notify listener about the status of BSS transition management
+ * request frame handling.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is present.
+ */
+void HidlManager::notifyBssTmStatus(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+	std::string hidl_ifname = wpa_s->ifname;
+	V1_3::ISupplicantStaIfaceCallback::BssTmData hidl_bsstm_data = {};
+
+	hidl_bsstm_data.status = convertSupplicantBssTmStatusToHidl(wpa_s->bss_tm_status);
+	hidl_bsstm_data.flags = setBssTmDataFlagsMask(wpa_s);
+	hidl_bsstm_data.assocRetryDelayMs = getBssTmDataAssocRetryDelayMs(wpa_s);
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_cell_pref_present) {
+		hidl_bsstm_data.mboCellPreference = static_cast
+		    <V1_3::ISupplicantStaIfaceCallback::MboCellularDataConnectionPrefValue>
+		    (wpa_s->wnm_mbo_cell_preference);
+	}
+	if (wpa_s->wnm_mbo_trans_reason_present) {
+		hidl_bsstm_data.mboTransitionReason =
+		    static_cast<V1_3::ISupplicantStaIfaceCallback::MboTransitionReasonCode>
+		    (wpa_s->wnm_mbo_transition_reason);
+	}
+#endif
+
+	const std::function<
+	    Return<void>(android::sp<V1_3::ISupplicantStaIfaceCallback>)>
+	    func = std::bind(
+		&V1_3::ISupplicantStaIfaceCallback::onBssTmHandlingDone,
+		std::placeholders::_1, hidl_bsstm_data);
+	callWithEachStaIfaceCallbackDerived(hidl_ifname, func);
+#endif
+}
+
+/**
  * Retrieve the |ISupplicantP2pIface| hidl object reference using the provided
  * ifname.
  *
@@ -1602,7 +1788,7 @@
  * @return 0 on success, 1 on failure.
  */
 int HidlManager::getStaIfaceHidlObjectByIfname(
-    const std::string &ifname, android::sp<ISupplicantStaIface> *iface_object)
+    const std::string &ifname, android::sp<V1_1::ISupplicantStaIface> *iface_object)
 {
 	if (ifname.empty() || !iface_object)
 		return 1;
@@ -1911,7 +2097,7 @@
     const std::function<
 	Return<void>(android::sp<V1_1::ISupplicantStaIfaceCallback>)> &method)
 {
-	callWithEachIfaceCallback_1_1(ifname, method, sta_iface_callbacks_map_);
+	callWithEachIfaceCallbackDerived(ifname, method, sta_iface_callbacks_map_);
 }
 
 /**
@@ -1928,7 +2114,42 @@
     const std::function<
 	Return<void>(android::sp<V1_2::ISupplicantStaIfaceCallback>)> &method)
 {
-	callWithEachIfaceCallback_1_2(ifname, method, sta_iface_callbacks_map_);
+	callWithEachIfaceCallbackDerived(ifname, method, sta_iface_callbacks_map_);
+}
+
+/**
+ * Helper function to invoke the provided callback method on all the
+ * registered V1.3 interface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * |V1_3::ISupplicantIfaceCallback|.
+ */
+void HidlManager::callWithEachStaIfaceCallback_1_3(
+    const std::string &ifname,
+    const std::function<
+	Return<void>(android::sp<V1_3::ISupplicantStaIfaceCallback>)> &method)
+{
+	callWithEachIfaceCallbackDerived(ifname, method, sta_iface_callbacks_map_);
+}
+
+/**
+ * Helper function to invoke the provided callback method on all the
+ * registered derived interface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * derived |V1_x::ISupplicantIfaceCallback|.
+ */
+template <class CallbackTypeDerived>
+void HidlManager::callWithEachStaIfaceCallbackDerived(
+    const std::string &ifname,
+    const std::function<
+	Return<void>(android::sp<CallbackTypeDerived>)> &method)
+{
+	callWithEachIfaceCallbackDerived(ifname, method, sta_iface_callbacks_map_);
 }
 
 /**
@@ -1986,7 +2207,7 @@
 	    ifname, network_id, method, sta_network_callbacks_map_);
 }
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/hidl_manager.h b/wpa_supplicant/hidl/1.3/hidl_manager.h
similarity index 92%
rename from wpa_supplicant/hidl/1.2/hidl_manager.h
rename to wpa_supplicant/hidl/1.3/hidl_manager.h
index 910e2bf..e49e28d 100644
--- a/wpa_supplicant/hidl/1.2/hidl_manager.h
+++ b/wpa_supplicant/hidl/1.3/hidl_manager.h
@@ -16,11 +16,12 @@
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pIfaceCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetworkCallback.h>
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
 
 #include "p2p_iface.h"
 #include "p2p_network.h"
+#include "rsn_supp/pmksa_cache.h"
 #include "sta_iface.h"
 #include "sta_network.h"
 #include "supplicant.h"
@@ -37,13 +38,14 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
-using namespace android::hardware::wifi::supplicant::V1_2;
+using V1_0::ISupplicant;
 using V1_0::ISupplicantP2pIface;
+using V1_0::ISupplicantStaIface;
 using V1_0::ISupplicantStaIfaceCallback;
-using V1_1::ISupplicant;
-using V1_1::ISupplicantStaIface;
+using V1_0::P2pGroupCapabilityMask;
+using V1_0::WpsConfigMethods;
 
 /**
  * HidlManager is responsible for managing the lifetime of all
@@ -131,8 +133,18 @@
 	void notifyDppConfigReceived(struct wpa_supplicant *wpa_s,
 			struct wpa_ssid *config);
 	void notifyDppConfigSent(struct wpa_supplicant *wpa_s);
-	void notifyDppFailure(struct wpa_supplicant *wpa_s, DppFailureCode code);
-	void notifyDppProgress(struct wpa_supplicant *wpa_s, DppProgressCode code);
+	void notifyDppSuccess(struct wpa_supplicant *wpa_s, DppSuccessCode code);
+	void notifyDppFailure(struct wpa_supplicant *wpa_s,
+			android::hardware::wifi::supplicant::V1_3::DppFailureCode code);
+	void notifyDppFailure(struct wpa_supplicant *wpa_s,
+			android::hardware::wifi::supplicant::V1_3::DppFailureCode code,
+			const char *ssid, const char *channel_list, unsigned short band_list[],
+			int size);
+	void notifyDppProgress(struct wpa_supplicant *wpa_s,
+			android::hardware::wifi::supplicant::V1_3::DppProgressCode code);
+	void notifyPmkCacheAdded(struct wpa_supplicant *wpa_s,
+			struct rsn_pmksa_cache_entry *pmksa_entry);
+	void notifyBssTmStatus(struct wpa_supplicant *wpa_s);
 
 	// Methods called from hidl objects.
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
@@ -206,6 +218,15 @@
 	    const std::string &ifname,
 	    const std::function<android::hardware::Return<void>(
 	    android::sp<V1_2::ISupplicantStaIfaceCallback>)> &method);
+	void callWithEachStaIfaceCallback_1_3(
+	    const std::string &ifname,
+	    const std::function<android::hardware::Return<void>(
+	    android::sp<V1_3::ISupplicantStaIfaceCallback>)> &method);
+	template <class CallbackTypeDerived>
+	void callWithEachStaIfaceCallbackDerived(
+	    const std::string &ifname,
+	    const std::function<
+		Return<void>(android::sp<CallbackTypeDerived>)> &method);
 	void callWithEachP2pNetworkCallback(
 	    const std::string &ifname, int network_id,
 	    const std::function<android::hardware::Return<void>(
@@ -365,6 +386,14 @@
 	WPA_KEY_MGMT_IEEE8021X_SHA256,
     "KeyMgmt value mismatch");
 static_assert(
+    static_cast<uint32_t>(V1_3::ISupplicantStaNetwork::KeyMgmtMask::WAPI_PSK) ==
+	WPA_KEY_MGMT_WAPI_PSK,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(V1_3::ISupplicantStaNetwork::KeyMgmtMask::WAPI_CERT) ==
+	WPA_KEY_MGMT_WAPI_CERT,
+    "KeyMgmt value mismatch");
+static_assert(
     static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::WPA) ==
 	WPA_PROTO_WPA,
     "Proto value mismatch");
@@ -377,6 +406,10 @@
 	WPA_PROTO_OSEN,
     "Proto value mismatch");
 static_assert(
+    static_cast<uint32_t>(V1_3::ISupplicantStaNetwork::ProtoMask::WAPI) ==
+	WPA_PROTO_WAPI,
+    "Proto value mismatch");
+static_assert(
     static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::OPEN) ==
 	WPA_AUTH_ALG_OPEN,
     "AuthAlg value mismatch");
@@ -409,6 +442,10 @@
 	WPA_CIPHER_GCMP_256,
     "GroupCipher value mismatch");
 static_assert(
+    static_cast<uint32_t>(V1_3::ISupplicantStaNetwork::GroupCipherMask::SMS4) ==
+	WPA_CIPHER_SMS4,
+    "GroupCipher value mismatch");
+static_assert(
     static_cast<uint32_t>(
 	ISupplicantStaNetwork::GroupCipherMask::GTK_NOT_USED) ==
 	WPA_CIPHER_GTK_NOT_USED,
@@ -431,6 +468,11 @@
 	WPA_CIPHER_GCMP_256,
     "PairwiseCipher value mismatch");
 static_assert(
+    static_cast<uint32_t>(
+	V1_3::ISupplicantStaNetwork::PairwiseCipherMask::SMS4) ==
+	WPA_CIPHER_SMS4,
+    "PairwiseCipher value mismatch");
+static_assert(
     static_cast<uint32_t>(ISupplicantStaIfaceCallback::State::DISCONNECTED) ==
 	WPA_DISCONNECTED,
     "State value mismatch");
@@ -718,7 +760,7 @@
 	P2P_PROV_DISC_INFO_UNAVAILABLE,
     "P2P status code value mismatch");
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/hidl_return_util.h b/wpa_supplicant/hidl/1.3/hidl_return_util.h
similarity index 97%
rename from wpa_supplicant/hidl/1.2/hidl_return_util.h
rename to wpa_supplicant/hidl/1.3/hidl_return_util.h
index 238646a..4c1f919 100644
--- a/wpa_supplicant/hidl/1.2/hidl_return_util.h
+++ b/wpa_supplicant/hidl/1.3/hidl_return_util.h
@@ -14,9 +14,10 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 namespace hidl_return_util {
+using V1_0::SupplicantStatusCode;
 
 /**
  * These utility functions are used to invoke a method on the provided
@@ -93,7 +94,7 @@
 
 }  // namespace hidl_return_util
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/iface_config_utils.cpp b/wpa_supplicant/hidl/1.3/iface_config_utils.cpp
similarity index 99%
rename from wpa_supplicant/hidl/1.2/iface_config_utils.cpp
rename to wpa_supplicant/hidl/1.3/iface_config_utils.cpp
index 43908e3..31370a6 100644
--- a/wpa_supplicant/hidl/1.2/iface_config_utils.cpp
+++ b/wpa_supplicant/hidl/1.3/iface_config_utils.cpp
@@ -81,7 +81,7 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 namespace iface_config_utils {
 SupplicantStatus setWpsDeviceName(
@@ -176,7 +176,7 @@
 }
 }  // namespace iface_config_utils
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/iface_config_utils.h b/wpa_supplicant/hidl/1.3/iface_config_utils.h
similarity index 97%
rename from wpa_supplicant/hidl/1.2/iface_config_utils.h
rename to wpa_supplicant/hidl/1.3/iface_config_utils.h
index 9e88b3e..822d7ac 100644
--- a/wpa_supplicant/hidl/1.2/iface_config_utils.h
+++ b/wpa_supplicant/hidl/1.3/iface_config_utils.h
@@ -30,7 +30,7 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 namespace iface_config_utils {
 SupplicantStatus setWpsDeviceName(
@@ -51,7 +51,7 @@
     struct wpa_supplicant* wpa_s, bool useExternalSim);
 }  // namespace iface_config_utils
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/manifest.xml b/wpa_supplicant/hidl/1.3/manifest.xml
new file mode 100644
index 0000000..33e4fd4
--- /dev/null
+++ b/wpa_supplicant/hidl/1.3/manifest.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.wifi.supplicant</name>
+        <transport>hwbinder</transport>
+        <version>1.3</version>
+        <interface>
+            <name>ISupplicant</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/wpa_supplicant/hidl/1.3/misc_utils.h b/wpa_supplicant/hidl/1.3/misc_utils.h
new file mode 100644
index 0000000..b95b1ee
--- /dev/null
+++ b/wpa_supplicant/hidl/1.3/misc_utils.h
@@ -0,0 +1,111 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MISC_UTILS_H_
+#define MISC_UTILS_H_
+
+#include <iostream>
+
+extern "C"
+{
+#include "wpabuf.h"
+}
+
+namespace {
+constexpr size_t kWpsPinNumDigits = 8;
+// Custom deleter for wpabuf.
+void freeWpaBuf(wpabuf *ptr) { wpabuf_free(ptr); }
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_3 {
+namespace implementation {
+namespace misc_utils {
+using wpabuf_unique_ptr = std::unique_ptr<wpabuf, void (*)(wpabuf *)>;
+
+// Creates a unique_ptr for wpabuf ptr with a custom deleter.
+inline wpabuf_unique_ptr createWpaBufUniquePtr(struct wpabuf *raw_ptr)
+{
+	return {raw_ptr, freeWpaBuf};
+}
+
+// Creates 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()));
+}
+
+// Copies the provided wpabuf contents to a std::vector.
+inline std::vector<uint8_t> convertWpaBufToVector(const struct wpabuf *buf)
+{
+	if (buf) {
+		return std::vector<uint8_t>(
+		    wpabuf_head_u8(buf), wpabuf_head_u8(buf) + wpabuf_len(buf));
+	} else {
+		return std::vector<uint8_t>();
+	}
+}
+
+// Returns a string holding the wps pin.
+inline std::string convertWpsPinToString(int pin)
+{
+	char pin_str[kWpsPinNumDigits + 1];
+	snprintf(pin_str, sizeof(pin_str), "%08d", pin);
+	return pin_str;
+}
+
+inline std::stringstream& serializePmkCacheEntry(
+    std::stringstream &ss, struct rsn_pmksa_cache_entry *pmksa_entry) {
+	ss.write((char *) &pmksa_entry->pmk_len, sizeof(pmksa_entry->pmk_len));
+	ss.write((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
+	ss.write((char *) pmksa_entry->pmkid, PMKID_LEN);
+	ss.write((char *) pmksa_entry->aa, ETH_ALEN);
+	// Omit wpa_ssid field because the network is created on connecting to a access point.
+	ss.write((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
+	ss.write((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
+	ss.write((char *) &pmksa_entry->expiration, sizeof(pmksa_entry->expiration));
+	ss.write((char *) &pmksa_entry->opportunistic, sizeof(pmksa_entry->opportunistic));
+	char byte = (pmksa_entry->fils_cache_id_set) ? 1 : 0;
+	ss.write((char *) &byte, sizeof(byte));
+	ss.write((char *) pmksa_entry->fils_cache_id, FILS_CACHE_ID_LEN);
+	ss << std::flush;
+	return ss;
+}
+
+inline std::stringstream& deserializePmkCacheEntry(
+    std::stringstream &ss, struct rsn_pmksa_cache_entry *pmksa_entry) {
+	ss.seekg(0);
+	ss.read((char *) &pmksa_entry->pmk_len, sizeof(pmksa_entry->pmk_len));
+	ss.read((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
+	ss.read((char *) pmksa_entry->pmkid, PMKID_LEN);
+	ss.read((char *) pmksa_entry->aa, ETH_ALEN);
+	// Omit wpa_ssid field because the network is created on connecting to a access point.
+	ss.read((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
+	ss.read((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
+	ss.read((char *) &pmksa_entry->expiration, sizeof(pmksa_entry->expiration));
+	ss.read((char *) &pmksa_entry->opportunistic, sizeof(pmksa_entry->opportunistic));
+	char byte = 0;
+	ss.read((char *) &byte, sizeof(byte));
+	pmksa_entry->fils_cache_id_set = (byte) ? 1 : 0;
+	ss.read((char *) pmksa_entry->fils_cache_id, FILS_CACHE_ID_LEN);
+	return ss;
+}
+}  // namespace misc_utils
+}  // namespace implementation
+}  // namespace V1_3
+}  // namespace supplicant
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+#endif  // MISC_UTILS_H_
diff --git a/wpa_supplicant/hidl/1.2/p2p_iface.cpp b/wpa_supplicant/hidl/1.3/p2p_iface.cpp
similarity index 98%
rename from wpa_supplicant/hidl/1.2/p2p_iface.cpp
rename to wpa_supplicant/hidl/1.3/p2p_iface.cpp
index fd9ce0d..bd893a3 100644
--- a/wpa_supplicant/hidl/1.2/p2p_iface.cpp
+++ b/wpa_supplicant/hidl/1.3/p2p_iface.cpp
@@ -163,7 +163,7 @@
 
 	// set P2p network defaults
 	wpa_network->p2p_group = 1;
-	wpa_network->mode = wpa_ssid::wpas_mode::WPAS_MODE_INFRA;
+	wpa_network->mode = wpas_mode::WPAS_MODE_INFRA;
 
 	wpa_network->auth_alg = WPA_AUTH_ALG_OPEN;
 	wpa_network->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -209,6 +209,12 @@
     struct wpa_supplicant *wpa_s,
     struct wpa_scan_results *scan_res)
 {
+	if (wpa_s->p2p_scan_work) {
+		struct wpa_radio_work *work = wpa_s->p2p_scan_work;
+		wpa_s->p2p_scan_work = NULL;
+		radio_work_done(work);
+	}
+
 	if (pending_scan_res_join_callback) {
 		pending_scan_res_join_callback();
 	}
@@ -310,6 +316,9 @@
 	ret = wpa_drv_scan(wpa_s, &params);
 	if (!ret) {
 		os_get_reltime(&wpa_s->scan_trigger_time);
+		if (wpa_s->scan_res_handler) {
+			wpa_printf(MSG_DEBUG, "Replace current running scan result handler");
+		}
 		wpa_s->scan_res_handler = scanResJoinWrapper;
 		wpa_s->own_scan_requested = 1;
 		wpa_s->clear_driver_scan_cache = 0;
@@ -331,6 +340,7 @@
     const std::string& passphrase)
 {
 	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;
 
@@ -349,7 +359,7 @@
 
 	if (wpas_p2p_group_add_persistent(
 		wpa_s, wpa_network, 0, 0, 0, 0, ht40, vht,
-		VHT_CHANWIDTH_USE_HT, 0, NULL, 0, 1)) {
+		CHANWIDTH_USE_HT, he, 0, NULL, 0, 0)) {
 		ret = -1;
 	}
 
@@ -381,6 +391,13 @@
 
 void scanResJoinIgnore(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) {
 	wpa_printf(MSG_DEBUG, "P2P: Ignore group join scan results.");
+
+	if (wpa_s->p2p_scan_work) {
+		struct wpa_radio_work *work = wpa_s->p2p_scan_work;
+		wpa_s->p2p_scan_work = NULL;
+		radio_work_done(work);
+	}
+
 }
 
 }  // namespace
@@ -389,9 +406,10 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 using hidl_return_util::validateAndCall;
+using V1_0::SupplicantStatusCode;
 
 P2pIface::P2pIface(struct wpa_global* wpa_global, const char ifname[])
     : wpa_global_(wpa_global), ifname_(ifname), is_valid_(true)
@@ -1060,6 +1078,7 @@
 		wps_method = WPS_PIN_KEYPAD;
 		break;
 	}
+	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;
 	const char* pin =
@@ -1067,7 +1086,7 @@
 	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,
-	    vht, VHT_CHANWIDTH_USE_HT, 0, nullptr, 0);
+	    vht, CHANWIDTH_USE_HT, he, 0, nullptr, 0);
 	if (new_pin < 0) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
@@ -1123,6 +1142,7 @@
     bool persistent, SupplicantNetworkId persistent_network_id)
 {
 	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;
 	struct wpa_ssid* ssid =
@@ -1130,7 +1150,7 @@
 	if (ssid == NULL) {
 		if (wpas_p2p_group_add(
 			wpa_s, persistent, 0, 0, ht40, vht,
-			VHT_CHANWIDTH_USE_HT, 0)) {
+			CHANWIDTH_USE_HT, he, 0)) {
 			return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 		} else {
 			return {SupplicantStatusCode::SUCCESS, ""};
@@ -1138,7 +1158,7 @@
 	} else if (ssid->disabled == 2) {
 		if (wpas_p2p_group_add_persistent(
 			wpa_s, ssid, 0, 0, 0, 0, ht40, vht,
-			VHT_CHANWIDTH_USE_HT, 0, NULL, 0, 0)) {
+			CHANWIDTH_USE_HT, he, 0, NULL, 0, 0)) {
 			return {SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN,
 				""};
 		} else {
@@ -1193,6 +1213,7 @@
     const std::array<uint8_t, 6>& peer_address)
 {
 	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;
 	struct wpa_ssid* ssid =
@@ -1202,7 +1223,7 @@
 	}
 	if (wpas_p2p_invite(
 		wpa_s, peer_address.data(), ssid, NULL, 0, 0, ht40, vht,
-		VHT_CHANWIDTH_USE_HT, 0, 0)) {
+		CHANWIDTH_USE_HT, 0, he, 0)) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
 	return {SupplicantStatusCode::SUCCESS, ""};
@@ -1631,6 +1652,7 @@
     bool joinExistingGroup)
 {
 	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;
 
@@ -1658,7 +1680,7 @@
 
 		if (wpas_p2p_group_add(
 		    wpa_s, persistent, freq, 0, ht40, vht,
-		    VHT_CHANWIDTH_USE_HT, 0)) {
+		    CHANWIDTH_USE_HT, he, 0)) {
 			return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 		}
 		return {SupplicantStatusCode::SUCCESS, ""};
@@ -1831,7 +1853,7 @@
 }
 
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/p2p_iface.h b/wpa_supplicant/hidl/1.3/p2p_iface.h
similarity index 98%
rename from wpa_supplicant/hidl/1.2/p2p_iface.h
rename to wpa_supplicant/hidl/1.3/p2p_iface.h
index bd43a5a..608dbd4 100644
--- a/wpa_supplicant/hidl/1.2/p2p_iface.h
+++ b/wpa_supplicant/hidl/1.3/p2p_iface.h
@@ -34,10 +34,13 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
-using namespace android::hardware::wifi::supplicant::V1_0;
-using namespace android::hardware::wifi::supplicant::V1_1;
+using V1_0::SupplicantNetworkId;
+using V1_0::SupplicantStatus;
+using V1_0::IfaceType;
+using V1_0::ISupplicantP2pIfaceCallback;
+using V1_0::ISupplicantP2pNetwork;
 
 /**
  * Implementation of P2pIface hidl object. Each unique hidl
@@ -318,7 +321,7 @@
 };
 
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/p2p_network.cpp b/wpa_supplicant/hidl/1.3/p2p_network.cpp
similarity index 98%
rename from wpa_supplicant/hidl/1.2/p2p_network.cpp
rename to wpa_supplicant/hidl/1.3/p2p_network.cpp
index 693b2c0..c87e4c0 100644
--- a/wpa_supplicant/hidl/1.2/p2p_network.cpp
+++ b/wpa_supplicant/hidl/1.3/p2p_network.cpp
@@ -20,9 +20,10 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 using hidl_return_util::validateAndCall;
+using V1_0::SupplicantStatusCode;
 
 P2pNetwork::P2pNetwork(
     struct wpa_global *wpa_global, const char ifname[], int network_id)
@@ -180,7 +181,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	return {{SupplicantStatusCode::SUCCESS, ""},
-		(wpa_ssid->mode == wpa_ssid::wpas_mode::WPAS_MODE_P2P_GO)};
+		(wpa_ssid->mode == wpas_mode::WPAS_MODE_P2P_GO)};
 }
 
 SupplicantStatus P2pNetwork::setClientListInternal(
@@ -249,7 +250,7 @@
 	    (struct wpa_global *)wpa_global_, ifname_.c_str());
 }
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/p2p_network.h b/wpa_supplicant/hidl/1.3/p2p_network.h
similarity index 95%
rename from wpa_supplicant/hidl/1.2/p2p_network.h
rename to wpa_supplicant/hidl/1.3/p2p_network.h
index e2e8ec2..8c134b0 100644
--- a/wpa_supplicant/hidl/1.2/p2p_network.h
+++ b/wpa_supplicant/hidl/1.3/p2p_network.h
@@ -26,10 +26,10 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
-using namespace android::hardware::wifi::supplicant::V1_0;
-using namespace android::hardware::wifi::supplicant::V1_1;
+using V1_0::ISupplicantP2pNetwork;
+using V1_0::ISupplicantP2pNetworkCallback;
 
 /**
  * Implementation of P2pNetwork hidl object. Each unique hidl
@@ -96,7 +96,7 @@
 };
 
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/sta_iface.cpp b/wpa_supplicant/hidl/1.3/sta_iface.cpp
similarity index 83%
rename from wpa_supplicant/hidl/1.2/sta_iface.cpp
rename to wpa_supplicant/hidl/1.3/sta_iface.cpp
index 1ca440e..b738ff7 100644
--- a/wpa_supplicant/hidl/1.2/sta_iface.cpp
+++ b/wpa_supplicant/hidl/1.3/sta_iface.cpp
@@ -20,15 +20,25 @@
 #include "interworking.h"
 #include "hs20_supplicant.h"
 #include "wps_supplicant.h"
-#include "dpp_supplicant.h"
 #include "dpp.h"
+#include "dpp_supplicant.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
 }
 
 namespace {
+using ISupplicantStaNetworkV1_2 =
+	android::hardware::wifi::supplicant::V1_2::ISupplicantStaNetwork;
+using ISupplicantStaNetworkV1_3 =
+	android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork;
+using android::hardware::wifi::V1_0::WifiChannelWidthInMhz;
 using android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
 using android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode;
-using android::hardware::wifi::supplicant::V1_2::ISupplicantStaIface;
-using android::hardware::wifi::supplicant::V1_2::implementation::HidlManager;
+using android::hardware::wifi::supplicant::V1_0::ISupplicantStaNetwork;
+using android::hardware::wifi::supplicant::V1_3::ISupplicantStaIface;
+using android::hardware::wifi::supplicant::V1_3::ConnectionCapabilities;
+using android::hardware::wifi::supplicant::V1_3::WifiTechnology;
+using android::hardware::wifi::supplicant::V1_3::implementation::HidlManager;
 
 constexpr uint32_t kMaxAnqpElems = 100;
 constexpr char kGetMacAddress[] = "MACADDR";
@@ -152,18 +162,70 @@
 	startExtRadioWork(work);
 }
 
+uint32_t convertWpaKeyMgmtCapabilitiesToHidl (
+    struct wpa_supplicant *wpa_s, struct wpa_driver_capa *capa) {
+
+	uint32_t mask = 0;
+	/* Logic from ctrl_iface.c, NONE and IEEE8021X have no capability
+	 * flags and always enabled.
+	 */
+	mask |=
+	    (ISupplicantStaNetwork::KeyMgmtMask::NONE |
+	     ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X);
+
+	if (capa->key_mgmt &
+	    (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+		mask |= ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP;
+	}
+
+	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+			     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+		mask |= ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK;
+	}
+#ifdef CONFIG_SUITEB192
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
+		mask |= ISupplicantStaNetworkV1_2::ISupplicantStaNetwork::KeyMgmtMask::SUITE_B_192;
+	}
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_OWE
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
+		mask |= ISupplicantStaNetworkV1_2::ISupplicantStaNetwork::KeyMgmtMask::OWE;
+	}
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_SAE
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
+		mask |= ISupplicantStaNetworkV1_2::ISupplicantStaNetwork::KeyMgmtMask::SAE;
+	}
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_DPP
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
+		mask |= ISupplicantStaNetworkV1_2::ISupplicantStaNetwork::KeyMgmtMask::DPP;
+	}
+#endif
+#ifdef CONFIG_WAPI_INTERFACE
+	mask |= ISupplicantStaNetworkV1_3::ISupplicantStaNetwork::KeyMgmtMask::WAPI_PSK;
+	mask |= ISupplicantStaNetworkV1_3::ISupplicantStaNetwork::KeyMgmtMask::WAPI_CERT;
+#endif /* CONFIG_WAPI_INTERFACE */
+#ifdef CONFIG_FILS
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
+		mask |= ISupplicantStaNetworkV1_3::ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA256;
+	}
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
+		mask |= ISupplicantStaNetworkV1_3::ISupplicantStaNetwork::KeyMgmtMask::FILS_SHA384;
+	}
+#endif /* CONFIG_FILS */
+	return mask;
+}
+
 }  // namespace
 
 namespace android {
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 using hidl_return_util::validateAndCall;
-
-using namespace android::hardware::wifi::supplicant::V1_0;
-using namespace android::hardware::wifi::supplicant::V1_1;
 using V1_0::ISupplicantStaIfaceCallback;
 
 StaIface::StaIface(struct wpa_global *wpa_global, const char ifname[])
@@ -205,6 +267,22 @@
 	    &StaIface::removeNetworkInternal, _hidl_cb, id);
 }
 
+Return<void> StaIface::filsHlpFlushRequest(filsHlpFlushRequest_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::filsHlpFlushRequestInternal, _hidl_cb);
+}
+
+Return<void> StaIface::filsHlpAddRequest(
+    const hidl_array<uint8_t, 6> &dst_mac, const hidl_vec<uint8_t> &pkt,
+    filsHlpAddRequest_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::filsHlpAddRequestInternal, _hidl_cb, dst_mac, pkt);
+}
+
 Return<void> StaIface::getNetwork(
     SupplicantNetworkId id, getNetwork_cb _hidl_cb)
 {
@@ -249,6 +327,16 @@
 	    &StaIface::registerCallbackInternal, _hidl_cb, callback_1_1);
 }
 
+Return<void> StaIface::registerCallback_1_3(
+    const sp<V1_3::ISupplicantStaIfaceCallback> &callback,
+    registerCallback_cb _hidl_cb)
+{
+	sp<V1_3::ISupplicantStaIfaceCallback> callback_1_3 = callback;
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::registerCallbackInternal, _hidl_cb, callback_1_3);
+}
+
 Return<void> StaIface::reassociate(reassociate_cb _hidl_cb)
 {
 	return validateAndCall(
@@ -575,6 +663,30 @@
 	    &StaIface::stopDppInitiatorInternal, _hidl_cb);
 }
 
+Return<void> StaIface::getWpaDriverCapabilities(
+		getWpaDriverCapabilities_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_UNKNOWN,
+	    &StaIface::getWpaDriverCapabilitiesInternal, _hidl_cb);
+}
+
+Return<void> StaIface::setMboCellularDataStatus(bool available,
+		setMboCellularDataStatus_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_UNKNOWN,
+	    &StaIface::setMboCellularDataStatusInternal, _hidl_cb, available);
+}
+
+Return<void> StaIface::getKeyMgmtCapabilities_1_3(
+    getKeyMgmtCapabilities_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaIface::getKeyMgmtCapabilitiesInternal_1_3, _hidl_cb);
+}
+
 std::pair<SupplicantStatus, std::string> StaIface::getNameInternal()
 {
 	return {{SupplicantStatusCode::SUCCESS, ""}, ifname_};
@@ -585,6 +697,48 @@
 	return {{SupplicantStatusCode::SUCCESS, ""}, IfaceType::STA};
 }
 
+SupplicantStatus StaIface::filsHlpFlushRequestInternal()
+{
+#ifdef CONFIG_FILS
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+
+	wpas_flush_fils_hlp_req(wpa_s);
+	return {SupplicantStatusCode::SUCCESS, ""};
+#else /* CONFIG_FILS */
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+#endif /* CONFIG_FILS */
+}
+
+SupplicantStatus StaIface::filsHlpAddRequestInternal(
+    const std::array<uint8_t, 6> &dst_mac, const std::vector<uint8_t> &pkt)
+{
+#ifdef CONFIG_FILS
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct fils_hlp_req *req;
+
+	if (!pkt.size())
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+
+
+	req = (struct fils_hlp_req *)os_zalloc(sizeof(*req));
+	if (!req)
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+
+	os_memcpy(req->dst, dst_mac.data(), ETH_ALEN);
+
+	req->pkt = wpabuf_alloc_copy(pkt.data(), pkt.size());
+	if (!req->pkt) {
+		os_free(req);
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+
+	dl_list_add_tail(&wpa_s->fils_hlp_req, &req->list);
+	return {SupplicantStatusCode::SUCCESS, ""};
+#else /* CONFIG_FILS */
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+#endif /* CONFIG_FILS */
+}
+
 std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
 StaIface::addNetworkInternal()
 {
@@ -603,6 +757,14 @@
 	return {{SupplicantStatusCode::SUCCESS, ""}, network};
 }
 
+Return<void> StaIface::getConnectionCapabilities(
+    getConnectionCapabilities_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_UNKNOWN,
+	    &StaIface::getConnectionCapabilitiesInternal, _hidl_cb);
+}
+
 SupplicantStatus StaIface::removeNetworkInternal(SupplicantNetworkId id)
 {
 	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
@@ -1083,52 +1245,7 @@
 std::pair<SupplicantStatus, uint32_t>
 StaIface::getKeyMgmtCapabilitiesInternal()
 {
-	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
-	struct wpa_driver_capa capa;
-	uint32_t mask = 0;
-
-	/* Get capabilities from driver and populate the key management mask */
-	if (wpa_drv_get_capa(wpa_s, &capa) < 0) {
-		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, mask};
-	}
-
-	/* Logic from ctrl_iface.c, NONE and IEEE8021X have no capability
-	 * flags and always enabled.
-	 */
-	mask |=
-	    (ISupplicantStaNetwork::KeyMgmtMask::NONE |
-	     ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X);
-
-	if (capa.key_mgmt &
-	    (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
-		mask |= ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP;
-	}
-
-	if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-			     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-		mask |= ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK;
-	}
-#ifdef CONFIG_SUITEB192
-	if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
-		mask |= ISupplicantStaNetwork::KeyMgmtMask::SUITE_B_192;
-	}
-#endif /* CONFIG_SUITEB192 */
-#ifdef CONFIG_OWE
-	if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
-		mask |= ISupplicantStaNetwork::KeyMgmtMask::OWE;
-	}
-#endif /* CONFIG_OWE */
-#ifdef CONFIG_SAE
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
-		mask |= ISupplicantStaNetwork::KeyMgmtMask::SAE;
-	}
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_DPP
-	if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
-		mask |= ISupplicantStaNetwork::KeyMgmtMask::DPP;
-	}
-#endif
-	return {{SupplicantStatusCode::SUCCESS, ""}, mask};
+	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
 }
 
 std::pair<SupplicantStatus, uint32_t>
@@ -1253,6 +1370,11 @@
 	cmd += " conf=";
 	cmd += role;
 
+	if (net_role == DppNetRole::STA) {
+		/* DPP R2 connection status request */
+		cmd += " conn_status=1";
+	}
+
 	wpa_printf(MSG_DEBUG,
 		   "DPP initiator command: %s", cmd.c_str());
 
@@ -1299,6 +1421,109 @@
 #endif
 }
 
+std::pair<SupplicantStatus, ConnectionCapabilities>
+StaIface::getConnectionCapabilitiesInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct ConnectionCapabilities capa;
+
+	if (wpa_s->connection_set) {
+		if (wpa_s->connection_he) {
+			capa.technology = WifiTechnology::HE;
+		} else if (wpa_s->connection_vht) {
+			capa.technology = WifiTechnology::VHT;
+		} else if (wpa_s->connection_ht) {
+			capa.technology = WifiTechnology::HT;
+		} else {
+			capa.technology = WifiTechnology::LEGACY;
+		}
+		switch (wpa_s->connection_channel_bandwidth) {
+		case CHAN_WIDTH_20:
+			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
+			break;
+		case CHAN_WIDTH_40:
+			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_40;
+			break;
+		case CHAN_WIDTH_80:
+			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_80;
+			break;
+		case CHAN_WIDTH_160:
+			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_160;
+			break;
+		case CHAN_WIDTH_80P80:
+			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_80P80;
+			break;
+		default:
+			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
+			break;
+		}
+		capa.maxNumberRxSpatialStreams = wpa_s->connection_max_nss_rx;
+		capa.maxNumberTxSpatialStreams = wpa_s->connection_max_nss_tx;
+	} else {
+		capa.technology = WifiTechnology::UNKNOWN;
+		capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
+		capa.maxNumberTxSpatialStreams = 1;
+		capa.maxNumberRxSpatialStreams = 1;
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, capa};
+}
+
+std::pair<SupplicantStatus, uint32_t>
+StaIface::getWpaDriverCapabilitiesInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	uint32_t mask = 0;
+
+#ifdef CONFIG_MBO
+	/* MBO has no capability flags. It's mainly legacy 802.11v BSS
+	 * transition + Cellular steering. 11v is a default feature in
+	 * supplicant. And cellular steering is handled in framework.
+	 */
+	mask |= WpaDriverCapabilitiesMask::MBO;
+	if (wpa_s->enable_oce & OCE_STA) {
+		mask |= WpaDriverCapabilitiesMask::OCE;
+	}
+#endif
+
+	wpa_printf(MSG_DEBUG, "Driver capability mask: 0x%x", mask);
+
+	return {{SupplicantStatusCode::SUCCESS, ""}, mask};
+}
+
+SupplicantStatus StaIface::setMboCellularDataStatusInternal(bool available)
+{
+#ifdef CONFIG_MBO
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	enum mbo_cellular_capa mbo_cell_capa;
+
+	if (available) {
+		mbo_cell_capa = MBO_CELL_CAPA_AVAILABLE;
+	} else {
+		mbo_cell_capa = MBO_CELL_CAPA_NOT_AVAILABLE;
+	}
+	wpas_mbo_update_cell_capa(wpa_s, mbo_cell_capa);
+	return {SupplicantStatusCode::SUCCESS, ""};
+#else
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+#endif
+}
+
+std::pair<SupplicantStatus, uint32_t>
+StaIface::getKeyMgmtCapabilitiesInternal_1_3()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_driver_capa capa;
+	uint32_t mask = 0;
+
+	/* Get capabilities from driver and populate the key management mask */
+	if (wpa_drv_get_capa(wpa_s, &capa) < 0) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, mask};
+	}
+
+	return {{SupplicantStatusCode::SUCCESS, ""},
+	    convertWpaKeyMgmtCapabilitiesToHidl(wpa_s, &capa)};
+}
+
 /**
  * Retrieve the underlying |wpa_supplicant| struct
  * pointer for this iface.
@@ -1310,7 +1535,7 @@
 	return wpa_supplicant_get_iface(wpa_global_, ifname_.c_str());
 }
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/sta_iface.h b/wpa_supplicant/hidl/1.3/sta_iface.h
similarity index 87%
rename from wpa_supplicant/hidl/1.2/sta_iface.h
rename to wpa_supplicant/hidl/1.3/sta_iface.h
index 5a04ee3..ba06e5a 100644
--- a/wpa_supplicant/hidl/1.2/sta_iface.h
+++ b/wpa_supplicant/hidl/1.3/sta_iface.h
@@ -15,9 +15,9 @@
 
 #include <android-base/macros.h>
 
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantStaIface.h>
+#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaIface.h>
 #include <android/hardware/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.h>
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantStaNetwork.h>
+#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
 
 extern "C"
 {
@@ -33,16 +33,18 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
-using namespace android::hardware::wifi::supplicant::V1_2;
+using V1_0::ISupplicantNetwork;
+using android::hardware::wifi::supplicant::V1_2::DppAkm;
+using android::hardware::wifi::supplicant::V1_2::DppNetRole;
 
 /**
  * Implementation of StaIface hidl object. Each unique hidl
  * object is used for control operations on a specific interface
  * controlled by wpa_supplicant.
  */
-class StaIface : public V1_2::ISupplicantStaIface
+class StaIface : public V1_3::ISupplicantStaIface
 {
 public:
 	StaIface(struct wpa_global* wpa_global, const char ifname[]);
@@ -68,6 +70,11 @@
 	Return<void> addNetwork(addNetwork_cb _hidl_cb) override;
 	Return<void> removeNetwork(
 	    SupplicantNetworkId id, removeNetwork_cb _hidl_cb) override;
+	Return<void> filsHlpFlushRequest(
+	    filsHlpFlushRequest_cb _hidl_cb) override;
+	Return<void> filsHlpAddRequest(
+	    const hidl_array<uint8_t, 6>& dst_mac, const hidl_vec<uint8_t>& pkt,
+	    filsHlpAddRequest_cb _hidl_cb) override;
 	Return<void> getNetwork(
 	    SupplicantNetworkId id, getNetwork_cb _hidl_cb) override;
 	Return<void> listNetworks(listNetworks_cb _hidl_cb) override;
@@ -80,6 +87,9 @@
 	Return<void> registerCallback_1_2(
 	    const sp<V1_2::ISupplicantStaIfaceCallback>& callback,
 	    registerCallback_cb _hidl_cb) override;
+	Return<void> registerCallback_1_3(
+	    const sp<V1_3::ISupplicantStaIfaceCallback>& callback,
+	    registerCallback_cb _hidl_cb) override;
 	Return<void> reassociate(reassociate_cb _hidl_cb) override;
 	Return<void> reconnect(reconnect_cb _hidl_cb) override;
 	Return<void> disconnect(disconnect_cb _hidl_cb) override;
@@ -177,6 +187,14 @@
 			uint32_t own_bootstrap_id,
 			startDppConfiguratorInitiator_cb _hidl_cb) override;
 	Return<void> stopDppInitiator(stopDppInitiator_cb _hidl_cb) override;
+	Return<void> getConnectionCapabilities(
+	    getConnectionCapabilities_cb _hidl_cb) override;
+	Return<void> getWpaDriverCapabilities(
+	    getWpaDriverCapabilities_cb _hidl_cb) override;
+	Return<void> setMboCellularDataStatus(bool available,
+	    setMboCellularDataStatus_cb _hidl_cb) override;
+	Return<void> getKeyMgmtCapabilities_1_3(
+	    getKeyMgmtCapabilities_1_3_cb _hidl_cb) override;
 
 private:
 	// Corresponding worker functions for the HIDL methods.
@@ -184,6 +202,10 @@
 	std::pair<SupplicantStatus, IfaceType> getTypeInternal();
 	std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
 	addNetworkInternal();
+	SupplicantStatus filsHlpFlushRequestInternal();
+	SupplicantStatus filsHlpAddRequestInternal(
+	    const std::array<uint8_t, 6>& dst_mac,
+	    const std::vector<uint8_t>& pkt);
 	SupplicantStatus removeNetworkInternal(SupplicantNetworkId id);
 	std::pair<SupplicantStatus, sp<ISupplicantNetwork>> getNetworkInternal(
 	    SupplicantNetworkId id);
@@ -260,7 +282,10 @@
 	SupplicantStatus startDppEnrolleeInitiatorInternal(uint32_t peer_bootstrap_id,
 			uint32_t own_bootstrap_id);
 	SupplicantStatus stopDppInitiatorInternal();
-
+	std::pair<SupplicantStatus, ConnectionCapabilities> getConnectionCapabilitiesInternal();
+	std::pair<SupplicantStatus, uint32_t> getWpaDriverCapabilitiesInternal();
+	SupplicantStatus setMboCellularDataStatusInternal(bool available);
+	std::pair<SupplicantStatus, uint32_t> getKeyMgmtCapabilitiesInternal_1_3();
 
 	struct wpa_supplicant* retrieveIfacePtr();
 
@@ -275,7 +300,7 @@
 };
 
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/sta_network.cpp b/wpa_supplicant/hidl/1.3/sta_network.cpp
similarity index 86%
rename from wpa_supplicant/hidl/1.2/sta_network.cpp
rename to wpa_supplicant/hidl/1.3/sta_network.cpp
index 4d694f7..4e492b5 100644
--- a/wpa_supplicant/hidl/1.2/sta_network.cpp
+++ b/wpa_supplicant/hidl/1.3/sta_network.cpp
@@ -19,8 +19,9 @@
 
 namespace {
 using android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
-using android::hardware::wifi::supplicant::V1_2::ISupplicantStaNetwork;
-using namespace android::hardware::wifi::supplicant::V1_2;
+using android::hardware::wifi::supplicant::V1_0::ISupplicantStaNetwork;
+using ISupplicantStaNetworkV1_2 = android::hardware::wifi::supplicant::V1_2::ISupplicantStaNetwork;
+using ISupplicantStaNetworkV1_3 = android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork;
 
 constexpr uint8_t kZeroBssid[6] = {0, 0, 0, 0, 0, 0};
 
@@ -32,19 +33,25 @@
      static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_EAP) |
      static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_PSK) |
      static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::OSEN) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::SAE) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::SUITE_B_192) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::OWE) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK_SHA256) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP_SHA256));
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_2::KeyMgmtMask::SAE) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_2::KeyMgmtMask::SUITE_B_192) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_2::KeyMgmtMask::OWE) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_2::KeyMgmtMask::WPA_PSK_SHA256) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_2::KeyMgmtMask::WPA_EAP_SHA256) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_3::KeyMgmtMask::WAPI_PSK) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_3::KeyMgmtMask::WAPI_CERT) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_3::KeyMgmtMask::FILS_SHA256) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_3::KeyMgmtMask::FILS_SHA384));
 constexpr uint32_t kAllowedProtoMask =
     (static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::WPA) |
      static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::RSN) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::OSEN));
+     static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::OSEN) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_3::ProtoMask::WAPI));
 constexpr uint32_t kAllowedAuthAlgMask =
     (static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::OPEN) |
      static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::SHARED) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::LEAP));
+     static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::LEAP) |
+	 static_cast<uint32_t>(ISupplicantStaNetworkV1_3::AuthAlgMask::SAE));
 constexpr uint32_t kAllowedGroupCipherMask =
     (static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP40) |
      static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP104) |
@@ -52,20 +59,23 @@
      static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::CCMP) |
      static_cast<uint32_t>(
 	 ISupplicantStaNetwork::GroupCipherMask::GTK_NOT_USED) |
-     static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::GCMP_256));
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_2::GroupCipherMask::GCMP_256) |
+     static_cast<uint32_t>(ISupplicantStaNetworkV1_3::GroupCipherMask::SMS4));
 constexpr uint32_t kAllowedPairwisewCipherMask =
     (static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::NONE) |
      static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::TKIP) |
      static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::CCMP) |
      static_cast<uint32_t>(
-	 ISupplicantStaNetwork::PairwiseCipherMask::GCMP_256));
+	 ISupplicantStaNetworkV1_2::PairwiseCipherMask::GCMP_256) |
+     static_cast<uint32_t>(
+	 ISupplicantStaNetworkV1_3::PairwiseCipherMask::SMS4));
 constexpr uint32_t kAllowedGroupMgmtCipherMask =
 	(static_cast<uint32_t>(
-			ISupplicantStaNetwork::GroupMgmtCipherMask::BIP_GMAC_128) |
+			ISupplicantStaNetworkV1_2::GroupMgmtCipherMask::BIP_GMAC_128) |
 	 static_cast<uint32_t>(
-			 ISupplicantStaNetwork::GroupMgmtCipherMask::BIP_GMAC_256) |
+			 ISupplicantStaNetworkV1_2::GroupMgmtCipherMask::BIP_GMAC_256) |
 	 static_cast<uint32_t>(
-			 ISupplicantStaNetwork::GroupMgmtCipherMask::BIP_CMAC_256));
+			 ISupplicantStaNetworkV1_2::GroupMgmtCipherMask::BIP_CMAC_256));
 
 constexpr uint32_t kEapMethodMax =
     static_cast<uint32_t>(ISupplicantStaNetwork::EapMethod::WFA_UNAUTH_TLS) + 1;
@@ -83,15 +93,20 @@
 constexpr char kNetworkEapSimUmtsAutsResponse[] = "UMTS-AUTS";
 constexpr char kNetworkEapSimGsmAuthFailure[] = "GSM-FAIL";
 constexpr char kNetworkEapSimUmtsAuthFailure[] = "UMTS-FAIL";
+
+#ifdef CONFIG_WAPI_INTERFACE
+std::string dummyWapiCertSuite;
+#endif /* CONFIG_WAPI_INTERFACE */
 }  // namespace
 
 namespace android {
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 using hidl_return_util::validateAndCall;
+using V1_0::SupplicantStatusCode;
 
 StaNetwork::StaNetwork(
     struct wpa_global *wpa_global, const char ifname[], int network_id)
@@ -386,6 +401,13 @@
 	    &StaNetwork::setUpdateIdentifierInternal, _hidl_cb, id);
 }
 
+Return<void> StaNetwork::setWapiCertSuite(
+    const hidl_string& suite, setWapiCertSuite_cb _hidl_cb) {
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setWapiCertSuiteInternal, _hidl_cb, suite);
+}
+
 Return<void> StaNetwork::getSsid(getSsid_cb _hidl_cb)
 {
 	return validateAndCall(
@@ -607,6 +629,13 @@
 	    &StaNetwork::getWpsNfcConfigurationTokenInternal, _hidl_cb);
 }
 
+Return<void> StaNetwork::getWapiCertSuite(getWapiCertSuite_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getWapiCertSuiteInternal, _hidl_cb);
+}
+
 Return<void> StaNetwork::enable(bool no_connect, enable_cb _hidl_cb)
 {
 	return validateAndCall(
@@ -791,6 +820,113 @@
 	    &StaNetwork::setSaePasswordIdInternal, _hidl_cb, sae_password_id);
 }
 
+Return<void> StaNetwork::setOcsp(
+    OcspType ocspType, setOcsp_cb _hidl_cb) {
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setOcspInternal, _hidl_cb, ocspType);
+}
+
+Return<void> StaNetwork::getOcsp(
+    getOcsp_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getOcspInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::setPmkCache(const hidl_vec<uint8_t> &serializedEntry,
+    setPmkCache_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setPmkCacheInternal, _hidl_cb, serializedEntry);
+}
+
+Return<void> StaNetwork::setKeyMgmt_1_3(
+    uint32_t key_mgmt_mask, setKeyMgmt_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setKeyMgmt_1_3Internal, _hidl_cb, key_mgmt_mask);
+}
+
+Return<void> StaNetwork::getKeyMgmt_1_3(getKeyMgmt_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getKeyMgmt_1_3Internal, _hidl_cb);
+}
+
+Return<void> StaNetwork::setProto_1_3(uint32_t proto_mask, setProto_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setProto_1_3Internal, _hidl_cb, proto_mask);
+}
+
+Return<void> StaNetwork::getProto_1_3(getProto_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getProto_1_3Internal, _hidl_cb);
+}
+
+Return<void> StaNetwork::setGroupCipher_1_3(
+    uint32_t group_cipher_mask, setGroupCipher_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setGroupCipher_1_3Internal, _hidl_cb, group_cipher_mask);
+}
+
+Return<void> StaNetwork::getGroupCipher_1_3(getGroupCipher_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getGroupCipher_1_3Internal, _hidl_cb);
+}
+
+Return<void> StaNetwork::setPairwiseCipher_1_3(
+    uint32_t pairwise_cipher_mask, setPairwiseCipher_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setPairwiseCipher_1_3Internal, _hidl_cb,
+	    pairwise_cipher_mask);
+}
+
+Return<void> StaNetwork::getPairwiseCipher_1_3(
+    getPairwiseCipher_1_3_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getPairwiseCipher_1_3Internal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getAuthAlg_1_3(getAuthAlg_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getAuthAlgInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::setAuthAlg_1_3(
+    uint32_t auth_alg_mask,
+    std::function<void(const SupplicantStatus &status)> _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setAuthAlgInternal, _hidl_cb, auth_alg_mask);
+}
+
+Return<void> StaNetwork::setEapErp(bool enable, setEapErp_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapErpInternal, _hidl_cb, enable);
+}
+
 std::pair<SupplicantStatus, uint32_t> StaNetwork::getIdInternal()
 {
 	return {{SupplicantStatusCode::SUCCESS, ""}, network_id_};
@@ -871,27 +1007,12 @@
 
 SupplicantStatus StaNetwork::setKeyMgmtInternal(uint32_t key_mgmt_mask)
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (key_mgmt_mask & ~kAllowedKeyMgmtMask) {
-		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
-	}
-	setFastTransitionKeyMgmt(key_mgmt_mask);
-	wpa_ssid->key_mgmt = key_mgmt_mask;
-	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", wpa_ssid->key_mgmt);
-	resetInternalStateAfterParamsUpdate();
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
 }
 
 SupplicantStatus StaNetwork::setProtoInternal(uint32_t proto_mask)
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (proto_mask & ~kAllowedProtoMask) {
-		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
-	}
-	wpa_ssid->proto = proto_mask;
-	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", wpa_ssid->proto);
-	resetInternalStateAfterParamsUpdate();
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
 }
 
 SupplicantStatus StaNetwork::setAuthAlgInternal(uint32_t auth_alg_mask)
@@ -908,28 +1029,13 @@
 
 SupplicantStatus StaNetwork::setGroupCipherInternal(uint32_t group_cipher_mask)
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (group_cipher_mask & ~kAllowedGroupCipherMask) {
-		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
-	}
-	wpa_ssid->group_cipher = group_cipher_mask;
-	wpa_printf(MSG_MSGDUMP, "group_cipher: 0x%x", wpa_ssid->group_cipher);
-	resetInternalStateAfterParamsUpdate();
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
 }
 
 SupplicantStatus StaNetwork::setPairwiseCipherInternal(
     uint32_t pairwise_cipher_mask)
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (pairwise_cipher_mask & ~kAllowedPairwisewCipherMask) {
-		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
-	}
-	wpa_ssid->pairwise_cipher = pairwise_cipher_mask;
-	wpa_printf(
-	    MSG_MSGDUMP, "pairwise_cipher: 0x%x", wpa_ssid->pairwise_cipher);
-	resetInternalStateAfterParamsUpdate();
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
 }
 
 SupplicantStatus StaNetwork::setPskPassphraseInternal(const std::string &psk)
@@ -1174,7 +1280,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		path.c_str(), &(wpa_ssid->eap.ca_cert), "eap ca_cert")) {
+		path.c_str(), &(wpa_ssid->eap.cert.ca_cert), "eap ca_cert")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
 	return {SupplicantStatusCode::SUCCESS, ""};
@@ -1184,7 +1290,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		path.c_str(), &(wpa_ssid->eap.ca_path), "eap ca_path")) {
+		path.c_str(), &(wpa_ssid->eap.cert.ca_path), "eap ca_path")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
 	return {SupplicantStatusCode::SUCCESS, ""};
@@ -1194,7 +1300,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		path.c_str(), &(wpa_ssid->eap.client_cert),
+		path.c_str(), &(wpa_ssid->eap.cert.client_cert),
 		"eap client_cert")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
@@ -1205,7 +1311,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		id.c_str(), &(wpa_ssid->eap.key_id), "eap key_id")) {
+		id.c_str(), &(wpa_ssid->eap.cert.key_id), "eap key_id")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
 	return {SupplicantStatusCode::SUCCESS, ""};
@@ -1216,7 +1322,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		match.c_str(), &(wpa_ssid->eap.subject_match),
+		match.c_str(), &(wpa_ssid->eap.cert.subject_match),
 		"eap subject_match")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
@@ -1228,7 +1334,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		match.c_str(), &(wpa_ssid->eap.altsubject_match),
+		match.c_str(), &(wpa_ssid->eap.cert.altsubject_match),
 		"eap altsubject_match")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
@@ -1238,7 +1344,7 @@
 SupplicantStatus StaNetwork::setEapEngineInternal(bool enable)
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	wpa_ssid->eap.engine = enable ? 1 : 0;
+	wpa_ssid->eap.cert.engine = enable ? 1 : 0;
 	return {SupplicantStatusCode::SUCCESS, ""};
 }
 
@@ -1246,7 +1352,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		id.c_str(), &(wpa_ssid->eap.engine_id), "eap engine_id")) {
+		id.c_str(), &(wpa_ssid->eap.cert.engine_id), "eap engine_id")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
 	return {SupplicantStatusCode::SUCCESS, ""};
@@ -1257,7 +1363,7 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (setStringFieldAndResetState(
-		match.c_str(), &(wpa_ssid->eap.domain_suffix_match),
+		match.c_str(), &(wpa_ssid->eap.cert.domain_suffix_match),
 		"eap domain_suffix_match")) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
@@ -1292,6 +1398,16 @@
 	return {SupplicantStatusCode::SUCCESS, ""};
 }
 
+SupplicantStatus StaNetwork::setWapiCertSuiteInternal(const std::string &suite)
+{
+#ifdef CONFIG_WAPI_INTERFACE
+	dummyWapiCertSuite = suite;
+	return {SupplicantStatusCode::SUCCESS, "Dummy implementation"};
+#else
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, "Not implemented"};
+#endif
+}
+
 std::pair<SupplicantStatus, std::vector<uint8_t>> StaNetwork::getSsidInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
@@ -1321,18 +1437,12 @@
 
 std::pair<SupplicantStatus, uint32_t> StaNetwork::getKeyMgmtInternal()
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	uint32_t key_mgmt_mask = wpa_ssid->key_mgmt & kAllowedKeyMgmtMask;
-
-	resetFastTransitionKeyMgmt(key_mgmt_mask);
-	return {{SupplicantStatusCode::SUCCESS, ""}, key_mgmt_mask};
+	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
 }
 
 std::pair<SupplicantStatus, uint32_t> StaNetwork::getProtoInternal()
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	return {{SupplicantStatusCode::SUCCESS, ""},
-		wpa_ssid->proto & kAllowedProtoMask};
+	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
 }
 
 std::pair<SupplicantStatus, uint32_t> StaNetwork::getAuthAlgInternal()
@@ -1344,16 +1454,12 @@
 
 std::pair<SupplicantStatus, uint32_t> StaNetwork::getGroupCipherInternal()
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	return {{SupplicantStatusCode::SUCCESS, ""},
-		wpa_ssid->group_cipher & kAllowedGroupCipherMask};
+	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
 }
 
 std::pair<SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipherInternal()
 {
-	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	return {{SupplicantStatusCode::SUCCESS, ""},
-		wpa_ssid->pairwise_cipher & kAllowedPairwisewCipherMask};
+	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
 }
 
 std::pair<SupplicantStatus, std::string> StaNetwork::getPskPassphraseInternal()
@@ -1438,7 +1544,7 @@
 	// the first EAP method for each network.
 	const std::string eap_method_str = eap_get_name(
 	    wpa_ssid->eap.eap_methods[0].vendor,
-	    static_cast<EapType>(wpa_ssid->eap.eap_methods[0].method));
+	    static_cast<enum eap_type>(wpa_ssid->eap.eap_methods[0].method));
 	size_t eap_method_idx =
 	    std::find(
 		std::begin(kEapMethodStrings), std::end(kEapMethodStrings),
@@ -1532,89 +1638,89 @@
 std::pair<SupplicantStatus, std::string> StaNetwork::getEapCACertInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.ca_cert) {
+	if (!wpa_ssid->eap.cert.ca_cert) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
 	return {{SupplicantStatusCode::SUCCESS, ""},
-		reinterpret_cast<char *>(wpa_ssid->eap.ca_cert)};
+		reinterpret_cast<char *>(wpa_ssid->eap.cert.ca_cert)};
 }
 
 std::pair<SupplicantStatus, std::string> StaNetwork::getEapCAPathInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.ca_path) {
+	if (!wpa_ssid->eap.cert.ca_path) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
 	return {{SupplicantStatusCode::SUCCESS, ""},
-		reinterpret_cast<char *>(wpa_ssid->eap.ca_path)};
+		reinterpret_cast<char *>(wpa_ssid->eap.cert.ca_path)};
 }
 
 std::pair<SupplicantStatus, std::string> StaNetwork::getEapClientCertInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.client_cert) {
+	if (!wpa_ssid->eap.cert.client_cert) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
 	return {{SupplicantStatusCode::SUCCESS, ""},
-		reinterpret_cast<char *>(wpa_ssid->eap.client_cert)};
+		reinterpret_cast<char *>(wpa_ssid->eap.cert.client_cert)};
 }
 
 std::pair<SupplicantStatus, std::string>
 StaNetwork::getEapPrivateKeyIdInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.key_id) {
+	if (!wpa_ssid->eap.cert.key_id) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
-	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->eap.key_id};
+	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->eap.cert.key_id};
 }
 
 std::pair<SupplicantStatus, std::string>
 StaNetwork::getEapSubjectMatchInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.subject_match) {
+	if (!wpa_ssid->eap.cert.subject_match) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
 	return {{SupplicantStatusCode::SUCCESS, ""},
-		reinterpret_cast<char *>(wpa_ssid->eap.subject_match)};
+		reinterpret_cast<char *>(wpa_ssid->eap.cert.subject_match)};
 }
 
 std::pair<SupplicantStatus, std::string>
 StaNetwork::getEapAltSubjectMatchInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.altsubject_match) {
+	if (!wpa_ssid->eap.cert.altsubject_match) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
 	return {{SupplicantStatusCode::SUCCESS, ""},
-		reinterpret_cast<char *>(wpa_ssid->eap.altsubject_match)};
+		reinterpret_cast<char *>(wpa_ssid->eap.cert.altsubject_match)};
 }
 
 std::pair<SupplicantStatus, bool> StaNetwork::getEapEngineInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->eap.engine == 1};
+	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->eap.cert.engine == 1};
 }
 
 std::pair<SupplicantStatus, std::string> StaNetwork::getEapEngineIDInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.engine_id) {
+	if (!wpa_ssid->eap.cert.engine_id) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
-	return {{SupplicantStatusCode::SUCCESS, ""}, {wpa_ssid->eap.engine_id}};
+	return {{SupplicantStatusCode::SUCCESS, ""}, {wpa_ssid->eap.cert.engine_id}};
 }
 
 std::pair<SupplicantStatus, std::string>
 StaNetwork::getEapDomainSuffixMatchInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	if (!wpa_ssid->eap.domain_suffix_match) {
+	if (!wpa_ssid->eap.cert.domain_suffix_match) {
 		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
 	}
 	return {{SupplicantStatusCode::SUCCESS, ""},
-		{wpa_ssid->eap.domain_suffix_match}};
+		{wpa_ssid->eap.cert.domain_suffix_match}};
 }
 
 std::pair<SupplicantStatus, std::string> StaNetwork::getIdStrInternal()
@@ -1640,6 +1746,15 @@
 		misc_utils::convertWpaBufToVector(token_buf.get())};
 }
 
+std::pair<SupplicantStatus, std::string> StaNetwork::getWapiCertSuiteInternal()
+{
+#ifdef CONFIG_WAPI_INTERFACE
+	return {{SupplicantStatusCode::SUCCESS, "Dummy implementation"}, dummyWapiCertSuite};
+#else
+	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "Not implemented"}, {}};
+#endif
+}
+
 SupplicantStatus StaNetwork::enableInternal(bool no_connect)
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
@@ -1677,9 +1792,6 @@
 	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
 	wpa_s->scan_min_time.sec = 0;
 	wpa_s->scan_min_time.usec = 0;
-	// Make sure that the supplicant is updated to the latest
-	// MAC address, which might have changed due to MAC randomization.
-	wpa_supplicant_update_mac_addr(wpa_s);
 	wpa_supplicant_select_network(wpa_s, wpa_ssid);
 	return {SupplicantStatusCode::SUCCESS, ""};
 }
@@ -1947,6 +2059,126 @@
 		wpa_ssid->group_mgmt_cipher & kAllowedGroupMgmtCipherMask};
 }
 
+SupplicantStatus StaNetwork::setOcspInternal(OcspType ocspType) {
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (ocspType < OcspType::NONE || ocspType > OcspType::REQUIRE_ALL_CERTS_STATUS) {
+		return{ SupplicantStatusCode::FAILURE_ARGS_INVALID, "" };
+	}
+	wpa_ssid->eap.cert.ocsp = (int) ocspType;
+	wpa_printf(
+	    MSG_MSGDUMP, "ocsp: %d", wpa_ssid->eap.cert.ocsp);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, OcspType> StaNetwork::getOcspInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		(OcspType) wpa_ssid->eap.cert.ocsp};
+}
+
+SupplicantStatus StaNetwork::setPmkCacheInternal(const std::vector<uint8_t>& serializedEntry) {
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	struct rsn_pmksa_cache_entry *new_entry = NULL;
+
+	new_entry = (struct rsn_pmksa_cache_entry *) os_zalloc(sizeof(*new_entry));
+	if (!new_entry) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, "Allocating memory failed"};
+	}
+
+	std::stringstream ss(
+	    std::stringstream::in | std::stringstream::out | std::stringstream::binary);
+	ss.write((char *) serializedEntry.data(), std::streamsize(serializedEntry.size()));
+	misc_utils::deserializePmkCacheEntry(ss, new_entry);
+	new_entry->network_ctx = wpa_ssid;
+	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, new_entry);
+
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setKeyMgmt_1_3Internal(uint32_t key_mgmt_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (key_mgmt_mask & ~kAllowedKeyMgmtMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	setFastTransitionKeyMgmt(key_mgmt_mask);
+	wpa_ssid->key_mgmt = key_mgmt_mask;
+	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", wpa_ssid->key_mgmt);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getKeyMgmt_1_3Internal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	uint32_t key_mgmt_mask = wpa_ssid->key_mgmt & kAllowedKeyMgmtMask;
+
+	resetFastTransitionKeyMgmt(key_mgmt_mask);
+	return {{SupplicantStatusCode::SUCCESS, ""}, key_mgmt_mask};
+}
+
+SupplicantStatus StaNetwork::setProto_1_3Internal(uint32_t proto_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (proto_mask & ~kAllowedProtoMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->proto = proto_mask;
+	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", wpa_ssid->proto);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getProto_1_3Internal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->proto & kAllowedProtoMask};
+}
+
+SupplicantStatus StaNetwork::setGroupCipher_1_3Internal(uint32_t group_cipher_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (group_cipher_mask & ~kAllowedGroupCipherMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->group_cipher = group_cipher_mask;
+	wpa_printf(MSG_MSGDUMP, "group_cipher: 0x%x", wpa_ssid->group_cipher);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getGroupCipher_1_3Internal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->group_cipher & kAllowedGroupCipherMask};
+}
+
+SupplicantStatus StaNetwork::setPairwiseCipher_1_3Internal(
+    uint32_t pairwise_cipher_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (pairwise_cipher_mask & ~kAllowedPairwisewCipherMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->pairwise_cipher = pairwise_cipher_mask;
+	wpa_printf(
+	    MSG_MSGDUMP, "pairwise_cipher: 0x%x", wpa_ssid->pairwise_cipher);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipher_1_3Internal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->pairwise_cipher & kAllowedPairwisewCipherMask};
+}
+
 /**
  * Retrieve the underlying |wpa_ssid| struct pointer for
  * this network.
@@ -2154,8 +2386,24 @@
 		key_mgmt_mask &= ~WPA_KEY_MGMT_FT_IEEE8021X;
 	}
 }
+
+/**
+ * Helper function to enable erp keys generation while connecting to FILS
+ * enabled APs.
+ */
+SupplicantStatus StaNetwork::setEapErpInternal(bool enable)
+{
+#ifdef CONFIG_FILS
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->eap.erp = enable ? 1 : 0;
+	return {SupplicantStatusCode::SUCCESS, ""};
+#else /* CONFIG_FILS */
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+#endif /* CONFIG_FILS */
+}
+
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/sta_network.h b/wpa_supplicant/hidl/1.3/sta_network.h
similarity index 86%
rename from wpa_supplicant/hidl/1.2/sta_network.h
rename to wpa_supplicant/hidl/1.3/sta_network.h
index 16d065e..2a847a6 100644
--- a/wpa_supplicant/hidl/1.2/sta_network.h
+++ b/wpa_supplicant/hidl/1.3/sta_network.h
@@ -15,7 +15,7 @@
 
 #include <android-base/macros.h>
 
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantStaNetwork.h>
+#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
 
 extern "C"
@@ -34,17 +34,18 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
-using namespace android::hardware::wifi::supplicant::V1_0;
-using namespace android::hardware::wifi::supplicant::V1_1;
+using V1_0::ISupplicantStaNetworkCallback;
+using V1_2::DppFailureCode;
+using V1_2::DppProgressCode;
 
 /**
  * Implementation of StaNetwork hidl object. Each unique hidl
  * object is used for control operations on a specific network
  * controlled by wpa_supplicant.
  */
-class StaNetwork : public V1_2::ISupplicantStaNetwork
+class StaNetwork : public V1_3::ISupplicantStaNetwork
 {
 public:
 	StaNetwork(
@@ -225,6 +226,36 @@
 	Return<void> setSaePasswordId(
 	    const hidl_string& sae_password_id,
 	    setSaePasswordId_cb _hidl_cb) override;
+	Return<void> setOcsp(
+	    OcspType ocspType, setOcsp_cb _hidl_cb) override;
+	Return<void> getOcsp(
+	    getOcsp_cb _hidl_cb) override;
+	Return<void> setPmkCache(const hidl_vec<uint8_t>& serializedEntry,
+			setPmkCache_cb _hidl_cb) override;
+	Return<void> setKeyMgmt_1_3(
+	    uint32_t key_mgmt_mask, setKeyMgmt_1_3_cb _hidl_cb) override;
+	Return<void> getKeyMgmt_1_3(getKeyMgmt_1_3_cb _hidl_cb) override;
+	Return<void> setProto_1_3(
+	    uint32_t proto_mask, setProto_cb _hidl_cb) override;
+	Return<void> getProto_1_3(getProto_cb _hidl_cb) override;
+	Return<void> setPairwiseCipher_1_3(
+	    uint32_t pairwise_cipher_mask,
+	    setPairwiseCipher_1_3_cb _hidl_cb) override;
+	Return<void> getPairwiseCipher_1_3(
+	    getPairwiseCipher_1_3_cb _hidl_cb) override;
+	Return<void> setGroupCipher_1_3(
+	    uint32_t group_cipher_mask,
+	    setGroupCipher_1_3_cb _hidl_cb) override;
+	Return<void> getGroupCipher_1_3(
+	    getGroupCipher_1_3_cb _hidl_cb) override;
+	Return<void> setWapiCertSuite(
+	    const hidl_string& suite, setWapiCertSuite_cb _hidl_cb) override;
+	Return<void> getWapiCertSuite(getWapiCertSuite_cb _hidl_cb) override;
+	Return<void> getAuthAlg_1_3(getAuthAlg_cb _hidl_cb) override;
+	Return<void> setAuthAlg_1_3(uint32_t auth_alg_mask,
+			std::function<void(const SupplicantStatus &status)> _hidl_cb)
+					override;
+	Return<void> setEapErp(bool enable, setEapErp_cb _hidl_cb) override;
 
 private:
 	// Corresponding worker functions for the HIDL methods.
@@ -341,6 +372,20 @@
 	    const std::string& sae_password_id);
 	SupplicantStatus setGroupMgmtCipherInternal(uint32_t group_mgmt_cipher_mask);
 	std::pair<SupplicantStatus, uint32_t> getGroupMgmtCipherInternal();
+	SupplicantStatus setOcspInternal(OcspType ocspType);
+	std::pair<SupplicantStatus, OcspType> getOcspInternal();
+	SupplicantStatus setPmkCacheInternal(const std::vector<uint8_t>& serialziedEntry);
+	SupplicantStatus setWapiCertSuiteInternal(const std::string& suite);
+	std::pair<SupplicantStatus, std::string> getWapiCertSuiteInternal();
+	SupplicantStatus setKeyMgmt_1_3Internal(uint32_t key_mgmt_mask);
+	std::pair<SupplicantStatus, uint32_t> getKeyMgmt_1_3Internal();
+	SupplicantStatus setProto_1_3Internal(uint32_t proto_mask);
+	std::pair<SupplicantStatus, uint32_t> getProto_1_3Internal();
+	std::pair<SupplicantStatus, uint32_t> getGroupCipher_1_3Internal();
+	SupplicantStatus setGroupCipher_1_3Internal(uint32_t group_cipher_mask);
+	std::pair<SupplicantStatus, uint32_t> getPairwiseCipher_1_3Internal();
+	SupplicantStatus setPairwiseCipher_1_3Internal(
+	    uint32_t pairwise_cipher_mask);
 
 	struct wpa_ssid* retrieveNetworkPtr();
 	struct wpa_supplicant* retrieveIfacePtr();
@@ -365,6 +410,7 @@
 	    const char* hexdump_prefix);
 	void setFastTransitionKeyMgmt(uint32_t &key_mgmt_mask);
 	void resetFastTransitionKeyMgmt(uint32_t &key_mgmt_mask);
+	SupplicantStatus setEapErpInternal(bool enable);
 
 	// Reference to the global wpa_struct. This is assumed to be valid
 	// for the lifetime of the process.
@@ -379,7 +425,7 @@
 };
 
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/supplicant.cpp b/wpa_supplicant/hidl/1.3/supplicant.cpp
similarity index 98%
rename from wpa_supplicant/hidl/1.2/supplicant.cpp
rename to wpa_supplicant/hidl/1.3/supplicant.cpp
index 9342ace..50d2343 100644
--- a/wpa_supplicant/hidl/1.2/supplicant.cpp
+++ b/wpa_supplicant/hidl/1.3/supplicant.cpp
@@ -16,7 +16,6 @@
 #include <sys/stat.h>
 
 namespace {
-using namespace android::hardware::wifi::supplicant::V1_2;
 
 // Pre-populated interface params for interfaces controlled by wpa_supplicant.
 // Note: This may differ for other OEM's. So, modify this accordingly.
@@ -157,9 +156,10 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
 using hidl_return_util::validateAndCall;
+using V1_0::SupplicantStatusCode;
 
 Supplicant::Supplicant(struct wpa_global* global) : wpa_global_(global) {}
 bool Supplicant::isValid()
@@ -353,7 +353,7 @@
 		wpa_s->conf->persistent_reconnect = true;
 		return {{SupplicantStatusCode::SUCCESS, ""}, iface};
 	} else {
-		android::sp<ISupplicantStaIface> iface;
+		android::sp<V1_1::ISupplicantStaIface> iface;
 		if (!hidl_manager ||
 		    hidl_manager->getStaIfaceHidlObjectByIfname(
 			wpa_s->ifname, &iface)) {
@@ -417,7 +417,7 @@
 	return SupplicantStatus{SupplicantStatusCode::SUCCESS, ""};
 }
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hidl/1.2/supplicant.h b/wpa_supplicant/hidl/1.3/supplicant.h
similarity index 93%
rename from wpa_supplicant/hidl/1.2/supplicant.h
rename to wpa_supplicant/hidl/1.3/supplicant.h
index 8985854..0c0ac72 100644
--- a/wpa_supplicant/hidl/1.2/supplicant.h
+++ b/wpa_supplicant/hidl/1.3/supplicant.h
@@ -13,7 +13,7 @@
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantIface.h>
 #include <android/hardware/wifi/supplicant/1.0/types.h>
-#include <android/hardware/wifi/supplicant/1.2/ISupplicant.h>
+#include <android/hardware/wifi/supplicant/1.3/ISupplicant.h>
 #include <android-base/macros.h>
 #include <hidl/Status.h>
 
@@ -29,16 +29,17 @@
 namespace hardware {
 namespace wifi {
 namespace supplicant {
-namespace V1_2 {
+namespace V1_3 {
 namespace implementation {
-using namespace android::hardware::wifi::supplicant::V1_0;
+using V1_0::ISupplicantCallback;
+using V1_0::ISupplicantIface;
 
 /**
  * Implementation of the supplicant hidl object. This hidl
  * object is used core for global control operations on
  * wpa_supplicant.
  */
-class Supplicant : public V1_2::ISupplicant
+class Supplicant : public V1_3::ISupplicant
 {
 public:
 	Supplicant(struct wpa_global* global);
@@ -92,7 +93,7 @@
 };
 
 }  // namespace implementation
-}  // namespace V1_2
+}  // namespace V1_3
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index e81fef1..741f925 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -341,7 +341,7 @@
 {
 	struct icon_entry *icon;
 	size_t out_size;
-	unsigned char *b64;
+	char *b64;
 	size_t b64_size;
 	int reply_size;
 
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index e96ea65..36c0aff 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -111,6 +111,7 @@
 
 	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
 	/* TODO: get correct RSN IE */
+	wpa_sm_set_ap_rsnxe(peer->supp, NULL, 0);
 	return wpa_sm_set_ap_rsn_ie(peer->supp,
 				    (u8 *) "\x30\x14\x01\x00"
 				    "\x00\x0f\xac\x04"
@@ -193,7 +194,7 @@
 }
 
 
-static void supp_deauthenticate(void * ctx, int reason_code)
+static void supp_deauthenticate(void * ctx, u16 reason_code)
 {
 	wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
 }
@@ -464,7 +465,7 @@
 				"\x00\x0f\xac\x04"
 				"\x01\x00\x00\x0f\xac\x04"
 				"\x01\x00\x00\x0f\xac\x02"
-				"\x00\x00", 22, NULL, 0, NULL, 0) !=
+				"\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) !=
 	    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 c66a09b..dc51418 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -176,7 +176,7 @@
 			continue;
 		if (!cred->eap_method)
 			return 1;
-		if (cred->realm && cred->roaming_consortium_len == 0)
+		if (cred->realm)
 			return 1;
 	}
 	return 0;
@@ -1388,11 +1388,18 @@
 		    cred->num_roaming_consortiums == 0)
 			continue;
 
+		if (!cred->eap_method)
+			continue;
+
 		if ((cred->roaming_consortium_len == 0 ||
 		     !roaming_consortium_match(ie, anqp,
 					       cred->roaming_consortium,
 					       cred->roaming_consortium_len)) &&
-		    !cred_roaming_consortiums_match(ie, anqp, cred))
+		    !cred_roaming_consortiums_match(ie, anqp, cred) &&
+		    (cred->required_roaming_consortium_len == 0 ||
+		     !roaming_consortium_match(
+			     ie, anqp, cred->required_roaming_consortium,
+			     cred->required_roaming_consortium_len)))
 			continue;
 
 		if (cred_no_required_oi_match(cred, bss))
@@ -1547,7 +1554,7 @@
 				  cred->domain_suffix_match) < 0)
 		return -1;
 
-	ssid->eap.ocsp = cred->ocsp;
+	ssid->eap.cert.ocsp = cred->ocsp;
 
 	return 0;
 }
@@ -2255,7 +2262,7 @@
 			realm++;
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: Search for match with SIM/USIM domain %s",
-			realm);
+			realm ? realm : "[NULL]");
 		if (realm &&
 		    domain_name_list_contains(domain_names, realm, 1))
 			return 1;
@@ -2669,7 +2676,8 @@
 			found++;
 			bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
 			wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
-				MACSTR, MAC2STR(bss->bssid));
+				MACSTR " (HESSID " MACSTR ")",
+				MAC2STR(bss->bssid), MAC2STR(bss->hessid));
 			interworking_anqp_send_req(wpa_s, bss);
 			break;
 		}
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 43b1fa7..8ac73ef 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -15,6 +15,7 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
+#include "rsn_supp/wpa.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
@@ -82,6 +83,35 @@
 }
 
 
+void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			struct wpa_ssid *ssid)
+{
+	const u8 *rsne, *mbo, *oce;
+	struct wpa_ie_data ie;
+
+	wpa_s->disable_mbo_oce = 0;
+	if (!bss)
+		return;
+	mbo = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND);
+	oce = wpas_mbo_get_bss_attr(bss, OCE_ATTR_ID_CAPA_IND);
+	if (!mbo && !oce)
+		return;
+	if (oce && oce[1] >= 1 && (oce[2] & OCE_IS_STA_CFON))
+		return; /* STA-CFON is not required to enable PMF */
+	rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (!rsne || wpa_parse_wpa_ie(rsne, 2 + rsne[1], &ie) < 0)
+		return; /* AP is not using RSN */
+
+	if (!(ie.capabilities & WPA_CAPABILITY_MFPC))
+		wpa_s->disable_mbo_oce = 1; /* AP uses RSN without PMF */
+	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
+		wpa_s->disable_mbo_oce = 1; /* STA uses RSN without PMF */
+	if (wpa_s->disable_mbo_oce)
+		wpa_printf(MSG_INFO,
+			   "MBO: Disable MBO/OCE due to misbehaving AP not having enabled PMF");
+}
+
+
 static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
 					     struct wpabuf *mbo,
 					     u8 start, u8 end)
@@ -434,9 +464,8 @@
 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
 			   size_t len)
 {
-	const u8 *pos, *cell_pref = NULL;
+	const u8 *pos;
 	u8 id, elen;
-	u16 disallowed_sec = 0;
 
 	if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
 	    mbo_ie[3] != MBO_OUI_TYPE)
@@ -459,11 +488,14 @@
 				goto fail;
 
 			if (wpa_s->conf->mbo_cell_capa ==
-			    MBO_CELL_CAPA_AVAILABLE)
-				cell_pref = pos;
-			else
+			    MBO_CELL_CAPA_AVAILABLE) {
+				wpa_s->wnm_mbo_cell_pref_present = 1;
+				wpa_s->wnm_mbo_cell_preference = *pos;
+			} else {
 				wpa_printf(MSG_DEBUG,
-					   "MBO: Station does not support Cellular data connection");
+					   "MBO: Station does not support "
+					   "Cellular data connection");
+			}
 			break;
 		case MBO_ATTR_ID_TRANSITION_REASON:
 			if (elen != 1)
@@ -479,17 +511,20 @@
 			if (wpa_s->wnm_mode &
 			    WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
 				wpa_printf(MSG_DEBUG,
-					   "MBO: Unexpected association retry delay, BSS is terminating");
+					   "MBO: Unexpected association retry delay, "
+					   "BSS is terminating");
 				goto fail;
 			} else if (wpa_s->wnm_mode &
 				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
-				disallowed_sec = WPA_GET_LE16(pos);
+				wpa_s->wnm_mbo_assoc_retry_delay_present = 1;
+				wpa_s->wnm_mbo_assoc_retry_delay_sec = WPA_GET_LE16(pos);
 				wpa_printf(MSG_DEBUG,
 					   "MBO: Association retry delay: %u",
-					   disallowed_sec);
+					   wpa_s->wnm_mbo_assoc_retry_delay_sec);
 			} else {
 				wpa_printf(MSG_DEBUG,
-					   "MBO: Association retry delay attribute not in disassoc imminent mode");
+					   "MBO: Association retry delay attribute "
+					   "not in disassoc imminent mode");
 			}
 
 			break;
@@ -512,17 +547,17 @@
 		len -= elen;
 	}
 
-	if (cell_pref)
+	if (wpa_s->wnm_mbo_cell_pref_present)
 		wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
-			*cell_pref);
+			wpa_s->wnm_mbo_cell_preference);
 
 	if (wpa_s->wnm_mbo_trans_reason_present)
 		wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
 			wpa_s->wnm_mbo_transition_reason);
 
-	if (disallowed_sec && wpa_s->current_bss)
+	if (wpa_s->wnm_mbo_assoc_retry_delay_sec && wpa_s->current_bss)
 		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
-				     disallowed_sec, 0);
+				     wpa_s->wnm_mbo_assoc_retry_delay_sec, 0);
 
 	return;
 fail:
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 9260021..b504124 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -86,7 +86,6 @@
 			MESH_CONF_SEC_AMPE;
 	else
 		conf->security |= MESH_CONF_SEC_NONE;
-#ifdef CONFIG_IEEE80211W
 	conf->ieee80211w = ssid->ieee80211w;
 	if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
 		if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
@@ -94,7 +93,6 @@
 		else
 			conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
 	}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	conf->ocv = ssid->ocv;
 #endif /* CONFIG_OCV */
@@ -116,8 +114,14 @@
 	}
 
 	conf->group_cipher = cipher;
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
-		conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		if (ssid->group_mgmt_cipher == WPA_CIPHER_BIP_GMAC_128 ||
+		    ssid->group_mgmt_cipher == WPA_CIPHER_BIP_GMAC_256 ||
+		    ssid->group_mgmt_cipher == WPA_CIPHER_BIP_CMAC_256)
+			conf->mgmt_group_cipher = ssid->group_mgmt_cipher;
+		else
+			conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+	}
 
 	/* defaults */
 	conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP;
@@ -208,6 +212,7 @@
 		wpa_printf(MSG_ERROR,
 			   "mesh: RSN initialization failed - deinit mesh");
 		wpa_supplicant_mesh_deinit(wpa_s);
+		wpa_drv_leave_mesh(wpa_s);
 		return -1;
 	}
 
@@ -333,14 +338,14 @@
 		if (ssid->max_oper_chwidth != DEFAULT_MAX_OPER_CHWIDTH)
 			conf->vht_oper_chwidth = ssid->max_oper_chwidth;
 		switch (conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_80MHZ:
-		case VHT_CHANWIDTH_80P80MHZ:
+		case CHANWIDTH_80MHZ:
+		case CHANWIDTH_80P80MHZ:
 			ieee80211_freq_to_chan(
 				frequency,
 				&conf->vht_oper_centr_freq_seg0_idx);
 			conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
+		case CHANWIDTH_160MHZ:
 			ieee80211_freq_to_chan(
 				frequency,
 				&conf->vht_oper_centr_freq_seg0_idx);
@@ -455,6 +460,7 @@
 	ibss_mesh_setup_freq(wpa_s, ssid, &params->freq);
 	wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
 	wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
+	wpa_s->mesh_he_enabled = !!params->freq.he_enabled;
 	if (params->freq.ht_enabled && params->freq.sec_channel_offset)
 		ssid->ht40 = params->freq.sec_channel_offset;
 
@@ -464,21 +470,23 @@
 		switch (params->freq.bandwidth) {
 		case 80:
 			if (params->freq.center_freq2) {
-				ssid->max_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
+				ssid->max_oper_chwidth = CHANWIDTH_80P80MHZ;
 				ssid->vht_center_freq2 =
 					params->freq.center_freq2;
 			} else {
-				ssid->max_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+				ssid->max_oper_chwidth = CHANWIDTH_80MHZ;
 			}
 			break;
 		case 160:
-			ssid->max_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+			ssid->max_oper_chwidth = CHANWIDTH_160MHZ;
 			break;
 		default:
-			ssid->max_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+			ssid->max_oper_chwidth = CHANWIDTH_USE_HT;
 			break;
 		}
 	}
+	if (wpa_s->mesh_he_enabled)
+		ssid->he = 1;
 	if (ssid->beacon_int > 0)
 		params->beacon_int = ssid->beacon_int;
 	else if (wpa_s->conf->beacon_int > 0)
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 9d6ab8d..308366f 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -231,7 +231,7 @@
 		  2 + 32 + /* mesh ID */
 		  2 + 7 +  /* mesh config */
 		  2 + 24 + /* peering management */
-		  2 + 96 + /* AMPE */
+		  2 + 96 + 32 + 32 + /* AMPE (96 + max GTKlen + max IGTKlen) */
 		  2 + 16;  /* MIC */
 #ifdef CONFIG_IEEE80211N
 	if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
@@ -245,6 +245,16 @@
 			   2 + 5;  /* VHT Operation */
 	}
 #endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	if (type != PLINK_CLOSE && wpa_s->mesh_he_enabled) {
+		buf_len += 3 +
+			   HE_MAX_MAC_CAPAB_SIZE +
+			   HE_MAX_PHY_CAPAB_SIZE +
+			   HE_MAX_MCS_CAPAB_SIZE +
+			   HE_MAX_PPET_CAPAB_SIZE;
+		buf_len += 3 + sizeof(struct ieee80211_he_operation);
+	}
+#endif /* CONFIG_IEEE80211AX */
 	if (type != PLINK_CLOSE)
 		buf_len += conf->rsn_ie_len; /* RSN IE */
 #ifdef CONFIG_OCV
@@ -362,6 +372,21 @@
 		wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper);
 	}
 #endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	if (type != PLINK_CLOSE && wpa_s->mesh_he_enabled) {
+		u8 he_capa_oper[3 +
+				HE_MAX_MAC_CAPAB_SIZE +
+				HE_MAX_PHY_CAPAB_SIZE +
+				HE_MAX_MCS_CAPAB_SIZE +
+				HE_MAX_PPET_CAPAB_SIZE +
+				3 + sizeof(struct ieee80211_he_operation)];
+
+		pos = hostapd_eid_he_capab(bss, he_capa_oper,
+					   IEEE80211_MODE_MESH);
+		pos = hostapd_eid_he_operation(bss, pos);
+		wpabuf_put_data(buf, he_capa_oper, pos - he_capa_oper);
+	}
+#endif /* CONFIG_IEEE80211AX */
 
 #ifdef CONFIG_OCV
 	if (type != PLINK_CLOSE && conf->ocv) {
@@ -725,6 +750,11 @@
 	set_sta_vht_opmode(data, sta, elems->vht_opmode_notif);
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_IEEE80211AX
+	copy_sta_he_capab(data, sta, IEEE80211_MODE_MESH,
+			  elems->he_capabilities, elems->he_capabilities_len);
+#endif /* CONFIG_IEEE80211AX */
+
 	if (hostapd_get_aid(data, sta) < 0) {
 		wpa_msg(wpa_s, MSG_ERROR, "No AIDs available");
 		ap_free_sta(data, sta);
@@ -742,6 +772,8 @@
 	params.listen_interval = 100;
 	params.ht_capabilities = sta->ht_capabilities;
 	params.vht_capabilities = sta->vht_capabilities;
+	params.he_capab = sta->he_capab;
+	params.he_capab_len = sta->he_capab_len;
 	params.flags |= WPA_STA_WMM;
 	params.flags_mask |= WPA_STA_AUTHENTICATED;
 	if (conf->security == MESH_CONF_SEC_NONE) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 4b8d6c4..e730b63 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -165,11 +165,9 @@
 	conf.wpa_group_rekey = -1;
 	conf.wpa_group_update_count = 4;
 	conf.wpa_pairwise_update_count = 4;
-#ifdef CONFIG_IEEE80211W
 	conf.ieee80211w = ieee80211w;
 	if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
 		conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	conf.ocv = ocv;
 #endif /* CONFIG_OCV */
@@ -186,7 +184,6 @@
 		return -1;
 	rsn->mgtk_key_id = 1;
 
-#ifdef CONFIG_IEEE80211W
 	if (ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher);
 		if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0)
@@ -201,7 +198,6 @@
 				rsn->igtk_key_id, 1,
 				seq, sizeof(seq), rsn->igtk, rsn->igtk_len);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	/* group privacy / data frames */
 	wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
@@ -545,10 +541,8 @@
 	len = sizeof(*ampe);
 	if (cat[1] == PLINK_OPEN)
 		len += rsn->mgtk_len + WPA_KEY_RSC_LEN + 4;
-#ifdef CONFIG_IEEE80211W
 	if (cat[1] == PLINK_OPEN && rsn->igtk_len)
 		len += 2 + 6 + rsn->igtk_len;
-#endif /* CONFIG_IEEE80211W */
 
 	if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) {
 		wpa_printf(MSG_ERROR, "protect frame: buffer too small");
@@ -591,7 +585,6 @@
 	WPA_PUT_LE32(pos, 0xffffffff);
 	pos += 4;
 
-#ifdef CONFIG_IEEE80211W
 	/*
 	 * IGTKdata[variable]:
 	 * Key ID[2], IPN[6], IGTK[variable]
@@ -603,7 +596,6 @@
 		pos += 6;
 		os_memcpy(pos, rsn->igtk, rsn->igtk_len);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 skip_keys:
 	wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element",
@@ -774,7 +766,6 @@
 		   WPA_GET_LE32(pos));
 	pos += 4;
 
-#ifdef CONFIG_IEEE80211W
 	/*
 	 * IGTKdata[variable]:
 	 * Key ID[2], IPN[6], IGTK[variable]
@@ -794,7 +785,6 @@
 		wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK",
 				sta->igtk, sta->igtk_len);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 free:
 	os_free(crypt);
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 41dc334..56eb62a 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -17,6 +17,7 @@
 #include "dbus/dbus_new.h"
 #include "rsn_supp/wpa.h"
 #include "fst/fst.h"
+#include "crypto/tls.h"
 #include "driver_i.h"
 #include "scan.h"
 #include "p2p_supplicant.h"
@@ -197,6 +198,10 @@
 		return;
 
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSS_TM_STATUS);
+
+#ifdef CONFIG_WNM
+	wpas_hidl_notify_bss_tm_status(wpa_s);
+#endif
 }
 
 
@@ -857,42 +862,42 @@
 }
 
 
-void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
-			       const char *subject, const char *altsubject[],
-			       int num_altsubject, const char *cert_hash,
-			       const struct wpabuf *cert)
+void wpas_notify_certification(struct wpa_supplicant *wpa_s,
+			       struct tls_cert_data *cert,
+			       const char *cert_hash)
 {
-	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
-		"depth=%d subject='%s'%s%s",
-		depth, subject, cert_hash ? " hash=" : "",
-		cert_hash ? cert_hash : "");
+	int i;
 
-	if (cert) {
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+		"depth=%d subject='%s'%s%s%s%s",
+		cert->depth, cert->subject, cert_hash ? " hash=" : "",
+		cert_hash ? cert_hash : "",
+		cert->tod == 2 ? " tod=2" : "",
+		cert->tod == 1 ? " tod=1" : "");
+
+	if (cert->cert) {
 		char *cert_hex;
-		size_t len = wpabuf_len(cert) * 2 + 1;
+		size_t len = wpabuf_len(cert->cert) * 2 + 1;
 		cert_hex = os_malloc(len);
 		if (cert_hex) {
-			wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
-					 wpabuf_len(cert));
+			wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert->cert),
+					 wpabuf_len(cert->cert));
 			wpa_msg_ctrl(wpa_s, MSG_INFO,
 				     WPA_EVENT_EAP_PEER_CERT
 				     "depth=%d subject='%s' cert=%s",
-				     depth, subject, cert_hex);
+				     cert->depth, cert->subject, cert_hex);
 			os_free(cert_hex);
 		}
 	}
 
-	if (altsubject) {
-		int i;
-
-		for (i = 0; i < num_altsubject; i++)
-			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
-				"depth=%d %s", depth, altsubject[i]);
-	}
+	for (i = 0; i < cert->num_altsubject; i++)
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+			"depth=%d %s", cert->depth, cert->altsubject[i]);
 
 	/* notify the new DBus API */
-	wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject,
-				       num_altsubject, cert_hash, cert);
+	wpas_dbus_signal_certification(wpa_s, cert->depth, cert->subject,
+				       cert->altsubject, cert->num_altsubject,
+				       cert_hash, cert->cert);
 }
 
 
@@ -1024,7 +1029,7 @@
 
 void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
 				    const u8 *meshid, u8 meshid_len,
-				    int reason_code)
+				    u16 reason_code)
 {
 	if (wpa_s->p2p_mgmt)
 		return;
@@ -1045,7 +1050,7 @@
 
 
 void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
-					const u8 *peer_addr, int reason_code)
+					const u8 *peer_addr, u16 reason_code)
 {
 	if (wpa_s->p2p_mgmt)
 		return;
@@ -1163,3 +1168,42 @@
 	wpas_hidl_notify_dpp_fail(wpa_s);
 #endif /* CONFIG_DPP */
 }
+
+void wpas_notify_dpp_config_sent_wait_response(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_DPP2
+	wpas_hidl_notify_dpp_config_sent_wait_response(wpa_s);
+#endif /* CONFIG_DPP2 */
+}
+
+void wpas_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_DPP2
+	wpas_hidl_notify_dpp_config_accepted(wpa_s);
+#endif /* CONFIG_DPP2 */
+}
+
+void wpas_notify_dpp_conn_status(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error status, const char *ssid,
+		const char *channel_list, unsigned short band_list[], int size)
+{
+#ifdef CONFIG_DPP2
+	wpas_hidl_notify_dpp_conn_status(wpa_s, status, ssid, channel_list, band_list, size);
+#endif /* CONFIG_DPP2 */
+}
+
+void wpas_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_DPP2
+	wpas_hidl_notify_dpp_config_rejected(wpa_s);
+#endif /* CONFIG_DPP2 */
+}
+
+void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
+				 struct rsn_pmksa_cache_entry *entry)
+{
+	if (!wpa_s)
+		return;
+
+	wpas_hidl_notify_pmk_cache_added(wpa_s, entry);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 2f194ce..e9e39ee 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -11,10 +11,13 @@
 
 #include "p2p/p2p.h"
 #include "bss.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "dpp.h"
 
 struct wps_credential;
 struct wps_event_m2d;
 struct wps_event_fail;
+struct tls_cert_data;
 
 int wpas_notify_supplicant_initialized(struct wpa_global *global);
 void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
@@ -134,10 +137,9 @@
 void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 				struct wps_event_fail *fail);
 
-void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
-			       const char *subject, const char *altsubject[],
-			       int num_altsubject, const char *cert_hash,
-			       const struct wpabuf *cert);
+void wpas_notify_certification(struct wpa_supplicant *wpa_s,
+			       struct tls_cert_data *cert,
+			       const char *cert_hash);
 void wpas_notify_preq(struct wpa_supplicant *wpa_s,
 		      const u8 *addr, const u8 *dst, const u8 *bssid,
 		      const u8 *ie, size_t ie_len, u32 ssi_signal);
@@ -155,11 +157,11 @@
 				    struct wpa_ssid *ssid);
 void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
 				    const u8 *meshid, u8 meshid_len,
-				    int reason_code);
+				    u16 reason_code);
 void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
 				     const u8 *peer_addr);
 void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
-					const u8 *peer_addr, int reason_code);
+					const u8 *peer_addr, u16 reason_code);
 void wpas_notify_anqp_query_done(struct wpa_supplicant *wpa_s, const u8* bssid,
 				 const char* result,
 				 const struct wpa_bss_anqp *anqp);
@@ -183,5 +185,13 @@
 void wpas_notify_dpp_timeout(struct wpa_supplicant *wpa_s);
 void wpas_notify_dpp_auth_failure(struct wpa_supplicant *wpa_s);
 void wpas_notify_dpp_failure(struct wpa_supplicant *wpa_s);
+void wpas_notify_dpp_config_sent_wait_response(struct wpa_supplicant *wpa_s);
+void wpas_notify_dpp_conn_status(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error status, const char *ssid,
+		const char *channel_list, unsigned short band_list[], int size);
+void wpas_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s);
+void wpas_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s);
+void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
+				 struct rsn_pmksa_cache_entry *entry);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index 947917b..b15ac9e 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -16,13 +16,19 @@
 #include "wpa_supplicant_i.h"
 
 
-static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan,
+static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode,
+				       u8 op_class, u8 chan,
 				       unsigned int *flags)
 {
 	int i;
+	int is_6ghz = op_class >= 131 && op_class <= 135;
 
 	for (i = 0; i < mode->num_channels; i++) {
-		if (mode->channels[i].chan == chan)
+		int chan_is_6ghz;
+
+		chan_is_6ghz = mode->channels[i].freq > 5940 &&
+			mode->channels[i].freq <= 7105;
+		if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
 			break;
 	}
 
@@ -62,7 +68,8 @@
 }
 
 
-static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel)
+static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode,
+				      u8 op_class, u8 channel)
 {
 	u8 center_chan;
 	unsigned int i;
@@ -77,7 +84,8 @@
 		unsigned int flags;
 		u8 adj_chan = center_chan - 6 + i * 4;
 
-		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+		if (allow_channel(mode, op_class, adj_chan, &flags) ==
+		    NOT_ALLOWED)
 			return NOT_ALLOWED;
 
 		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
@@ -120,7 +128,7 @@
 
 
 static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
-				       u8 channel)
+				       u8 op_class, u8 channel)
 {
 	u8 center_chan;
 	unsigned int i;
@@ -135,7 +143,8 @@
 		unsigned int flags;
 		u8 adj_chan = center_chan - 14 + i * 4;
 
-		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+		if (allow_channel(mode, op_class, adj_chan, &flags) ==
+		    NOT_ALLOWED)
 			return NOT_ALLOWED;
 
 		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
@@ -159,42 +168,42 @@
 }
 
 
-enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
-				 u8 bw)
+enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
+				 u8 channel, u8 bw)
 {
 	unsigned int flag = 0;
 	enum chan_allowed res, res2;
 
-	res2 = res = allow_channel(mode, channel, &flag);
+	res2 = res = allow_channel(mode, op_class, channel, &flag);
 	if (bw == BW40MINUS) {
 		if (!(flag & HOSTAPD_CHAN_HT40MINUS))
 			return NOT_ALLOWED;
-		res2 = allow_channel(mode, channel - 4, NULL);
+		res2 = allow_channel(mode, op_class, channel - 4, NULL);
 	} else if (bw == BW40PLUS) {
 		if (!(flag & HOSTAPD_CHAN_HT40PLUS))
 			return NOT_ALLOWED;
-		res2 = allow_channel(mode, channel + 4, NULL);
+		res2 = allow_channel(mode, op_class, channel + 4, NULL);
 	} else if (bw == BW80) {
 		/*
 		 * channel is a center channel and as such, not necessarily a
 		 * valid 20 MHz channels. Override earlier allow_channel()
 		 * result and use only the 80 MHz specific version.
 		 */
-		res2 = res = verify_80mhz(mode, channel);
+		res2 = res = verify_80mhz(mode, op_class, channel);
 	} else if (bw == BW160) {
 		/*
 		 * channel is a center channel and as such, not necessarily a
 		 * valid 20 MHz channels. Override earlier allow_channel()
 		 * result and use only the 160 MHz specific version.
 		 */
-		res2 = res = verify_160mhz(mode, channel);
+		res2 = res = verify_160mhz(mode, op_class, channel);
 	} else if (bw == BW80P80) {
 		/*
 		 * channel is a center channel and as such, not necessarily a
 		 * valid 20 MHz channels. Override earlier allow_channel()
 		 * result and use only the 80 MHz specific version.
 		 */
-		res2 = res = verify_80mhz(mode, channel);
+		res2 = res = verify_80mhz(mode, op_class, channel);
 	}
 
 	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -225,7 +234,7 @@
 
 	/* If we are configured to disable certain things, take that into
 	 * account here. */
-	if (ssid->freq_list && ssid->freq_list[0]) {
+	if (ssid && ssid->freq_list && ssid->freq_list[0]) {
 		for (z = 0; ; z++) {
 			int f = ssid->freq_list[z];
 
@@ -248,7 +257,7 @@
 		return 0;
 
 #ifdef CONFIG_HT_OVERRIDES
-	if (ssid->disable_ht) {
+	if (ssid && ssid->disable_ht) {
 		switch (op_class->op_class) {
 		case 83:
 		case 84:
@@ -272,7 +281,7 @@
 #endif /* CONFIG_HT_OVERRIDES */
 
 #ifdef CONFIG_VHT_OVERRIDES
-	if (ssid->disable_vht) {
+	if (ssid && ssid->disable_vht) {
 		if (op_class->op_class >= 128 && op_class->op_class <= 130) {
 			/* Disable >= 80 MHz channels if VHT is disabled */
 			return 0;
@@ -284,7 +293,8 @@
 		u8 channels[] = { 42, 58, 106, 122, 138, 155 };
 
 		for (i = 0; i < ARRAY_SIZE(channels); i++) {
-			if (verify_channel(mode, channels[i], op_class->bw) !=
+			if (verify_channel(mode, op_class->op_class,
+					   channels[i], op_class->bw) !=
 			    NOT_ALLOWED)
 				return 1;
 		}
@@ -294,25 +304,35 @@
 
 	if (op_class->op_class == 129) {
 		/* Check if either 160 MHz channels is allowed */
-		return verify_channel(mode, 50, op_class->bw) != NOT_ALLOWED ||
-			verify_channel(mode, 114, op_class->bw) != NOT_ALLOWED;
+		return verify_channel(mode, op_class->op_class, 50,
+				      op_class->bw) != NOT_ALLOWED ||
+			verify_channel(mode, op_class->op_class, 114,
+				       op_class->bw) != NOT_ALLOWED;
 	}
 
 	if (op_class->op_class == 130) {
 		/* Need at least two non-contiguous 80 MHz segments */
 		found = 0;
 
-		if (verify_channel(mode, 42, op_class->bw) != NOT_ALLOWED ||
-		    verify_channel(mode, 58, op_class->bw) != NOT_ALLOWED)
+		if (verify_channel(mode, op_class->op_class, 42,
+				   op_class->bw) != NOT_ALLOWED ||
+		    verify_channel(mode, op_class->op_class, 58,
+				   op_class->bw) != NOT_ALLOWED)
 			found++;
-		if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED ||
-		    verify_channel(mode, 122, op_class->bw) != NOT_ALLOWED ||
-		    verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED)
+		if (verify_channel(mode, op_class->op_class, 106,
+				   op_class->bw) != NOT_ALLOWED ||
+		    verify_channel(mode, op_class->op_class, 122,
+				   op_class->bw) != NOT_ALLOWED ||
+		    verify_channel(mode, op_class->op_class, 138,
+				   op_class->bw) != NOT_ALLOWED)
 			found++;
-		if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED &&
-		    verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED)
+		if (verify_channel(mode, op_class->op_class, 106,
+				   op_class->bw) != NOT_ALLOWED &&
+		    verify_channel(mode, op_class->op_class, 138,
+				   op_class->bw) != NOT_ALLOWED)
 			found++;
-		if (verify_channel(mode, 155, op_class->bw) != NOT_ALLOWED)
+		if (verify_channel(mode, op_class->op_class, 155,
+				   op_class->bw) != NOT_ALLOWED)
 			found++;
 
 		if (found >= 2)
@@ -324,7 +344,8 @@
 	found = 0;
 	for (chan = op_class->min_chan; chan <= op_class->max_chan;
 	     chan += op_class->inc) {
-		if (verify_channel(mode, chan, op_class->bw) != NOT_ALLOWED) {
+		if (verify_channel(mode, op_class->op_class, chan,
+				   op_class->bw) != NOT_ALLOWED) {
 			found = 1;
 			break;
 		}
@@ -348,7 +369,7 @@
 	 * TODO: Use the secondary channel and VHT channel width that will be
 	 * used after association.
 	 */
-	if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+	if (ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT,
 					  &current, &chan) == NUM_HOSTAPD_MODES)
 		return 0;
 
@@ -385,3 +406,24 @@
 	wpabuf_free(buf);
 	return res;
 }
+
+
+int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s)
+{
+	int op;
+	unsigned int pos, max_num = 0;
+	int *classes;
+
+	for (op = 0; global_op_class[op].op_class; op++)
+		max_num++;
+	classes = os_zalloc((max_num + 1) * sizeof(int));
+	if (!classes)
+		return NULL;
+
+	for (op = 0, pos = 0; global_op_class[op].op_class; op++) {
+		if (wpas_op_class_supported(wpa_s, NULL, &global_op_class[op]))
+			classes[pos++] = global_op_class[op].op_class;
+	}
+
+	return classes;
+}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 70b61d6..a3c4574 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1556,7 +1556,7 @@
 {
 	struct send_action_work *awork;
 
-	if (wpa_s->p2p_send_action_work) {
+	if (radio_work_pending(wpa_s, "p2p-send-action")) {
 		wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending");
 		return -1;
 	}
@@ -1573,7 +1573,7 @@
 	awork->wait_time = wait_time;
 	os_memcpy(awork->buf, buf, len);
 
-	if (radio_add_work(wpa_s, freq, "p2p-send-action", 0,
+	if (radio_add_work(wpa_s, freq, "p2p-send-action", 1,
 			   wpas_send_action_cb, awork) < 0) {
 		os_free(awork);
 		return -1;
@@ -1885,6 +1885,83 @@
 }
 
 
+/**
+ * wpas_p2p_freq_to_edmg_channel - Convert frequency into EDMG channel
+ * @freq: Frequency (MHz) to convert
+ * @op_class: Buffer for returning operating class
+ * @op_edmg_channel: Buffer for returning channel number
+ * Returns: 0 on success, -1 on failure
+ *
+ * This can be used to find the highest channel bonding which includes the
+ * specified frequency.
+ */
+static int wpas_p2p_freq_to_edmg_channel(struct wpa_supplicant *wpa_s,
+					 unsigned int freq,
+					 u8 *op_class, u8 *op_edmg_channel)
+{
+	struct hostapd_hw_modes *hwmode;
+	struct ieee80211_edmg_config edmg;
+	unsigned int i;
+	enum chan_width chanwidth[] = {
+		CHAN_WIDTH_8640,
+		CHAN_WIDTH_6480,
+		CHAN_WIDTH_4320,
+	};
+
+	if (!wpa_s->hw.modes)
+		return -1;
+
+	hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+			  HOSTAPD_MODE_IEEE80211AD);
+	if (!hwmode) {
+		wpa_printf(MSG_ERROR,
+			   "Unsupported AP mode: HOSTAPD_MODE_IEEE80211AD");
+		return -1;
+	}
+
+	/* Find the highest EDMG channel bandwidth to start the P2P GO */
+	for (i = 0; i < ARRAY_SIZE(chanwidth); i++) {
+		if (ieee80211_chaninfo_to_channel(freq, chanwidth[i], 0,
+						  op_class,
+						  op_edmg_channel) < 0)
+			continue;
+
+		hostapd_encode_edmg_chan(1, *op_edmg_channel, 0, &edmg);
+		if (edmg.channels &&
+		    ieee802_edmg_is_allowed(hwmode->edmg, edmg)) {
+			wpa_printf(MSG_DEBUG,
+				   "Freq %u to EDMG channel %u at opclass %u",
+				   freq, *op_edmg_channel, *op_class);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s,
+			      struct p2p_go_neg_results *params)
+{
+	u8 op_channel, op_class;
+	int freq;
+
+	/* Try social channel as primary channel frequency */
+	freq = (!params->freq) ? 58320 + 1 * 2160 : params->freq;
+
+	if (wpas_p2p_freq_to_edmg_channel(wpa_s, freq, &op_class,
+					  &op_channel) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "Freq %d will be used to set an EDMG connection (channel=%u opclass=%u)",
+			   freq, op_channel, op_class);
+		params->freq = freq;
+		return 0;
+	}
+
+	return -1;
+}
+
+
 static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
 			      struct p2p_go_neg_results *params,
 			      int group_formation)
@@ -1921,6 +1998,20 @@
 	ssid->max_oper_chwidth = params->max_oper_chwidth;
 	ssid->vht_center_freq2 = params->vht_center_freq2;
 	ssid->he = params->he;
+	if (params->edmg) {
+		u8 op_channel, op_class;
+
+		if (!wpas_p2p_freq_to_edmg_channel(wpa_s, params->freq,
+						   &op_class, &op_channel)) {
+			ssid->edmg_channel = op_channel;
+			ssid->enable_edmg = params->edmg;
+		} else {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Could not match EDMG channel, freq %d, for GO",
+				params->freq);
+		}
+	}
+
 	ssid->ssid = os_zalloc(params->ssid_len + 1);
 	if (ssid->ssid) {
 		os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -2268,6 +2359,10 @@
 		res->ht40 = 1;
 	if (wpa_s->p2p_go_vht)
 		res->vht = 1;
+	if (wpa_s->p2p_go_he)
+		res->he = 1;
+	if (wpa_s->p2p_go_edmg)
+		res->edmg = 1;
 	res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
 	res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
 
@@ -3097,7 +3192,8 @@
 					       MAC2STR(sa), s->id);
 			}
 			wpas_p2p_group_add_persistent(
-				wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, 0, NULL,
+				wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, 0,
+				0, NULL,
 				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
 				1);
 		} else if (bssid) {
@@ -3324,6 +3420,7 @@
 				      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,
 				      channels,
 				      ssid->mode == WPAS_MODE_P2P_GO ?
 				      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
@@ -3568,6 +3665,20 @@
 }
 
 
+static enum chan_allowed wpas_p2p_verify_edmg(struct wpa_supplicant *wpa_s,
+					      struct hostapd_hw_modes *mode,
+					      u8 channel)
+{
+	struct ieee80211_edmg_config edmg;
+
+	hostapd_encode_edmg_chan(1, channel, 0, &edmg);
+	if (edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg))
+		return ALLOWED;
+
+	return NOT_ALLOWED;
+}
+
+
 static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
 						 struct hostapd_hw_modes *mode,
 						 u8 channel, u8 bw)
@@ -3588,6 +3699,8 @@
 		res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
 	} else if (bw == BW160) {
 		res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw);
+	} else if (bw == BW4320 || bw == BW6480 || bw == BW8640) {
+		return wpas_p2p_verify_edmg(wpa_s, mode, channel);
 	}
 
 	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -4238,14 +4351,14 @@
 			if (response_done && persistent_go) {
 				wpas_p2p_group_add_persistent(
 					wpa_s, persistent_go,
-					0, 0, freq, 0, 0, 0, 0, 0, NULL,
+					0, 0, freq, 0, 0, 0, 0, 0, 0, NULL,
 					persistent_go->mode ==
 					WPAS_MODE_P2P_GO ?
 					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
 					0, 0);
 			} else if (response_done) {
 				wpas_p2p_group_add(wpa_s, 1, freq,
-						   0, 0, 0, 0, 0);
+						   0, 0, 0, 0, 0, 0);
 			}
 
 			if (passwd_id == DEV_PW_P2PS_DEFAULT) {
@@ -4361,11 +4474,12 @@
 
 	if (persistent_go) {
 		wpas_p2p_group_add_persistent(
-			wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, 0, NULL,
+			wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			NULL,
 			persistent_go->mode == WPAS_MODE_P2P_GO ?
 			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
 	} else {
-		wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0);
+		wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0);
 	}
 
 	return 1;
@@ -4948,6 +5062,7 @@
 					 wpa_s->p2p_go_vht,
 					 wpa_s->p2p_go_max_oper_chwidth,
 					 wpa_s->p2p_go_he,
+					 wpa_s->p2p_go_edmg,
 					 NULL, 0);
 			return;
 		}
@@ -5484,7 +5599,7 @@
  * @ht40: Start GO with 40 MHz channel width
  * @vht:  Start GO with VHT support
  * @vht_chwidth: Channel width supported by GO operating with VHT support
- *	(VHT_CHANWIDTH_*).
+ *	(CHANWIDTH_*).
  * @group_ssid: Specific Group SSID for join or %NULL if not set
  * @group_ssid_len: Length of @group_ssid in octets
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
@@ -5496,8 +5611,8 @@
 		     int persistent_group, int auto_join, int join, int auth,
 		     int go_intent, int freq, unsigned int vht_center_freq2,
 		     int persistent_id, int pd, int ht40, int vht,
-		     unsigned int vht_chwidth, int he, const u8 *group_ssid,
-		     size_t group_ssid_len)
+		     unsigned int vht_chwidth, int he, int edmg,
+		     const u8 *group_ssid, size_t group_ssid_len)
 {
 	int force_freq = 0, pref_freq = 0;
 	int ret = 0, res;
@@ -5542,6 +5657,7 @@
 	wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
 	wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
 	wpa_s->p2p_go_he = !!he;
+	wpa_s->p2p_go_edmg = !!edmg;
 
 	if (pin)
 		os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -5966,6 +6082,7 @@
 				   struct p2p_go_neg_results *params,
 				   int freq, int vht_center_freq2, int ht40,
 				   int vht, int max_oper_chwidth, int he,
+				   int edmg,
 				   const struct p2p_channels *channels)
 {
 	struct wpa_used_freq_data *freqs;
@@ -5981,6 +6098,7 @@
 	params->he = he;
 	params->max_oper_chwidth = max_oper_chwidth;
 	params->vht_center_freq2 = vht_center_freq2;
+	params->edmg = edmg;
 
 	freqs = os_calloc(wpa_s->num_multichan_concurrent,
 			  sizeof(struct wpa_used_freq_data));
@@ -6011,6 +6129,13 @@
 		}
 	}
 
+	/* Try to use EDMG channel */
+	if (params->edmg) {
+		if (wpas_p2p_try_edmg_channel(wpa_s, params) == 0)
+			goto success;
+		params->edmg = 0;
+	}
+
 	/* try using the forced freq */
 	if (freq) {
 		if (wpas_p2p_disallowed_freq(wpa_s->global, freq) ||
@@ -6328,6 +6453,7 @@
  * @ht40: Start GO with 40 MHz channel width
  * @vht:  Start GO with VHT support
  * @vht_chwidth: channel bandwidth for GO operating with VHT support
+ * @edmg: Start GO with EDMG support
  * Returns: 0 on success, -1 on failure
  *
  * This function creates a new P2P group with the local end as the Group Owner,
@@ -6335,7 +6461,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 max_oper_chwidth, int he, int edmg)
 {
 	struct p2p_go_neg_results params;
 
@@ -6356,7 +6482,8 @@
 	}
 
 	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
-				    ht40, vht, max_oper_chwidth, he, NULL))
+				    ht40, vht, max_oper_chwidth, he, edmg,
+				    NULL))
 		return -1;
 
 	p2p_go_params(wpa_s->global->p2p, &params);
@@ -6436,6 +6563,7 @@
 				  int force_freq, int neg_freq,
 				  int vht_center_freq2, int ht40,
 				  int vht, int max_oper_chwidth, int he,
+				  int edmg,
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan)
 {
@@ -6511,7 +6639,8 @@
 	}
 
 	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
-				    ht40, vht, max_oper_chwidth, he, channels))
+				    ht40, vht, max_oper_chwidth, he, edmg,
+				    channels))
 		return -1;
 
 	params.role_go = 1;
@@ -7063,7 +7192,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 pref_freq, int he, int edmg)
 {
 	enum p2p_invite_role role;
 	u8 *bssid = NULL;
@@ -7084,6 +7213,7 @@
 	wpa_s->p2p_go_he = !!he;
 	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;
 	if (ssid->mode == WPAS_MODE_P2P_GO) {
 		role = P2P_INVITE_ROLE_GO;
 		if (peer_addr == NULL) {
@@ -7161,6 +7291,7 @@
 	wpa_s->p2p_go_vht = 0;
 	wpa_s->p2p_go_vht_center_freq2 = 0;
 	wpa_s->p2p_go_max_oper_chwidth = 0;
+	wpa_s->p2p_go_edmg = 0;
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -8106,7 +8237,9 @@
 			 wpa_s->p2p_go_ht40,
 			 wpa_s->p2p_go_vht,
 			 wpa_s->p2p_go_max_oper_chwidth,
-			 wpa_s->p2p_go_he, NULL, 0);
+			 wpa_s->p2p_go_he,
+			 wpa_s->p2p_go_edmg,
+			 NULL, 0);
 	return ret;
 }
 
@@ -8642,7 +8775,7 @@
 				WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
 				params->go_freq, wpa_s->p2p_go_vht_center_freq2,
 				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
-				wpa_s->p2p_go_he,
+				wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
 				params->go_ssid_len ? params->go_ssid : NULL,
 				params->go_ssid_len);
 }
@@ -8722,7 +8855,8 @@
 				WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
 				forced_freq, wpa_s->p2p_go_vht_center_freq2,
 				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
-				wpa_s->p2p_go_he, NULL, 0);
+				wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
+				NULL, 0);
 }
 
 
@@ -8738,7 +8872,8 @@
 			       WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
 			       forced_freq, wpa_s->p2p_go_vht_center_freq2,
 			       -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
-			       wpa_s->p2p_go_he, NULL, 0);
+			       wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
+			       NULL, 0);
 	if (res)
 		return res;
 
@@ -9123,7 +9258,8 @@
 	 * TODO: This function may not always work correctly. For example,
 	 * when we have a running GO and a BSS on a DFS channel.
 	 */
-	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, 0, NULL)) {
+	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, 0, 0,
+				    NULL)) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"P2P CSA: Failed to select new frequency for GO");
 		return -1;
@@ -9201,11 +9337,11 @@
 		csa_settings.freq_params.center_freq2 = freq2;
 
 		switch (conf->vht_oper_chwidth) {
-		case VHT_CHANWIDTH_80MHZ:
-		case VHT_CHANWIDTH_80P80MHZ:
+		case CHANWIDTH_80MHZ:
+		case CHANWIDTH_80P80MHZ:
 			csa_settings.freq_params.bandwidth = 80;
 			break;
-		case VHT_CHANWIDTH_160MHZ:
+		case CHANWIDTH_160MHZ:
 			csa_settings.freq_params.bandwidth = 160;
 			break;
 		}
@@ -9235,7 +9371,8 @@
 	wpa_supplicant_ap_deinit(wpa_s);
 
 	/* Reselect the GO frequency */
-	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, 0, NULL)) {
+	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, 0, 0,
+				    NULL)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
 		wpas_p2p_group_delete(wpa_s,
 				      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 24ec2ca..941198e 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -37,18 +37,18 @@
 		     int persistent_group, int auto_join, int join, int auth,
 		     int go_intent, int freq, unsigned int vht_center_freq2,
 		     int persistent_id, int pd, int ht40, int vht,
-		     unsigned int vht_chwidth, int he, const u8 *group_ssid,
-		     size_t group_ssid_len);
+		     unsigned int vht_chwidth, int he, int edmg,
+		     const u8 *group_ssid, size_t group_ssid_len);
 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 max_oper_chwidth, int he, int edmg);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
 				  int force_freq, int neg_freq,
-				  int vht_center_freq2, int ht40,
-				  int vht, int max_oper_chwidth, int he,
+				  int vht_center_freq2, int ht40, int vht,
+				  int max_oper_chwidth, int he, int edmg,
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
@@ -116,8 +116,8 @@
 int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
 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_oper_chwidth, int pref_freq, int he);
+		    int vht_center_freq2, int ht40, int vht, int max_chwidth,
+		    int pref_freq, int he, int edmg);
 int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 			  const u8 *peer_addr, const u8 *go_dev_addr);
 int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
@@ -165,6 +165,8 @@
 				 const struct wpabuf *sel, int forced_freq);
 int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled);
 void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx);
+int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s,
+			      struct p2p_go_neg_results *params);
 
 #ifdef CONFIG_P2P
 
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index f2fff55..3f2da34 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -35,7 +35,7 @@
 };
 
 
-static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
+static void _wpa_supplicant_deauthenticate(void *wpa_s, u16 reason_code)
 {
 	wpa_supplicant_deauthenticate(wpa_s, reason_code);
 }
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index cb3c6c9..7930e01 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -79,7 +79,7 @@
 			     NULL);
 
 	if (!wpa_s->rrm.notify_neighbor_rep) {
-		wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+		wpa_msg(wpa_s, MSG_INFO, "RRM: Unexpected neighbor report");
 		return;
 	}
 
@@ -90,8 +90,8 @@
 		return;
 	}
 	wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
-	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
-		   report[0]);
+	wpa_dbg(wpa_s, MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+		report[0]);
 	wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
 				       neighbor_rep);
 	wpa_s->rrm.notify_neighbor_rep = NULL;
@@ -101,10 +101,16 @@
 
 #if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
 /* Workaround different, undefined for Windows, error codes used here */
+#ifndef ENOTCONN
 #define ENOTCONN -1
+#endif
+#ifndef EOPNOTSUPP
 #define EOPNOTSUPP -1
+#endif
+#ifndef ECANCELED
 #define ECANCELED -1
 #endif
+#endif
 
 /* Measurement Request element + Location Subject + Maximum Age subelement */
 #define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4)
@@ -142,12 +148,12 @@
 	const u8 *rrm_ie;
 
 	if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
-		wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+		wpa_dbg(wpa_s, MSG_DEBUG, "RRM: No connection, no RRM.");
 		return -ENOTCONN;
 	}
 
 	if (!wpa_s->rrm.rrm_used) {
-		wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+		wpa_dbg(wpa_s, MSG_DEBUG, "RRM: No RRM in current connection.");
 		return -EOPNOTSUPP;
 	}
 
@@ -155,15 +161,15 @@
 				WLAN_EID_RRM_ENABLED_CAPABILITIES);
 	if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
 	    !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: No network support for Neighbor Report.");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"RRM: No network support for Neighbor Report.");
 		return -EOPNOTSUPP;
 	}
 
 	/* Refuse if there's a live request */
 	if (wpa_s->rrm.notify_neighbor_rep) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: Currently handling previous Neighbor Report.");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"RRM: Currently handling previous Neighbor Report.");
 		return -EBUSY;
 	}
 
@@ -172,14 +178,15 @@
 			   (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) +
 			   (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0));
 	if (buf == NULL) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: Failed to allocate Neighbor Report Request");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"RRM: Failed to allocate Neighbor Report Request");
 		return -ENOMEM;
 	}
 
-	wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
-		   (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
-		   wpa_s->rrm.next_neighbor_rep_token);
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"RRM: Neighbor report request (for %s), token=%d",
+		(ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+		wpa_s->rrm.next_neighbor_rep_token);
 
 	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
 	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
@@ -261,8 +268,8 @@
 	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,
-			   "RRM: Failed to send Neighbor Report Request");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"RRM: Failed to send Neighbor Report Request");
 		wpabuf_free(buf);
 		return -ECANCELED;
 	}
@@ -522,7 +529,8 @@
 	next_freq = freqs;
 	for  (i = 0; i < num_chans; i++) {
 		u8 chan = channels ? channels[i] : op->min_chan + i * op->inc;
-		enum chan_allowed res = verify_channel(mode, chan, op->bw);
+		enum chan_allowed res = verify_channel(mode, op->op_class, chan,
+						       op->bw);
 
 		if (res == NOT_ALLOWED || (res == NO_IR && active))
 			continue;
@@ -717,20 +725,20 @@
 			seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx;
 			seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx;
 			if (seg1 && abs(seg1 - seg0) == 8)
-				vht = VHT_CHANWIDTH_160MHZ;
+				vht = CHANWIDTH_160MHZ;
 			else if (seg1)
-				vht = VHT_CHANWIDTH_80P80MHZ;
+				vht = CHANWIDTH_80P80MHZ;
 			else
-				vht = VHT_CHANWIDTH_80MHZ;
+				vht = CHANWIDTH_80MHZ;
 			break;
 		case 2:
-			vht = VHT_CHANWIDTH_160MHZ;
+			vht = CHANWIDTH_160MHZ;
 			break;
 		case 3:
-			vht = VHT_CHANWIDTH_80P80MHZ;
+			vht = CHANWIDTH_80P80MHZ;
 			break;
 		default:
-			vht = VHT_CHANWIDTH_USE_HT;
+			vht = CHANWIDTH_USE_HT;
 			break;
 		}
 	}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 7abb028..4d158a9 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -79,6 +79,33 @@
 #endif /* CONFIG_WPS */
 
 
+static int wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params *params,
+					  const u8 *mac_addr)
+{
+	u8 *tmp;
+
+	if (params->mac_addr) {
+		params->mac_addr_mask = NULL;
+		os_free(params->mac_addr);
+		params->mac_addr = NULL;
+	}
+
+	params->mac_addr_rand = 1;
+
+	if (!mac_addr)
+		return 0;
+
+	tmp = os_malloc(2 * ETH_ALEN);
+	if (!tmp)
+		return -1;
+
+	os_memcpy(tmp, mac_addr, 2 * ETH_ALEN);
+	params->mac_addr = tmp;
+	params->mac_addr_mask = tmp + ETH_ALEN;
+	return 0;
+}
+
+
 /**
  * wpa_supplicant_enabled_networks - Check whether there are enabled networks
  * @wpa_s: Pointer to wpa_supplicant data
@@ -169,6 +196,10 @@
 		return;
 	}
 
+	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
+	    wpa_s->wpa_state <= WPA_SCANNING)
+		wpa_setup_mac_addr_rand_params(params, wpa_s->mac_addr_scan);
+
 	if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
 		wpa_msg(wpa_s, MSG_INFO,
 			"Failed to assign random MAC address for a scan");
@@ -1211,13 +1242,8 @@
 #endif /* CONFIG_P2P */
 
 	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
-	    wpa_s->wpa_state <= WPA_SCANNING) {
-		params.mac_addr_rand = 1;
-		if (wpa_s->mac_addr_scan) {
-			params.mac_addr = wpa_s->mac_addr_scan;
-			params.mac_addr_mask = wpa_s->mac_addr_scan + ETH_ALEN;
-		}
-	}
+	    wpa_s->wpa_state <= WPA_SCANNING)
+		wpa_setup_mac_addr_rand_params(&params, wpa_s->mac_addr_scan);
 
 	if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
 		struct wpa_bss *bss;
@@ -1286,6 +1312,7 @@
 	wpabuf_free(extra_ie);
 	os_free(params.freqs);
 	os_free(params.filter_ssids);
+	os_free(params.mac_addr);
 
 	if (ret) {
 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
@@ -1664,20 +1691,16 @@
 	wpa_setband_scan_freqs(wpa_s, scan_params);
 
 	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) &&
-	    wpa_s->wpa_state <= WPA_SCANNING) {
-		params.mac_addr_rand = 1;
-		if (wpa_s->mac_addr_sched_scan) {
-			params.mac_addr = wpa_s->mac_addr_sched_scan;
-			params.mac_addr_mask = wpa_s->mac_addr_sched_scan +
-				ETH_ALEN;
-		}
-	}
+	    wpa_s->wpa_state <= WPA_SCANNING)
+		wpa_setup_mac_addr_rand_params(&params,
+					       wpa_s->mac_addr_sched_scan);
 
 	wpa_scan_set_relative_rssi_params(wpa_s, scan_params);
 
 	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
 	wpabuf_free(extra_ie);
 	os_free(params.filter_ssids);
+	os_free(params.mac_addr);
 	if (ret) {
 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
 		if (prev_state != wpa_s->wpa_state)
@@ -2535,23 +2558,9 @@
 		params->sched_scan_plans_num = src->sched_scan_plans_num;
 	}
 
-	if (src->mac_addr_rand) {
-		params->mac_addr_rand = src->mac_addr_rand;
-
-		if (src->mac_addr && src->mac_addr_mask) {
-			u8 *mac_addr;
-
-			mac_addr = os_malloc(2 * ETH_ALEN);
-			if (!mac_addr)
-				goto failed;
-
-			os_memcpy(mac_addr, src->mac_addr, ETH_ALEN);
-			os_memcpy(mac_addr + ETH_ALEN, src->mac_addr_mask,
-				  ETH_ALEN);
-			params->mac_addr = mac_addr;
-			params->mac_addr_mask = mac_addr + ETH_ALEN;
-		}
-	}
+	if (src->mac_addr_rand &&
+	    wpa_setup_mac_addr_rand_params(params, src->mac_addr))
+		goto failed;
 
 	if (src->bssid) {
 		u8 *bssid;
@@ -2738,18 +2747,14 @@
 	}
 
 	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) &&
-	    wpa_s->wpa_state <= WPA_SCANNING) {
-		params.mac_addr_rand = 1;
-		if (wpa_s->mac_addr_pno) {
-			params.mac_addr = wpa_s->mac_addr_pno;
-			params.mac_addr_mask = wpa_s->mac_addr_pno + ETH_ALEN;
-		}
-	}
+	    wpa_s->wpa_state <= WPA_SCANNING)
+		wpa_setup_mac_addr_rand_params(&params, wpa_s->mac_addr_pno);
 
 	wpa_scan_set_relative_rssi_params(wpa_s, &params);
 
 	ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
 	os_free(params.filter_ssids);
+	os_free(params.mac_addr);
 	if (ret == 0)
 		wpa_s->pno = 1;
 	else
@@ -2843,6 +2848,32 @@
 }
 
 
+int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
+				     unsigned int type, u8 *mask)
+{
+	const u8 *to_copy;
+
+	if ((wpa_s->mac_addr_rand_enable & type) != type)
+		return -1;
+
+	if (type == MAC_ADDR_RAND_SCAN) {
+		to_copy = wpa_s->mac_addr_scan;
+	} else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
+		to_copy = wpa_s->mac_addr_sched_scan;
+	} else if (type == MAC_ADDR_RAND_PNO) {
+		to_copy = wpa_s->mac_addr_pno;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "scan: Invalid MAC randomization type=0x%x",
+			   type);
+		return -1;
+	}
+
+	os_memcpy(mask, to_copy + ETH_ALEN, ETH_ALEN);
+	return 0;
+}
+
+
 int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_radio_work *work;
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 2aa0a8b..58caa78 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -52,6 +52,8 @@
 int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
 				unsigned int type, const u8 *addr,
 				const u8 *mask);
+int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
+				     unsigned int type, u8 *mask);
 int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
 void filter_scan_res(struct wpa_supplicant *wpa_s,
 		     struct wpa_scan_results *res);
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index e2cc439..08d7e3e 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -13,6 +13,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/ocv.h"
+#include "common/hw_features_common.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "common/wpa_common.h"
 #include "common/sae.h"
@@ -37,9 +38,7 @@
 static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
 static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
 static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
-#ifdef CONFIG_IEEE80211W
 static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
-#endif /* CONFIG_IEEE80211W */
 
 
 #ifdef CONFIG_SAE
@@ -86,11 +85,16 @@
 static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 						 struct wpa_ssid *ssid,
 						 const u8 *bssid, int external,
-						 int reuse)
+						 int reuse, int *ret_use_pt)
 {
 	struct wpabuf *buf;
 	size_t len;
 	const char *password;
+	struct wpa_bss *bss;
+	int use_pt = 0;
+
+	if (ret_use_pt)
+		*ret_use_pt = 0;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (wpa_s->sae_commit_override) {
@@ -119,6 +123,7 @@
 	    os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: Reuse previously generated PWE on a retry with the same AP");
+		use_pt = wpa_s->sme.sae.tmp->h2e;
 		goto reuse_data;
 	}
 	if (sme_set_sae_group(wpa_s) < 0) {
@@ -126,7 +131,31 @@
 		return NULL;
 	}
 
-	if (sae_prepare_commit(wpa_s->own_addr, bssid,
+	if (wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
+		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+		if (bss) {
+			const u8 *rsnxe;
+
+			rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+			if (rsnxe && rsnxe[1] >= 1)
+				use_pt = !!(rsnxe[2] &
+					    BIT(WLAN_RSNX_CAPAB_SAE_H2E));
+		}
+
+		if (wpa_s->conf->sae_pwe == 1 && !use_pt) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Cannot use H2E with the selected AP");
+			return NULL;
+		}
+	}
+
+	if (use_pt &&
+	    sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
+				  wpa_s->own_addr, bssid,
+				  wpa_s->sme.sae_rejected_groups) < 0)
+		return NULL;
+	if (!use_pt &&
+	    sae_prepare_commit(wpa_s->own_addr, bssid,
 			       (u8 *) password, os_strlen(password),
 			       ssid->sae_password_id,
 			       &wpa_s->sme.sae) < 0) {
@@ -145,10 +174,13 @@
 		return NULL;
 	if (!external) {
 		wpabuf_put_le16(buf, 1); /* Transaction seq# */
-		wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+		wpabuf_put_le16(buf, use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
+				WLAN_STATUS_SUCCESS);
 	}
 	sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
 			 ssid->sae_password_id);
+	if (ret_use_pt)
+		*ret_use_pt = use_pt;
 
 	return buf;
 }
@@ -492,7 +524,6 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef CONFIG_IEEE80211W
 	wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid);
 	if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) {
 		const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
@@ -505,7 +536,6 @@
 			wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
 		}
 	}
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_P2P
 	if (wpa_s->global->p2p) {
@@ -562,6 +592,26 @@
 		os_memcpy(pos, ext_capab, ext_capab_len);
 	}
 
+#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) {
+		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);
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (wpa_s->rsnxe_len > 0 &&
+	    wpa_s->rsnxe_len <=
+	    sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) {
+		os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+			  wpa_s->rsnxe, wpa_s->rsnxe_len);
+		wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_len;
+	}
+
 #ifdef CONFIG_HS20
 	if (is_hs20_network(wpa_s, ssid, bss)) {
 		struct wpabuf *hs20;
@@ -623,7 +673,7 @@
 
 #ifdef CONFIG_MBO
 	mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
-	if (mbo_ie) {
+	if (!wpa_s->disable_mbo_oce && mbo_ie) {
 		int len;
 
 		len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie +
@@ -655,7 +705,7 @@
 		if (start)
 			resp = sme_auth_build_sae_commit(wpa_s, ssid,
 							 bss->bssid, 0,
-							 start == 2);
+							 start == 2, NULL);
 		else
 			resp = sme_auth_build_sae_confirm(wpa_s, 0);
 		if (resp == NULL) {
@@ -855,6 +905,8 @@
 	/* Starting new connection, so clear the possibly used WPA IE from the
 	 * previous association. */
 	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+	wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
+	wpa_s->rsnxe_len = 0;
 
 	sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
 }
@@ -909,7 +961,8 @@
 static int sme_external_auth_build_buf(struct wpabuf *buf,
 				       struct wpabuf *params,
 				       const u8 *sa, const u8 *da,
-				       u16 auth_transaction, u16 seq_num)
+				       u16 auth_transaction, u16 seq_num,
+				       u16 status_code)
 {
 	struct ieee80211_mgmt *resp;
 
@@ -924,7 +977,7 @@
 	resp->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE);
 	resp->seq_ctrl = host_to_le16(seq_num << 4);
 	resp->u.auth.auth_transaction = host_to_le16(auth_transaction);
-	resp->u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+	resp->u.auth.status_code = host_to_le16(status_code);
 	if (params)
 		wpabuf_put_buf(buf, params);
 
@@ -932,29 +985,36 @@
 }
 
 
-static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
-					      const u8 *bssid,
-					      struct wpa_ssid *ssid)
+static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
+					     const u8 *bssid,
+					     struct wpa_ssid *ssid)
 {
 	struct wpabuf *resp, *buf;
+	int use_pt;
 
-	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0);
-	if (!resp)
-		return;
+	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt);
+	if (!resp) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
+		return -1;
+	}
 
 	wpa_s->sme.sae.state = SAE_COMMITTED;
 	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
 	if (!buf) {
 		wpabuf_free(resp);
-		return;
+		return -1;
 	}
 
 	wpa_s->sme.seq_num++;
 	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
-				    bssid, 1, wpa_s->sme.seq_num);
+				    bssid, 1, wpa_s->sme.seq_num,
+				    use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
+				    WLAN_STATUS_SUCCESS);
 	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
 	wpabuf_free(resp);
 	wpabuf_free(buf);
+
+	return 0;
 }
 
 
@@ -968,12 +1028,14 @@
 	params.ssid = wpa_s->sme.ext_auth_ssid;
 	params.ssid_len = wpa_s->sme.ext_auth_ssid_len;
 	params.bssid = wpa_s->sme.ext_auth_bssid;
+	if (wpa_s->conf->sae_pmkid_in_assoc && status == WLAN_STATUS_SUCCESS)
+		params.pmkid = wpa_s->sme.sae.pmkid;
 	wpa_drv_send_external_auth_status(wpa_s, &params);
 }
 
 
-static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
-					   union wpa_event_data *data)
+static int sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
+					  union wpa_event_data *data)
 {
 	struct wpa_ssid *ssid;
 	size_t ssid_str_len = data->external_auth.ssid_len;
@@ -987,13 +1049,12 @@
 		    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)))
 			break;
 	}
-	if (ssid)
-		sme_external_auth_send_sae_commit(wpa_s,
-						  data->external_auth.bssid,
-						  ssid);
-	else
-		sme_send_external_auth_status(wpa_s,
-					      WLAN_STATUS_UNSPECIFIED_FAILURE);
+	if (!ssid ||
+	    sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid,
+					      ssid) < 0)
+		return -1;
+
+	return 0;
 }
 
 
@@ -1017,7 +1078,8 @@
 	}
 	wpa_s->sme.seq_num++;
 	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
-				    da, 2, wpa_s->sme.seq_num);
+				    da, 2, wpa_s->sme.seq_num,
+				    WLAN_STATUS_SUCCESS);
 	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
 	wpabuf_free(resp);
 	wpabuf_free(buf);
@@ -1043,7 +1105,9 @@
 		wpa_s->sme.sae.state = SAE_NOTHING;
 		wpa_s->sme.sae.send_confirm = 0;
 		wpa_s->sme.sae_group_index = 0;
-		sme_handle_external_auth_start(wpa_s, data);
+		if (sme_handle_external_auth_start(wpa_s, data) < 0)
+			sme_send_external_auth_status(wpa_s,
+					      WLAN_STATUS_UNSPECIFIED_FAILURE);
 	} else if (data->external_auth.action == EXT_AUTH_ABORT) {
 		/* Report failure to driver for the wrong trigger */
 		sme_send_external_auth_status(wpa_s,
@@ -1052,6 +1116,52 @@
 }
 
 
+static int sme_sae_is_group_enabled(struct wpa_supplicant *wpa_s, int group)
+{
+	int *groups = wpa_s->conf->sae_groups;
+	int default_groups[] = { 19, 20, 21, 0 };
+	int i;
+
+	if (!groups)
+		groups = default_groups;
+
+	for (i = 0; groups[i] > 0; i++) {
+		if (groups[i] == group)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int sme_check_sae_rejected_groups(struct wpa_supplicant *wpa_s,
+					 const struct wpabuf *groups)
+{
+	size_t i, count;
+	const u8 *pos;
+
+	if (!groups)
+		return 0;
+
+	pos = wpabuf_head(groups);
+	count = wpabuf_len(groups) / 2;
+	for (i = 0; i < count; i++) {
+		int enabled;
+		u16 group;
+
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		enabled = sme_sae_is_group_enabled(wpa_s, group);
+		wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
+			   group, enabled ? "enabled" : "disabled");
+		if (enabled)
+			return 1;
+	}
+
+	return 0;
+}
+
+
 static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 			u16 status_code, const u8 *data, size_t len,
 			int external, const u8 *sa)
@@ -1106,6 +1216,8 @@
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
 	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
+		int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
+				     wpa_s->sme.sae.group);
 		wpa_s->sme.sae_group_index++;
 		if (sme_set_sae_group(wpa_s) < 0)
 			return -1; /* no other groups enabled */
@@ -1130,7 +1242,8 @@
 		return -1;
 	}
 
-	if (status_code != WLAN_STATUS_SUCCESS)
+	if (status_code != WLAN_STATUS_SUCCESS &&
+	    status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
 		return -1;
 
 	if (auth_transaction == 1) {
@@ -1142,12 +1255,16 @@
 		if ((!external && wpa_s->current_bss == NULL) ||
 		    wpa_s->current_ssid == NULL)
 			return -1;
-		if (wpa_s->sme.sae.state != SAE_COMMITTED)
-			return -1;
+		if (wpa_s->sme.sae.state != SAE_COMMITTED) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Ignore commit message while waiting for confirm");
+			return 0;
+		}
 		if (groups && groups[0] <= 0)
 			groups = NULL;
 		res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
-				       groups);
+				       groups, status_code ==
+				       WLAN_STATUS_SAE_HASH_TO_ELEMENT);
 		if (res == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message due to reflection attack");
@@ -1156,6 +1273,12 @@
 		if (res != WLAN_STATUS_SUCCESS)
 			return -1;
 
+		if (wpa_s->sme.sae.tmp &&
+		    sme_check_sae_rejected_groups(
+			    wpa_s,
+			    wpa_s->sme.sae.tmp->peer_rejected_groups))
+			return -1;
+
 		if (sae_process_commit(&wpa_s->sme.sae) < 0) {
 			wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
 				   "commit");
@@ -1171,6 +1294,8 @@
 			sme_external_auth_send_sae_confirm(wpa_s, sa);
 		return 0;
 	} else if (auth_transaction == 2) {
+		if (status_code != WLAN_STATUS_SUCCESS)
+			return -1;
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
 		if (wpa_s->sme.sae.state != SAE_CONFIRMED)
 			return -1;
@@ -1192,6 +1317,37 @@
 }
 
 
+static int sme_sae_set_pmk(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	wpa_printf(MSG_DEBUG,
+		   "SME: SAE completed - setting PMK for 4-way handshake");
+	wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+		       wpa_s->sme.sae.pmkid, bssid);
+	if (wpa_s->conf->sae_pmkid_in_assoc) {
+		/* Update the own RSNE contents now that we have set the PMK
+		 * and added a PMKSA cache entry based on the successfully
+		 * completed SAE exchange. In practice, this will add the PMKID
+		 * into RSNE. */
+		if (wpa_s->sme.assoc_req_ie_len + 2 + PMKID_LEN >
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_msg(wpa_s, MSG_WARNING,
+				"RSN: Not enough room for inserting own PMKID into RSNE");
+			return -1;
+		}
+		if (wpa_insert_pmkid(wpa_s->sme.assoc_req_ie,
+				     &wpa_s->sme.assoc_req_ie_len,
+				     wpa_s->sme.sae.pmkid) < 0)
+			return -1;
+		wpa_hexdump(MSG_DEBUG,
+			    "SME: Updated Association Request IEs",
+			    wpa_s->sme.assoc_req_ie,
+			    wpa_s->sme.assoc_req_ie_len);
+	}
+
+	return 0;
+}
+
+
 void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
 			       const u8 *auth_frame, size_t len)
 {
@@ -1225,10 +1381,8 @@
 		if (res != 1)
 			return;
 
-		wpa_printf(MSG_DEBUG,
-			   "SME: SAE completed - setting PMK for 4-way handshake");
-		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
-			       wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
+		if (sme_sae_set_pmk(wpa_s, wpa_s->sme.ext_auth_bssid) < 0)
+			return;
 	}
 }
 
@@ -1281,10 +1435,8 @@
 		if (res != 1)
 			return;
 
-		wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
-			   "4-way handshake");
-		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
-			       wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
+		if (sme_sae_set_pmk(wpa_s, wpa_s->pending_bssid) < 0)
+			return;
 	}
 #endif /* CONFIG_SAE */
 
@@ -1773,6 +1925,11 @@
 					elems.osen_len + 2);
 	} else
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+	if (elems.rsnxe)
+		wpa_sm_set_assoc_rsnxe(wpa_s->wpa, elems.rsnxe - 2,
+				       elems.rsnxe_len + 2);
+	else
+		wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
 	if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
 		params.p2p = 1;
 
@@ -1990,15 +2147,17 @@
 	if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used)
 		sme_update_ft_ies(wpa_s, NULL, NULL, 0);
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	sme_stop_sa_query(wpa_s);
-#endif /* CONFIG_IEEE80211W */
 }
 
 
 void sme_deinit(struct wpa_supplicant *wpa_s)
 {
 	sme_clear_on_disassoc(wpa_s);
+#ifdef CONFIG_SAE
+	os_free(wpa_s->sme.sae_rejected_groups);
+	wpa_s->sme.sae_rejected_groups = NULL;
+#endif /* CONFIG_SAE */
 
 	eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
@@ -2056,13 +2215,14 @@
 }
 
 
-int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s,
+		       struct wpa_scan_results *scan_res)
 {
-	struct wpa_bss *bss;
 	const u8 *ie;
-	u16 ht_cap;
 	u8 chan_list[P2P_MAX_CHANNELS], channel;
 	u8 num_channels = 0, num_intol = 0, i;
+	size_t j;
+	int pri_freq, sec_freq;
 
 	if (!wpa_s->sme.sched_obss_scan)
 		return 0;
@@ -2090,22 +2250,36 @@
 
 	os_memset(chan_list, 0, sizeof(chan_list));
 
-	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		/* Skip other band bss */
+	pri_freq = wpa_s->assoc_freq;
+
+	switch (wpa_s->sme.ht_sec_chan) {
+	case HT_SEC_CHAN_ABOVE:
+		sec_freq = pri_freq + 20;
+		break;
+	case HT_SEC_CHAN_BELOW:
+		sec_freq = pri_freq - 20;
+		break;
+	case HT_SEC_CHAN_UNKNOWN:
+	default:
+		wpa_msg(wpa_s, MSG_WARNING,
+			"Undefined secondary channel: drop OBSS scan results");
+		return 1;
+	}
+
+	for (j = 0; j < scan_res->num; j++) {
+		struct wpa_scan_res *bss = scan_res->res[j];
 		enum hostapd_hw_mode mode;
+		int res;
+
+		/* Skip other band bss */
 		mode = ieee80211_freq_to_chan(bss->freq, &channel);
 		if (mode != HOSTAPD_MODE_IEEE80211G &&
 		    mode != HOSTAPD_MODE_IEEE80211B)
 			continue;
 
-		ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
-		ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
-		wpa_printf(MSG_DEBUG, "SME OBSS scan BSS " MACSTR
-			   " freq=%u chan=%u ht_cap=0x%x",
-			   MAC2STR(bss->bssid), bss->freq, channel, ht_cap);
-
-		if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
-			if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+		res = check_bss_coex_40mhz(bss, pri_freq, sec_freq);
+		if (res) {
+			if (res == 2)
 				num_intol++;
 
 			/* Check whether the channel is already considered */
@@ -2241,7 +2415,7 @@
 	 */
 	if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
 	      (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) ||
-	    ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA)
+	    ssid == NULL || ssid->mode != WPAS_MODE_INFRA)
 		return;
 
 	if (!wpa_s->hw.modes)
@@ -2287,8 +2461,6 @@
 }
 
 
-#ifdef CONFIG_IEEE80211W
-
 static const unsigned int sa_query_max_timeout = 1000;
 static const unsigned int sa_query_retry_timeout = 201;
 static const unsigned int sa_query_ch_switch_max_delay = 5000; /* in usec */
@@ -2577,5 +2749,3 @@
 	else if (data[0] == WLAN_SA_QUERY_RESPONSE)
 		sme_process_sa_query_response(wpa_s, sa, data, len);
 }
-
-#endif /* CONFIG_IEEE80211W */
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
index 1a7f9e8..42d5a83 100644
--- a/wpa_supplicant/sme.h
+++ b/wpa_supplicant/sme.h
@@ -37,7 +37,8 @@
 void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s);
 void sme_deinit(struct wpa_supplicant *wpa_s);
 
-int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s,
+		       struct wpa_scan_results *scan_res);
 void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
 void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
 			       union wpa_event_data *data);
@@ -112,7 +113,8 @@
 {
 }
 
-static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s,
+				     struct wpa_scan_results *scan_res)
 {
 	return 0;
 }
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 39cd163..1c0399f 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -271,7 +271,6 @@
 				WNM_SLEEP_SUBELEM_GTK,
 				ptr);
 			ptr += 13 + gtk_len;
-#ifdef CONFIG_IEEE80211W
 		} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
 			if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
 				wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
@@ -281,7 +280,6 @@
 			wpa_wnmsleep_install_key(wpa_s->wpa,
 						 WNM_SLEEP_SUBELEM_IGTK, ptr);
 			ptr += 10 + WPA_IGTK_LEN;
-#endif /* CONFIG_IEEE80211W */
 		} else
 			break; /* skip the loop */
 	}
@@ -452,7 +450,8 @@
 		break;
 	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
 		if (elen < 10) {
-			wpa_printf(MSG_DEBUG, "WNM: Too short bss_term_tsf");
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Too short BSS termination duration");
 			break;
 		}
 		rep->bss_term_tsf = WPA_GET_LE64(pos);
@@ -924,9 +923,9 @@
 	if (ie && ie[1] >= 1) {
 		vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
 
-		if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ ||
-		    vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ ||
-		    vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ)
+		if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ ||
+		    vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ ||
+		    vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ)
 			vht = vht_oper->vht_op_info_chwidth;
 	}
 
@@ -1370,12 +1369,19 @@
 	const u8 *vendor;
 #endif /* CONFIG_MBO */
 
+	if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm)
+		return;
+
 	if (end - pos < 5)
 		return;
 
 #ifdef CONFIG_MBO
 	wpa_s->wnm_mbo_trans_reason_present = 0;
 	wpa_s->wnm_mbo_transition_reason = 0;
+	wpa_s->wnm_mbo_cell_pref_present = 0;
+	wpa_s->wnm_mbo_cell_preference = 0;
+	wpa_s->wnm_mbo_assoc_retry_delay_present = 0;
+	wpa_s->wnm_mbo_assoc_retry_delay_sec = 0;
 #endif /* CONFIG_MBO */
 
 	if (wpa_s->current_bss)
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 695fcbe..a1b0615 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -52,6 +52,7 @@
 static const char *global = NULL;
 static const char *pid_file = NULL;
 static const char *action_file = NULL;
+static int reconnect = 0;
 static int ping_interval = 5;
 static int interactive = 0;
 static char *ifname_prefix = NULL;
@@ -80,7 +81,7 @@
 
 static void usage(void)
 {
-	printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
+	printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvBr] "
 	       "[-a<action file>] \\\n"
 	       "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] "
 	       "\\\n"
@@ -91,6 +92,8 @@
 	       "  -a = run in daemon mode executing the action file based on "
 	       "events from\n"
 	       "       wpa_supplicant\n"
+	       "  -r = try to reconnect when client socket is disconnected.\n"
+	       "       This is useful only when used with -a.\n"
 	       "  -B = run a daemon in the background\n"
 	       "  default path: " CONFIG_CTRL_IFACE_DIR "\n"
 	       "  default interface: first interface found in socket path\n");
@@ -474,7 +477,7 @@
 		"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
 		"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
 		"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
-		"ip_addr_start", "ip_addr_end",
+		"ip_addr_start", "ip_addr_end", "p2p_go_edmg",
 #endif /* CONFIG_P2P */
 		"country", "bss_max_count", "bss_expiration_age",
 		"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
@@ -1406,7 +1409,7 @@
 	"bssid_whitelist", "psk", "proto", "key_mgmt",
 	"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
 	"freq_list", "max_oper_chwidth", "ht40", "vht", "vht_center_freq1",
-	"vht_center_freq2", "ht",
+	"vht_center_freq2", "ht", "edmg",
 #ifdef IEEE8021X_EAPOL
 	"eap", "identity", "anonymous_identity", "password", "ca_cert",
 	"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -1427,22 +1430,18 @@
 #ifdef IEEE8021X_EAPOL
 	"eap_workaround", "pac_file", "fragment_size", "ocsp",
 #endif /* IEEE8021X_EAPOL */
-#ifdef CONFIG_MESH
-	"mode", "no_auto_peer", "mesh_rssi_threshold",
-#else /* CONFIG_MESH */
 	"mode",
-#endif /* CONFIG_MESH */
 	"proactive_key_caching", "disabled", "id_str",
-#ifdef CONFIG_IEEE80211W
 	"ieee80211w",
-#endif /* CONFIG_IEEE80211W */
 	"mixed_cell", "frequency", "fixed_freq",
 #ifdef CONFIG_MESH
+	"no_auto_peer", "mesh_rssi_threshold",
 	"mesh_basic_rates", "dot11MeshMaxRetries",
 	"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
 	"dot11MeshHoldingTimeout",
 #endif /* CONFIG_MESH */
 	"wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
+	"enable_edmg", "edmg_channel",
 #ifdef CONFIG_P2P
 	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
 #endif /* CONFIG_P2P */
@@ -2064,6 +2063,13 @@
 	return wpa_cli_cmd(ctrl, "MESH_PEER_ADD", 1, argc, argv);
 }
 
+
+static int wpa_cli_cmd_mesh_link_probe(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_LINK_PROBE", 1, argc, argv);
+}
+
 #endif /* CONFIG_MESH */
 
 
@@ -3384,6 +3390,9 @@
 	{ "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> [duration=<seconds>] = Add a mesh peer" },
+	{ "mesh_link_probe", wpa_cli_cmd_mesh_link_probe, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> [payload=<hex dump of payload>] = Probe a mesh link for a given peer by injecting a frame." },
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
@@ -3967,6 +3976,8 @@
 			wpa_cli_connected = 0;
 			wpa_cli_exec(action_file, ifname, "DISCONNECTED");
 		}
+	} else if (str_starts(pos, WPA_EVENT_CHANNEL_SWITCH_STARTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
 	} else if (str_starts(pos, AP_EVENT_ENABLED)) {
 		wpa_cli_exec(action_file, ctrl_ifname, pos);
 	} else if (str_starts(pos, AP_EVENT_DISABLED)) {
@@ -4009,9 +4020,26 @@
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, HS20_T_C_ACCEPTANCE)) {
 		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_CONF_RECEIVED)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_CONFOBJ_AKM)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_CONFOBJ_SSID)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_CONNECTOR)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_CONFOBJ_PASS)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_CONFOBJ_PSK)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_C_SIGN_KEY)) {
+		wpa_cli_exec(action_file, ifname, pos);
+	} else if (str_starts(pos, DPP_EVENT_NET_ACCESS_KEY)) {
+		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_starts(pos, WPA_EVENT_TERMINATING)) {
 		printf("wpa_supplicant is terminating - stop monitoring\n");
-		wpa_cli_quit = 1;
+		if (!reconnect)
+			wpa_cli_quit = 1;
 	}
 }
 
@@ -4203,6 +4231,10 @@
 	if (wpa_ctrl_pending(ctrl) < 0) {
 		printf("Connection to wpa_supplicant lost - trying to "
 		       "reconnect\n");
+		if (reconnect) {
+			eloop_terminate();
+			return;
+		}
 		wpa_cli_reconnect();
 	}
 }
@@ -4550,6 +4582,8 @@
 static void wpa_cli_terminate(int sig, void *ctx)
 {
 	eloop_terminate();
+	if (reconnect)
+		wpa_cli_quit = 1;
 }
 
 
@@ -4580,8 +4614,11 @@
 		if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
 			continue;
 #endif /* _DIRENT_HAVE_D_TYPE */
+		/* Skip current/previous directory and special P2P Device
+		 * interfaces. */
 		if (os_strcmp(dent->d_name, ".") == 0 ||
-		    os_strcmp(dent->d_name, "..") == 0)
+		    os_strcmp(dent->d_name, "..") == 0 ||
+		    os_strncmp(dent->d_name, "p2p-dev-", 8) == 0)
 			continue;
 		printf("Selected interface '%s'\n", dent->d_name);
 		ifname = os_strdup(dent->d_name);
@@ -4627,7 +4664,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v");
+		c = getopt(argc, argv, "a:Bg:G:hi:p:P:rs:v");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -4659,6 +4696,9 @@
 		case 'P':
 			pid_file = optarg;
 			break;
+		case 'r':
+			reconnect = 1;
+			break;
 		case 's':
 			client_socket_dir = optarg;
 			break;
@@ -4684,7 +4724,22 @@
 	if (ctrl_ifname == NULL)
 		ctrl_ifname = wpa_cli_get_default_ifname();
 
-	if (interactive) {
+	if (reconnect && action_file && ctrl_ifname) {
+		while (!wpa_cli_quit) {
+			if (ctrl_conn)
+				wpa_cli_action(ctrl_conn);
+			else
+				os_sleep(1, 0);
+			wpa_cli_close_connection();
+			wpa_cli_open_connection(ctrl_ifname, 0);
+			if (ctrl_conn) {
+				if (wpa_ctrl_attach(ctrl_conn) != 0)
+					wpa_cli_close_connection();
+				else
+					wpa_cli_attached = 1;
+			}
+		}
+	} else if (interactive) {
 		wpa_cli_interactive();
 	} else {
 		if (!global &&
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index adca1cc..538997e 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -31,9 +31,9 @@
 	if (argc > 2) {
 		passphrase = argv[2];
 	} else {
-		printf("# reading passphrase from stdin\n");
+		fprintf(stderr, "# reading passphrase from stdin\n");
 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
-			printf("Failed to read passphrase\n");
+			fprintf(stderr, "Failed to read passphrase\n");
 			return 1;
 		}
 		buf[sizeof(buf) - 1] = '\0';
@@ -50,11 +50,11 @@
 
 	len = os_strlen(passphrase);
 	if (len < 8 || len > 63) {
-		printf("Passphrase must be 8..63 characters\n");
+		fprintf(stderr, "Passphrase must be 8..63 characters\n");
 		return 1;
 	}
 	if (has_ctrl_char((u8 *) passphrase, len)) {
-		printf("Invalid passphrase character\n");
+		fprintf(stderr, "Invalid passphrase character\n");
 		return 1;
 	}
 
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index b3ad45e..f197352 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -598,7 +598,7 @@
 	}
 
 	dst_addr = buf;
-	os_memcpy(&proto, buf + ETH_ALEN, 2);
+	os_memcpy(&proto, (char *) buf + ETH_ALEN, 2);
 
 	if (!wpa_priv_allowed_l2_proto(proto)) {
 		wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
@@ -607,7 +607,8 @@
 	}
 
 	res = l2_packet_send(iface->l2[idx], dst_addr, proto,
-			     buf + ETH_ALEN + 2, len - ETH_ALEN - 2);
+			     (unsigned char *) buf + ETH_ALEN + 2,
+			     len - ETH_ALEN - 2);
 	wpa_printf(MSG_DEBUG, "L2 send[idx=%d]: res=%d", idx, res);
 }
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 5d4adf4..73e63ab 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -36,6 +36,7 @@
 #include "rsn_supp/preauth.h"
 #include "rsn_supp/pmksa_cache.h"
 #include "common/wpa_ctrl.h"
+#include "common/ieee802_11_common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/hw_features_common.h"
 #include "common/gas_server.h"
@@ -124,6 +125,9 @@
 #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
 static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s);
 #endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+#ifdef CONFIG_OWE
+static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_OWE */
 
 
 /* Configure default/group WEP keys for static WEP */
@@ -399,7 +403,10 @@
 		wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
 	wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
 	wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+	wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0);
 	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+	wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
+	wpa_s->rsnxe_len = 0;
 	wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
 	wpa_s->group_cipher = WPA_CIPHER_NONE;
 	wpa_s->mgmt_group_cipher = 0;
@@ -421,10 +428,8 @@
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
 			 wpa_s->pairwise_cipher);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
-#ifdef CONFIG_IEEE80211W
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
 			 wpa_s->mgmt_group_cipher);
-#endif /* CONFIG_IEEE80211W */
 
 	pmksa_cache_clear_current(wpa_s->wpa);
 }
@@ -495,6 +500,12 @@
 	wpa_s->get_pref_freq_list_override = NULL;
 	wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
 	wpa_s->last_assoc_req_wpa_ie = NULL;
+	os_free(wpa_s->extra_sae_rejected_groups);
+	wpa_s->extra_sae_rejected_groups = NULL;
+	wpabuf_free(wpa_s->rsnxe_override_assoc);
+	wpa_s->rsnxe_override_assoc = NULL;
+	wpabuf_free(wpa_s->rsnxe_override_eapol);
+	wpa_s->rsnxe_override_eapol = NULL;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	if (wpa_s->conf != NULL) {
@@ -690,13 +701,7 @@
  */
 void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 {
-	int i, max;
-
-#ifdef CONFIG_IEEE80211W
-	max = 6;
-#else /* CONFIG_IEEE80211W */
-	max = 4;
-#endif /* CONFIG_IEEE80211W */
+	int i, max = 6;
 
 	/* MLME-DELETEKEYS.request */
 	for (i = 0; i < max; i++) {
@@ -845,6 +850,9 @@
 			      enum wpa_states state)
 {
 	enum wpa_states old_state = wpa_s->wpa_state;
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+	Boolean update_fils_connect_params = FALSE;
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s",
 		wpa_supplicant_state_txt(wpa_s->wpa_state),
@@ -942,8 +950,12 @@
 
 #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
 		if (!fils_hlp_sent && ssid && ssid->eap.erp)
-			wpas_update_fils_connect_params(wpa_s);
+			update_fils_connect_params = TRUE;
 #endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+#ifdef CONFIG_OWE
+		if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_OWE))
+			wpas_update_owe_connect_params(wpa_s);
+#endif /* CONFIG_OWE */
 	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
 		   state == WPA_ASSOCIATED) {
 		wpa_s->new_connection = 1;
@@ -983,7 +995,15 @@
 		if (wpa_s->wpa_state == WPA_COMPLETED ||
 		    old_state == WPA_COMPLETED)
 			wpas_notify_auth_changed(wpa_s);
+#ifdef CONFIG_DPP2
+		if (wpa_s->wpa_state == WPA_COMPLETED)
+			wpas_dpp_connected(wpa_s);
+#endif /* CONFIG_DPP2 */
 	}
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+	if (update_fils_connect_params)
+		wpas_update_fils_connect_params(wpa_s);
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
 }
 
 
@@ -1179,7 +1199,6 @@
 		return -1;
 	}
 
-#ifdef CONFIG_IEEE80211W
 	if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
 	    wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
 		wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
@@ -1187,7 +1206,6 @@
 			"reject");
 		return -1;
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	return 0;
 }
@@ -1225,14 +1243,16 @@
 {
 	struct wpa_ie_data ie;
 	int sel, proto;
-	const u8 *bss_wpa, *bss_rsn, *bss_osen;
+	const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
 
 	if (bss) {
 		bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 		bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+		bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
 		bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
-	} else
-		bss_wpa = bss_rsn = bss_osen = NULL;
+	} else {
+		bss_wpa = bss_rsn = bss_rsnx = bss_osen = NULL;
+	}
 
 	if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
 	    wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
@@ -1312,7 +1332,6 @@
 			ie.group_cipher = ssid->group_cipher;
 			ie.pairwise_cipher = ssid->pairwise_cipher;
 			ie.key_mgmt = ssid->key_mgmt;
-#ifdef CONFIG_IEEE80211W
 			ie.mgmt_group_cipher = 0;
 			if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 				if (ssid->group_mgmt_cipher &
@@ -1331,7 +1350,6 @@
 					ie.mgmt_group_cipher =
 						WPA_CIPHER_AES_128_CMAC;
 			}
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OWE
 			if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
 			    !ssid->owe_only &&
@@ -1351,12 +1369,10 @@
 	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
 		"pairwise %d key_mgmt %d proto %d",
 		ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
-#ifdef CONFIG_IEEE80211W
 	if (ssid->ieee80211w) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
 			ie.mgmt_group_cipher);
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	wpa_s->wpa_proto = proto;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
@@ -1367,7 +1383,9 @@
 		if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
 					 bss_wpa ? 2 + bss_wpa[1] : 0) ||
 		    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
-					 bss_rsn ? 2 + bss_rsn[1] : 0))
+					 bss_rsn ? 2 + bss_rsn[1] : 0) ||
+		    wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx,
+					bss_rsnx ? 2 + bss_rsnx[1] : 0))
 			return -1;
 	}
 
@@ -1413,13 +1431,15 @@
 	if (0) {
 #ifdef CONFIG_IEEE80211R
 #ifdef CONFIG_SHA384
-	} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+	} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
+		   os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"WPA: using KEY_MGMT FT/802.1X-SHA384");
-		if (pmksa_cache_get_current(wpa_s->wpa)) {
-			/* PMKSA caching with FT is not fully functional, so
-			 * disable the case for now. */
+		if (!ssid->ft_eap_pmksa_caching &&
+		    pmksa_cache_get_current(wpa_s->wpa)) {
+			/* PMKSA caching with FT may have interoperability
+			 * issues, so disable that case by default for now. */
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"WPA: Disable PMKSA caching for FT/802.1X connection");
 			pmksa_cache_clear_current(wpa_s->wpa);
@@ -1455,12 +1475,14 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R
-	} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
+	} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X) &&
+		   os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
-		if (pmksa_cache_get_current(wpa_s->wpa)) {
-			/* PMKSA caching with FT is not fully functional, so
-			 * disable the case for now. */
+		if (!ssid->ft_eap_pmksa_caching &&
+		    pmksa_cache_get_current(wpa_s->wpa)) {
+			/* PMKSA caching with FT may have interoperability
+			 * issues, so disable that case by default for now. */
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"WPA: Disable PMKSA caching for FT/802.1X connection");
 			pmksa_cache_clear_current(wpa_s->wpa);
@@ -1484,7 +1506,6 @@
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
 	} else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
 		wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1493,7 +1514,6 @@
 		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"WPA: using KEY_MGMT PSK with SHA256");
-#endif /* CONFIG_IEEE80211W */
 	} else if (sel & WPA_KEY_MGMT_IEEE8021X) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
@@ -1524,7 +1544,13 @@
 			 wpa_s->pairwise_cipher);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
 
-#ifdef CONFIG_IEEE80211W
+	if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+	    wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"RSN: Management frame protection required but the selected AP does not enable it");
+		return -1;
+	}
+
 	sel = ie.mgmt_group_cipher;
 	if (ssid->group_mgmt_cipher)
 		sel &= ssid->group_mgmt_cipher;
@@ -1558,16 +1584,23 @@
 			 wpa_s->mgmt_group_cipher);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
 			 wpas_get_ssid_pmf(wpa_s, ssid));
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_OCV
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
 #endif /* CONFIG_OCV */
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, wpa_s->conf->sae_pwe);
 
 	if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
 		return -1;
 	}
 
+	wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
+	if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
+					   &wpa_s->rsnxe_len)) {
+		wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE");
+		return -1;
+	}
+
 	if (0) {
 #ifdef CONFIG_DPP
 	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
@@ -1709,7 +1742,8 @@
 	case 2: /* Bits 16-23 */
 #ifdef CONFIG_WNM
 		*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
-		*pos |= 0x08; /* Bit 19 - BSS Transition */
+		if (!wpa_s->disable_mbo_oce && !wpa_s->conf->disable_btm)
+			*pos |= 0x08; /* Bit 19 - BSS Transition */
 #endif /* CONFIG_WNM */
 		break;
 	case 3: /* Bits 24-31 */
@@ -1915,6 +1949,58 @@
 }
 
 
+static void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_SAE
+	int *groups = conf->sae_groups;
+	int default_groups[] = { 19, 20, 21, 0 };
+	const char *password;
+
+	if (!groups || groups[0] <= 0)
+		groups = default_groups;
+
+	password = ssid->sae_password;
+	if (!password)
+		password = ssid->passphrase;
+
+	if (conf->sae_pwe == 0 || !password) {
+		/* PT derivation not needed */
+		sae_deinit_pt(ssid->pt);
+		ssid->pt = NULL;
+		return;
+	}
+
+	if (ssid->pt)
+		return; /* PT already derived */
+	ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
+				 (const u8 *) password, os_strlen(password),
+				 ssid->sae_password_id);
+#endif /* CONFIG_SAE */
+}
+
+
+static void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s)
+{
+#if defined(CONFIG_SAE) && defined(CONFIG_SME)
+	os_free(wpa_s->sme.sae_rejected_groups);
+	wpa_s->sme.sae_rejected_groups = NULL;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->extra_sae_rejected_groups) {
+		int i, *groups = wpa_s->extra_sae_rejected_groups;
+
+		for (i = 0; groups[i]; i++) {
+			wpa_printf(MSG_DEBUG,
+				   "TESTING: Indicate rejection of an extra SAE group %d",
+				   groups[i]);
+			int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
+					     groups[i]);
+		}
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE && CONFIG_SME */
+}
+
+
 static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
 
 /**
@@ -1961,6 +2047,11 @@
 		} else if (wpa_s->current_bss && wpa_s->current_bss != bss) {
 			os_get_reltime(&wpa_s->roam_start);
 		}
+	} else {
+#ifdef CONFIG_SAE
+		wpa_s_clear_sae_rejected(wpa_s);
+		wpa_s_setup_sae_pt(wpa_s->conf, ssid);
+#endif /* CONFIG_SAE */
 	}
 
 	if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
@@ -2062,8 +2153,12 @@
 				bss->ie_len);
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_MBO
+	wpas_mbo_check_pmf(wpa_s, bss, ssid);
+#endif /* CONFIG_MBO */
+
 	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
-	    ssid->mode == IEEE80211_MODE_INFRA) {
+	    ssid->mode == WPAS_MODE_INFRA) {
 		sme_authenticate(wpa_s, bss, ssid);
 		return;
 	}
@@ -2137,6 +2232,7 @@
 			  const struct wpa_ssid *ssid,
 			  struct hostapd_freq_params *freq)
 {
+	int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
 	enum hostapd_hw_mode hw_mode;
 	struct hostapd_hw_modes *mode = NULL;
 	int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
@@ -2149,6 +2245,7 @@
 	struct hostapd_freq_params vht_freq;
 	int chwidth, seg0, seg1;
 	u32 vht_caps = 0;
+	int is_24ghz;
 
 	freq->freq = ssid->frequency;
 
@@ -2200,6 +2297,9 @@
 	if (!mode)
 		return;
 
+	is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+		hw_mode == HOSTAPD_MODE_IEEE80211B;
+
 #ifdef CONFIG_HT_OVERRIDES
 	if (ssid->disable_ht) {
 		freq->ht_enabled = 0;
@@ -2211,6 +2311,10 @@
 	if (!freq->ht_enabled)
 		return;
 
+	/* Allow HE on 2.4 GHz without VHT: see nl80211_put_freq_params() */
+	if (is_24ghz)
+		freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported;
+
 	/* Setup higher BW only for 5 GHz */
 	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
 		return;
@@ -2279,8 +2383,7 @@
 			return;
 		}
 
-		res = check_40mhz_5g(mode, scan_res, pri_chan->chan,
-				     sec_chan->chan);
+		res = check_40mhz_5g(scan_res, pri_chan, sec_chan);
 		switch (res) {
 		case 0:
 			/* Back to HT20 */
@@ -2331,6 +2434,9 @@
 	if (!vht_freq.vht_enabled)
 		return;
 
+	/* Enable HE for VHT */
+	vht_freq.he_enabled = mode->he_capab[ieee80211_mode].he_supported;
+
 	/* setup center_freq1, bandwidth */
 	for (j = 0; j < ARRAY_SIZE(vht80); j++) {
 		if (freq->channel >= vht80[j] &&
@@ -2353,11 +2459,11 @@
 			return;
 	}
 
-	chwidth = VHT_CHANWIDTH_80MHZ;
+	chwidth = CHANWIDTH_80MHZ;
 	seg0 = vht80[j] + 6;
 	seg1 = 0;
 
-	if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+	if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) {
 		/* setup center_freq2, bandwidth */
 		for (k = 0; k < ARRAY_SIZE(vht80); k++) {
 			/* Only accept 80 MHz segments separated by a gap */
@@ -2376,27 +2482,27 @@
 					continue;
 
 				/* Found a suitable second segment for 80+80 */
-				chwidth = VHT_CHANWIDTH_80P80MHZ;
+				chwidth = CHANWIDTH_80P80MHZ;
 				vht_caps |=
 					VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 				seg1 = vht80[k] + 6;
 			}
 
-			if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+			if (chwidth == CHANWIDTH_80P80MHZ)
 				break;
 		}
-	} else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) {
+	} else if (ssid->max_oper_chwidth == CHANWIDTH_160MHZ) {
 		if (freq->freq == 5180) {
-			chwidth = VHT_CHANWIDTH_160MHZ;
+			chwidth = CHANWIDTH_160MHZ;
 			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 			seg0 = 50;
 		} else if (freq->freq == 5520) {
-			chwidth = VHT_CHANWIDTH_160MHZ;
+			chwidth = CHANWIDTH_160MHZ;
 			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 			seg0 = 114;
 		}
-	} else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_USE_HT) {
-		chwidth = VHT_CHANWIDTH_USE_HT;
+	} else if (ssid->max_oper_chwidth == CHANWIDTH_USE_HT) {
+		chwidth = CHANWIDTH_USE_HT;
 		seg0 = vht80[j] + 2;
 #ifdef CONFIG_HT_OVERRIDES
 		if (ssid->disable_ht40)
@@ -2405,10 +2511,12 @@
 	}
 
 	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
-				    freq->channel, freq->ht_enabled,
-				    vht_freq.vht_enabled,
+				    freq->channel, ssid->enable_edmg,
+				    ssid->edmg_channel, freq->ht_enabled,
+				    vht_freq.vht_enabled, freq->he_enabled,
 				    freq->sec_channel_offset,
-				    chwidth, seg0, seg1, vht_caps) != 0)
+				    chwidth, seg0, seg1, vht_caps,
+				    &mode->he_capab[ieee80211_mode]) != 0)
 		return;
 
 	*freq = vht_freq;
@@ -2807,7 +2915,7 @@
 
 #ifdef CONFIG_MBO
 	mbo_ie = bss ? wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE) : NULL;
-	if (mbo_ie) {
+	if (!wpa_s->disable_mbo_oce && mbo_ie) {
 		int len;
 
 		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
@@ -2922,6 +3030,23 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->rsnxe_override_assoc &&
+	    wpabuf_len(wpa_s->rsnxe_override_assoc) <=
+	    max_wpa_ie_len - wpa_ie_len) {
+		wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override");
+		os_memcpy(wpa_ie + wpa_ie_len,
+			  wpabuf_head(wpa_s->rsnxe_override_assoc),
+			  wpabuf_len(wpa_s->rsnxe_override_assoc));
+		wpa_ie_len += wpabuf_len(wpa_s->rsnxe_override_assoc);
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (wpa_s->rsnxe_len > 0 &&
+	    wpa_s->rsnxe_len <= max_wpa_ie_len - wpa_ie_len) {
+		os_memcpy(wpa_ie + wpa_ie_len, wpa_s->rsnxe, wpa_s->rsnxe_len);
+		wpa_ie_len += wpa_s->rsnxe_len;
+	}
+
 	if (ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
 
@@ -2947,6 +3072,24 @@
 }
 
 
+#ifdef CONFIG_OWE
+static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_driver_associate_params params;
+	u8 *wpa_ie;
+
+	os_memset(&params, 0, sizeof(params));
+	wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss,
+					 wpa_s->current_ssid, &params, NULL);
+	if (!wpa_ie)
+		return;
+
+	wpa_drv_update_connect_params(wpa_s, &params, WPA_DRV_UPDATE_ASSOC_IES);
+	os_free(wpa_ie);
+}
+#endif /* CONFIG_OWE */
+
+
 #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
 static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s)
 {
@@ -2975,6 +3118,117 @@
 #endif /* CONFIG_FILS && IEEE8021X_EAPOL */
 
 
+static u8 wpa_ie_get_edmg_oper_chans(const u8 *edmg_ie)
+{
+	if (!edmg_ie || edmg_ie[1] < 6)
+		return 0;
+	return edmg_ie[EDMG_BSS_OPERATING_CHANNELS_OFFSET];
+}
+
+
+static u8 wpa_ie_get_edmg_oper_chan_width(const u8 *edmg_ie)
+{
+	if (!edmg_ie || edmg_ie[1] < 6)
+		return 0;
+	return edmg_ie[EDMG_OPERATING_CHANNEL_WIDTH_OFFSET];
+}
+
+
+/* Returns the intersection of two EDMG configurations.
+ * Note: The current implementation is limited to CB2 only (CB1 included),
+ * i.e., the implementation supports up to 2 contiguous channels.
+ * For supporting non-contiguous (aggregated) channels and for supporting
+ * CB3 and above, this function will need to be extended.
+ */
+static struct ieee80211_edmg_config
+get_edmg_intersection(struct ieee80211_edmg_config a,
+		      struct ieee80211_edmg_config b,
+		      u8 primary_channel)
+{
+	struct ieee80211_edmg_config result;
+	int i, contiguous = 0;
+	int max_contiguous = 0;
+
+	result.channels = b.channels & a.channels;
+	if (!result.channels) {
+		wpa_printf(MSG_DEBUG,
+			   "EDMG not possible: cannot intersect channels 0x%x and 0x%x",
+			   a.channels, b.channels);
+		goto fail;
+	}
+
+	if (!(result.channels & BIT(primary_channel - 1))) {
+		wpa_printf(MSG_DEBUG,
+			   "EDMG not possible: the primary channel %d is not one of the intersected channels 0x%x",
+			   primary_channel, result.channels);
+		goto fail;
+	}
+
+	/* Find max contiguous channels */
+	for (i = 0; i < 6; i++) {
+		if (result.channels & BIT(i))
+			contiguous++;
+		else
+			contiguous = 0;
+
+		if (contiguous > max_contiguous)
+			max_contiguous = contiguous;
+	}
+
+	/* Assuming AP and STA supports ONLY contiguous channels,
+	 * bw configuration can have value between 4-7.
+	 */
+	if ((b.bw_config < a.bw_config))
+		result.bw_config = b.bw_config;
+	else
+		result.bw_config = a.bw_config;
+
+	if ((max_contiguous >= 2 && result.bw_config < EDMG_BW_CONFIG_5) ||
+	    (max_contiguous >= 1 && result.bw_config < EDMG_BW_CONFIG_4)) {
+		wpa_printf(MSG_DEBUG,
+			   "EDMG not possible: not enough contiguous channels %d for supporting CB1 or CB2",
+			   max_contiguous);
+		goto fail;
+	}
+
+	return result;
+
+fail:
+	result.channels = 0;
+	result.bw_config = 0;
+	return result;
+}
+
+
+static struct ieee80211_edmg_config
+get_supported_edmg(struct wpa_supplicant *wpa_s,
+		   struct hostapd_freq_params *freq,
+		   struct ieee80211_edmg_config request_edmg)
+{
+	enum hostapd_hw_mode hw_mode;
+	struct hostapd_hw_modes *mode = NULL;
+	u8 primary_channel;
+
+	if (!wpa_s->hw.modes)
+		goto fail;
+
+	hw_mode = ieee80211_freq_to_chan(freq->freq, &primary_channel);
+	if (hw_mode == NUM_HOSTAPD_MODES)
+		goto fail;
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, hw_mode);
+	if (!mode)
+		goto fail;
+
+	return get_edmg_intersection(mode->edmg, request_edmg, primary_channel);
+
+fail:
+	request_edmg.channels = 0;
+	request_edmg.bw_config = 0;
+	return request_edmg;
+}
+
+
 #ifdef CONFIG_MBO
 void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s)
 {
@@ -3010,6 +3264,7 @@
 	struct wpa_ssid *ssid = cwork->ssid;
 	struct wpa_supplicant *wpa_s = work->wpa_s;
 	u8 *wpa_ie;
+	const u8 *edmg_ie_oper;
 	int use_crypt, ret, i, bssid_changed;
 	unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
 	struct wpa_driver_associate_params params;
@@ -3102,6 +3357,8 @@
 	/* Starting new association, so clear the possibly used WPA IE from the
 	 * previous association. */
 	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+	wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
+	wpa_s->rsnxe_len = 0;
 
 	wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);
 	if (!wpa_ie) {
@@ -3193,6 +3450,71 @@
 			params.beacon_int = wpa_s->conf->beacon_int;
 	}
 
+	if (bss && ssid->enable_edmg)
+		edmg_ie_oper = get_ie_ext((const u8 *) (bss + 1), bss->ie_len,
+					  WLAN_EID_EXT_EDMG_OPERATION);
+	else
+		edmg_ie_oper = NULL;
+
+	if (edmg_ie_oper) {
+		params.freq.edmg.channels =
+			wpa_ie_get_edmg_oper_chans(edmg_ie_oper);
+		params.freq.edmg.bw_config =
+			wpa_ie_get_edmg_oper_chan_width(edmg_ie_oper);
+		wpa_printf(MSG_DEBUG,
+			   "AP supports EDMG channels 0x%x, bw_config %d",
+			   params.freq.edmg.channels,
+			   params.freq.edmg.bw_config);
+
+		/* User may ask for specific EDMG channel for EDMG connection
+		 * (must be supported by AP)
+		 */
+		if (ssid->edmg_channel) {
+			struct ieee80211_edmg_config configured_edmg;
+			enum hostapd_hw_mode hw_mode;
+			u8 primary_channel;
+
+			hw_mode = ieee80211_freq_to_chan(bss->freq,
+							 &primary_channel);
+			if (hw_mode == NUM_HOSTAPD_MODES)
+				goto edmg_fail;
+
+			hostapd_encode_edmg_chan(ssid->enable_edmg,
+						 ssid->edmg_channel,
+						 primary_channel,
+						 &configured_edmg);
+
+			if (ieee802_edmg_is_allowed(params.freq.edmg,
+						    configured_edmg)) {
+				params.freq.edmg = configured_edmg;
+				wpa_printf(MSG_DEBUG,
+					   "Use EDMG channel %d for connection",
+					   ssid->edmg_channel);
+			} else {
+			edmg_fail:
+				params.freq.edmg.channels = 0;
+				params.freq.edmg.bw_config = 0;
+				wpa_printf(MSG_WARNING,
+					   "EDMG channel %d not supported by AP, fallback to DMG",
+					   ssid->edmg_channel);
+			}
+		}
+
+		if (params.freq.edmg.channels) {
+			wpa_printf(MSG_DEBUG,
+				   "EDMG before: channels 0x%x, bw_config %d",
+				   params.freq.edmg.channels,
+				   params.freq.edmg.bw_config);
+			params.freq.edmg = get_supported_edmg(wpa_s,
+							      &params.freq,
+							      params.freq.edmg);
+			wpa_printf(MSG_DEBUG,
+				   "EDMG after: channels 0x%x, bw_config %d",
+				   params.freq.edmg.channels,
+				   params.freq.edmg.bw_config);
+		}
+	}
+
 	params.pairwise_suite = cipher_pairwise;
 	params.group_suite = cipher_group;
 	params.mgmt_group_suite = cipher_group_mgmt;
@@ -3221,7 +3543,7 @@
 	     params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
 	     params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
 	     params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192))
-		params.req_key_mgmt_offload = 1;
+		params.req_handshake_offload = 1;
 
 	if (wpa_s->conf->key_mgmt_offload) {
 		if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
@@ -3243,7 +3565,6 @@
 
 	params.drop_unencrypted = use_crypt;
 
-#ifdef CONFIG_IEEE80211W
 	params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
 	if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
 		const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
@@ -3262,7 +3583,6 @@
 #endif /* CONFIG_OWE */
 		}
 	}
-#endif /* CONFIG_IEEE80211W */
 
 	params.p2p = ssid->p2p_group;
 
@@ -3422,16 +3742,17 @@
  * current AP.
  */
 void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
-				   int reason_code)
+				   u16 reason_code)
 {
 	u8 *addr = NULL;
 	union wpa_event_data event;
 	int zero_addr = 0;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR
-		" pending_bssid=" MACSTR " reason=%d state=%s",
+		" pending_bssid=" MACSTR " reason=%d (%s) state=%s",
 		MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
-		reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
+		reason_code, reason2str(reason_code),
+		wpa_supplicant_state_txt(wpa_s->wpa_state));
 
 	if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
 	    (wpa_s->wpa_state == WPA_AUTHENTICATING ||
@@ -3473,7 +3794,7 @@
 	if (addr) {
 		wpa_drv_deauthenticate(wpa_s, addr, reason_code);
 		os_memset(&event, 0, sizeof(event));
-		event.deauth_info.reason_code = (u16) reason_code;
+		event.deauth_info.reason_code = reason_code;
 		event.deauth_info.locally_generated = 1;
 		wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event);
 		if (zero_addr)
@@ -3754,9 +4075,12 @@
 
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
+	wpa_s_clear_sae_rejected(wpa_s);
 	wpa_s->last_owe_group = 0;
-	if (ssid)
+	if (ssid) {
 		ssid->owe_transition_bss_select_count = 0;
+		wpa_s_setup_sae_pt(wpa_s->conf, ssid);
+	}
 
 	if (wpa_s->connect_without_scan ||
 	    wpa_supplicant_fast_associate(wpa_s) != 1) {
@@ -4228,7 +4552,7 @@
 	     !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
 	     wpa_s->wpa_state != WPA_COMPLETED) &&
 	    (wpa_s->current_ssid == NULL ||
-	     wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) {
+	     wpa_s->current_ssid->mode != WPAS_MODE_IBSS)) {
 		/* Timeout for completing IEEE 802.1X and WPA authentication */
 		int timeout = 10;
 
@@ -4335,6 +4659,11 @@
 	wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
 	wpas_wps_update_mac_addr(wpa_s);
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst)
+		fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr);
+#endif /* CONFIG_FST */
+
 	return 0;
 }
 
@@ -5943,7 +6272,7 @@
 	hs20_init(wpa_s);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_MBO
-	if (wpa_s->conf->oce) {
+	if (!wpa_s->disable_mbo_oce && wpa_s->conf->oce) {
 		if ((wpa_s->conf->oce & OCE_STA) &&
 		    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
 			wpa_s->enable_oce = OCE_STA;
@@ -6041,6 +6370,7 @@
 	}
 
 	os_free(wpa_s->ssids_from_scan_req);
+	os_free(wpa_s->last_scan_freqs);
 
 	os_free(wpa_s);
 }
@@ -6630,6 +6960,9 @@
 				   wpa_s->conf->wowlan_triggers);
 	}
 
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM)
+		wpa_supplicant_set_default_scan_ies(wpa_s);
+
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -6907,8 +7240,8 @@
 			wpa_s->reassociate = 1;
 		break;
 	case WPA_CTRL_REQ_EAP_PIN:
-		str_clear_free(eap->pin);
-		eap->pin = os_strdup(value);
+		str_clear_free(eap->cert.pin);
+		eap->cert.pin = os_strdup(value);
 		eap->pending_req_pin = 0;
 		if (ssid == wpa_s->current_ssid)
 			wpa_s->reassociate = 1;
@@ -6922,8 +7255,8 @@
 		eap->pending_req_otp_len = 0;
 		break;
 	case WPA_CTRL_REQ_EAP_PASSPHRASE:
-		str_clear_free(eap->private_key_passwd);
-		eap->private_key_passwd = os_strdup(value);
+		str_clear_free(eap->cert.private_key_passwd);
+		eap->cert.private_key_passwd = os_strdup(value);
 		eap->pending_req_passphrase = 0;
 		if (ssid == wpa_s->current_ssid)
 			wpa_s->reassociate = 1;
@@ -7010,7 +7343,6 @@
 
 int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
-#ifdef CONFIG_IEEE80211W
 	if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
 		if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL &&
 		    !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) {
@@ -7039,9 +7371,6 @@
 	}
 
 	return ssid->ieee80211w;
-#else /* CONFIG_IEEE80211W */
-	return NO_MGMT_FRAME_PROTECTION;
-#endif /* CONFIG_IEEE80211W */
 }
 
 
@@ -7517,3 +7846,66 @@
 
 	return 1;
 }
+
+
+int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
+				       unsigned int type, const u8 *addr,
+				       const u8 *mask)
+{
+	if ((addr && !mask) || (!addr && mask)) {
+		wpa_printf(MSG_INFO,
+			   "MAC_ADDR_RAND_SCAN invalid addr/mask combination");
+		return -1;
+	}
+
+	if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
+		wpa_printf(MSG_INFO,
+			   "MAC_ADDR_RAND_SCAN cannot allow multicast address");
+		return -1;
+	}
+
+	if (type & MAC_ADDR_RAND_SCAN) {
+		if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
+						addr, mask))
+			return -1;
+	}
+
+	if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+		if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
+						addr, mask))
+			return -1;
+
+		if (wpa_s->sched_scanning && !wpa_s->pno)
+			wpas_scan_restart_sched_scan(wpa_s);
+	}
+
+	if (type & MAC_ADDR_RAND_PNO) {
+		if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO,
+						addr, mask))
+			return -1;
+
+		if (wpa_s->pno) {
+			wpas_stop_pno(wpa_s);
+			wpas_start_pno(wpa_s);
+		}
+	}
+
+	return 0;
+}
+
+
+int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
+					unsigned int type)
+{
+	wpas_mac_addr_rand_scan_clear(wpa_s, type);
+	if (wpa_s->pno) {
+		if (type & MAC_ADDR_RAND_PNO) {
+			wpas_stop_pno(wpa_s);
+			wpas_start_pno(wpa_s);
+		}
+	} else if (wpa_s->sched_scanning && (type & MAC_ADDR_RAND_SCHED_SCAN)) {
+		wpas_scan_restart_sched_scan(wpa_s);
+	}
+
+	return 0;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index a9205f0..328f91a 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -311,6 +311,26 @@
 # by executing the WPS protocol.
 #wps_priority=0
 
+# Device Provisioning Protocol (DPP) parameters
+#
+# How to process DPP configuration
+# 0 = report received configuration to an external program for
+#     processing; do not generate any network profile internally (default)
+# 1 = report received configuration to an external program and generate
+#     a network profile internally, but do not automatically connect
+#     to the created (disabled) profile; the network profile id is
+#     reported to external programs
+# 2 = report received configuration to an external program, generate
+#     a network profile internally, try to connect to the created
+#     profile automatically
+#dpp_config_processing=0
+#
+# Name for Enrollee's DPP Configuration Request
+#dpp_name=Test
+#
+# MUD URL for Enrollee's DPP Configuration Request (optional)
+#dpp_mud_url=https://example.com/mud
+
 # Maximum number of BSS entries to keep in memory
 # Default: 200
 # This can be used to limit memory use on the BSS entries (cached scan
@@ -405,6 +425,14 @@
 # since all implementations are required to support group 19.
 #sae_groups=19 20 21
 
+# SAE mechanism for PWE derivation
+# 0 = hunting-and-pecking loop only (default)
+# 1 = hash-to-element only
+# 2 = both hunting-and-pecking loop and hash-to-element enabled
+# Note: The default value is likely to change from 0 to 2 once the new
+# hash-to-element mechanism has received more interoperability testing.
+#sae_pwe=0
+
 # Default value for DTIM period (if not overridden in network block)
 #dtim_period=2
 
@@ -927,7 +955,8 @@
 # management frames) certification program are:
 # PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
 # PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
-# (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used)
+# (and similarly for WPA-PSK and WPA-PSK-SHA256 if WPA2-Personal is used)
+# WPA3-Personal-only mode: ieee80211w=2 and key_mgmt=SAE
 #
 # ocv: whether operating channel validation is enabled
 # This is a countermeasure against multi-channel man-in-the-middle attacks.
@@ -1058,6 +1087,14 @@
 # 0 = disabled (default unless changed with the global okc parameter)
 # 1 = enabled
 #
+# ft_eap_pmksa_caching:
+# Whether FT-EAP PMKSA caching is allowed
+# 0 = do not try to use PMKSA caching with FT-EAP (default)
+# 1 = try to use PMKSA caching with FT-EAP
+# This controls whether to try to use PMKSA caching with FT-EAP for the
+# FT initial mobility domain association.
+#ft_eap_pmksa_caching=0
+#
 # wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or
 # hex without quotation, e.g., 0102030405)
 # wep_tx_keyidx: Default WEP key index (TX) (0..3)
@@ -1315,6 +1352,12 @@
 #	certificate. See altsubject_match documentation for more details.
 # domain_suffix_match2: Constraint for server domain name. See
 #	domain_suffix_match for more details.
+# ocsp2: See ocsp for more details.
+#
+# Separate machine credentials can be configured for EAP-TEAP Phase 2 with
+# "machine_" prefix (e.g., "machine_identity") in the configuration parameters.
+# See the parameters without that prefix for more details on the meaning and
+# format of each such parameter.
 #
 # fragment_size: Maximum EAP fragment size in bytes (default 1398).
 #	This value limits the fragment size for EAP methods that support
@@ -1508,6 +1551,22 @@
 # Transitioning between states).
 #fst_llt=100
 
+# BSS Transition Management
+# disable_btm - Disable BSS transition management in STA
+# Set to 0 to enable BSS transition management (default behavior)
+# Set to 1 to disable BSS transition management
+#disable_btm=0
+
+# Enable EDMG capability in STA/AP mode, default value is false
+#enable_edmg=1
+
+# This value is used to configure the channel bonding feature.
+# Default value is 0.
+# Relevant only if enable_edmg is true
+# In AP mode it defines the EDMG channel to use for AP operation.
+# In STA mode it defines the EDMG channel for connection (if supported by AP).
+#edmg_channel=9
+
 # Example blocks:
 
 # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7eef32c..967298b 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -614,6 +614,9 @@
 	int eapol_received; /* number of EAPOL packets received after the
 			     * previous association event */
 
+	u8 rsnxe[20];
+	size_t rsnxe_len;
+
 	struct scard_data *scard;
 	char imsi[20];
 	int mnc_len;
@@ -700,6 +703,10 @@
 
 	struct wpa_ssid_value *ssids_from_scan_req;
 	unsigned int num_ssids_from_scan_req;
+	int *last_scan_freqs;
+	unsigned int num_last_scan_freqs;
+	unsigned int suitable_network;
+	unsigned int no_suitable_network;
 
 	u64 drv_flags;
 	unsigned int drv_enc;
@@ -752,6 +759,10 @@
 	unsigned int connection_ht:1;
 	unsigned int connection_vht:1;
 	unsigned int connection_he:1;
+	unsigned int connection_max_nss_rx:4;
+	unsigned int connection_max_nss_tx:4;
+	unsigned int connection_channel_bandwidth:5;
+	unsigned int disable_mbo_oce:1;
 
 	struct os_reltime last_mac_addr_change;
 	int last_mac_addr_style;
@@ -805,6 +816,7 @@
 		u8 ext_auth_bssid[ETH_ALEN];
 		u8 ext_auth_ssid[SSID_MAX_LEN];
 		size_t ext_auth_ssid_len;
+		int *sae_rejected_groups;
 #endif /* CONFIG_SAE */
 	} sme;
 #endif /* CONFIG_SME */
@@ -823,6 +835,7 @@
 	unsigned int mesh_if_created:1;
 	unsigned int mesh_ht_enabled:1;
 	unsigned int mesh_vht_enabled:1;
+	unsigned int mesh_he_enabled:1;
 	struct wpa_driver_mesh_join_params *mesh_params;
 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 	/* struct external_pmksa_cache::list */
@@ -921,6 +934,7 @@
 	unsigned int p2p_pd_before_go_neg:1;
 	unsigned int p2p_go_ht40:1;
 	unsigned int p2p_go_vht:1;
+	unsigned int p2p_go_edmg:1;
 	unsigned int p2p_go_he:1;
 	unsigned int user_initiated_pd:1;
 	unsigned int p2p_go_group_formation_completed:1;
@@ -1083,7 +1097,11 @@
 	u8 coloc_intf_timeout;
 #ifdef CONFIG_MBO
 	unsigned int wnm_mbo_trans_reason_present:1;
+	unsigned int wnm_mbo_cell_pref_present:1;
+	unsigned int wnm_mbo_assoc_retry_delay_present:1;
 	u8 wnm_mbo_transition_reason;
+	u8 wnm_mbo_cell_preference;
+	u16 wnm_mbo_assoc_retry_delay_sec;
 #endif /* CONFIG_MBO */
 #endif /* CONFIG_WNM */
 
@@ -1109,6 +1127,7 @@
 	unsigned int ignore_auth_resp:1;
 	unsigned int ignore_assoc_disallow:1;
 	unsigned int testing_resend_assoc:1;
+	unsigned int ignore_sae_h2e_only:1;
 	struct wpabuf *sae_commit_override;
 	enum wpa_alg last_tk_alg;
 	u8 last_tk_addr[ETH_ALEN];
@@ -1116,6 +1135,9 @@
 	u8 last_tk[WPA_TK_MAX_LEN];
 	size_t last_tk_len;
 	struct wpabuf *last_assoc_req_wpa_ie;
+	int *extra_sae_rejected_groups;
+	struct wpabuf *rsnxe_override_assoc;
+	struct wpabuf *rsnxe_override_eapol;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1218,7 +1240,7 @@
 	unsigned int dpp_listen_freq;
 	u8 dpp_allowed_roles;
 	int dpp_qr_mutual;
-	int dpp_netrole_ap;
+	int dpp_netrole;
 	int dpp_auth_ok_on_ack;
 	int dpp_in_response_listen;
 	int dpp_gas_client;
@@ -1238,6 +1260,8 @@
 	unsigned int dpp_resp_wait_time;
 	unsigned int dpp_resp_max_tries;
 	unsigned int dpp_resp_retry_time;
+	u8 dpp_last_ssid[SSID_MAX_LEN];
+	size_t dpp_last_ssid_len;
 #ifdef CONFIG_DPP2
 	struct dpp_pfs *dpp_pfs;
 #endif /* CONFIG_DPP2 */
@@ -1255,6 +1279,7 @@
 	unsigned int ieee80211ac:1;
 	unsigned int enabled_4addr_mode:1;
 	unsigned int multi_bss_support:1;
+	unsigned int drv_authorized_port:1;
 };
 
 
@@ -1295,7 +1320,7 @@
 const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
-				   int reason_code);
+				   u16 reason_code);
 
 struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id);
@@ -1390,6 +1415,8 @@
 		int add_oce_capa);
 const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr);
 const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
+void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			struct wpa_ssid *ssid);
 const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
 				 enum mbo_attr_id attr);
 int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
@@ -1413,11 +1440,18 @@
 	NOT_ALLOWED, NO_IR, ALLOWED
 };
 
-enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
-				 u8 bw);
+enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
+				 u8 channel, u8 bw);
 size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s,
 			     struct wpa_ssid *ssid,
 			     int freq, u8 *pos, size_t len);
+int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s);
+
+int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
+				       unsigned int type, const u8 *addr,
+				       const u8 *mask);
+int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
+					unsigned int type);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -1480,6 +1514,24 @@
 	return ssid->disabled == 2 && ssid->p2p_persistent_group;
 }
 
+static inline int wpas_mode_to_ieee80211_mode(enum wpas_mode mode)
+{
+	switch (mode) {
+	default:
+	case WPAS_MODE_INFRA:
+		return IEEE80211_MODE_INFRA;
+	case WPAS_MODE_IBSS:
+		return IEEE80211_MODE_IBSS;
+	case WPAS_MODE_AP:
+	case WPAS_MODE_P2P_GO:
+	case WPAS_MODE_P2P_GROUP_FORMATION:
+		return IEEE80211_MODE_AP;
+	case WPAS_MODE_MESH:
+		return IEEE80211_MODE_MESH;
+	}
+}
+
+
 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);
 
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index f55227f..fce7e5e 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -5,3 +5,4 @@
 fast_reauth=1
 pmf=1
 p2p_add_cli_chan=1
+oce=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 449e04a..d80b8f2 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -396,6 +396,10 @@
 		ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
 		if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
 			ret = -1;
+
+		ie = wpa_bss_get_ie(curr, WLAN_EID_RSNX);
+		if (wpa_sm_set_ap_rsnxe(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
+			ret = -1;
 	} else {
 		ret = -1;
 	}
@@ -464,7 +468,7 @@
 }
 
 
-static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
+static void _wpa_supplicant_deauthenticate(void *wpa_s, u16 reason_code)
 {
 	wpa_supplicant_deauthenticate(wpa_s, reason_code);
 	/* Schedule a scan to make sure we continue looking for networks */
@@ -1017,15 +1021,12 @@
 }
 
 
-static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject,
-				   const char *altsubject[], int num_altsubject,
-				   const char *cert_hash,
-				   const struct wpabuf *cert)
+static void wpa_supplicant_cert_cb(void *ctx, struct tls_cert_data *cert,
+				   const char *cert_hash)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	wpas_notify_certification(wpa_s, depth, subject, altsubject,
-				  num_altsubject, cert_hash, cert);
+	wpas_notify_certification(wpa_s, cert, cert_hash);
 }
 
 
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 5da8154..8387fc3 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -533,9 +533,7 @@
 		if (wpa_s->conf->wps_cred_add_sae &&
 		    cred->key_len != 2 * PMK_LEN) {
 			ssid->key_mgmt |= WPA_KEY_MGMT_SAE;
-#ifdef CONFIG_IEEE80211W
 			ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
-#endif /* CONFIG_IEEE80211W */
 		}
 		ssid->proto = WPA_PROTO_RSN;
 		break;
@@ -1180,6 +1178,11 @@
 				/* P2P in 60 GHz uses PBSS */
 				ssid->pbss = 1;
 			}
+			if (wpa_s->go_params->edmg &&
+			    wpas_p2p_try_edmg_channel(wpa_s,
+						      wpa_s->go_params) == 0)
+				ssid->enable_edmg = 1;
+
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 					  "SSID", ssid->ssid, ssid->ssid_len);
 		}
@@ -1263,6 +1266,11 @@
 				/* P2P in 60 GHz uses PBSS */
 				ssid->pbss = 1;
 			}
+			if (wpa_s->go_params->edmg &&
+			    wpas_p2p_try_edmg_channel(wpa_s,
+						      wpa_s->go_params) == 0)
+				ssid->enable_edmg = 1;
+
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 					  "SSID", ssid->ssid, ssid->ssid_len);
 		}
@@ -1286,6 +1294,10 @@
 		wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
 		return -1;
 	}
+
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER)
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_PIN_ACTIVE);
+
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
@@ -1351,6 +1363,7 @@
 			wpas_clear_wps(wpa_s);
 	}
 
+	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CANCEL);
 	wpa_s->after_wps = 0;
 
 	return 0;
@@ -2691,7 +2704,7 @@
 			 (attr.rf_bands == NULL ||
 			  *attr.rf_bands & WPS_RF_50GHZ))
 			freq = 5000 + 5 * chan;
-		else if (chan >= 1 && chan <= 4 &&
+		else if (chan >= 1 && chan <= 6 &&
 			 (attr.rf_bands == NULL ||
 			  *attr.rf_bands & WPS_RF_60GHZ))
 			freq = 56160 + 2160 * chan;