am b73408d1: am fdb708a3: P2P: Validate SSID element length before copying it

* commit 'b73408d1cfdbad09b1d470e8896e2abf7077d702':
  P2P: Validate SSID element length before copying it
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
new file mode 100644
index 0000000..d20a556
--- /dev/null
+++ b/CONTRIBUTIONS
@@ -0,0 +1,143 @@
+Contributions to hostap.git
+---------------------------
+
+This software is distributed under a permissive open source license to
+allow it to be used in any projects, whether open source or proprietary.
+Contributions to the project are welcome and it is important to maintain
+clear record of contributions and terms under which they are licensed.
+To help with this, following procedure is used to allow acceptance and
+recording of the terms.
+
+All contributions are expected to be licensed under the modified BSD
+license (see below). Acknowledgment of the terms is tracked through
+inclusion of Signed-off-by tag in the contributions at the end of the
+commit log message. This tag indicates that the contributor agrees with
+the Developer Certificate of Origin (DCO) version 1.1 terms (see below;
+also available from http://developercertificate.org/).
+
+
+The current requirements for contributions to hostap.git
+--------------------------------------------------------
+
+To indicate your acceptance of Developer's Certificate of Origin 1.1
+terms, please add the following line to the end of the commit message
+for each contribution you make to the project:
+
+Signed-off-by: Your Name <your@email.example.org>
+
+using your real name. Pseudonyms or anonymous contributions cannot
+unfortunately be accepted.
+
+
+History of license and contributions terms
+------------------------------------------
+
+Until February 11, 2012, in case of most files in hostap.git, "under the
+open source license indicated in the file" means that the contribution
+is licensed both under GPL v2 and modified BSD license (see below) and
+the choice between these licenses is given to anyone who redistributes
+or uses the software. As such, the contribution has to be licensed under
+both options to allow this choice.
+
+As of February 11, 2012, the project has chosen to use only the BSD
+license option for future distribution. As such, the GPL v2 license
+option is no longer used and the contributions are not required to be
+licensed until GPL v2. In case of most files in hostap.git, "under the
+open source license indicated in the file" means that the contribution
+is licensed under the modified BSD license (see below).
+
+Until February 13, 2014, the project used an extended version of the DCO
+that included the identical items (a) through (d) from DCO 1.1 and an
+additional item (e):
+
+(e) The contribution can be licensed under the modified BSD license
+    as shown below even in case of files that are currently licensed
+    under other terms.
+
+This was used during the period when some of the files included the old
+license terms. Acceptance of this extended DCO version was indicated
+with a Signed-hostap tag in the commit message. This additional item (e)
+was used to collect explicit approval to license the contribution with
+only the modified BSD license (see below), i.e., without the GPL v2
+option. This was done to allow simpler licensing terms to be used in the
+future. It should be noted that the modified BSD license is compatible
+with GNU GPL and as such, this possible move to simpler licensing option
+does not prevent use of this software in GPL projects.
+
+
+===[ start quote from http://developercertificate.org/ ]=======================
+
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+660 York Street, Suite 102,
+San Francisco, CA 94110 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+    have the right to submit it under the open source license
+    indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+    of my knowledge, is covered under an appropriate open source
+    license and I have the right under that license to submit that
+    work with modifications, whether created in whole or in part
+    by me, under the same open source license (unless I am
+    permitted to submit under a different license), as indicated
+    in the file; or
+
+(c) The contribution was provided directly to me by some other
+    person who certified (a), (b) or (c) and I have not modified
+    it.
+
+(d) I understand and agree that this project and the contribution
+    are public and that a record of the contribution (including all
+    personal information I submit with it, including my sign-off) is
+    maintained indefinitely and may be redistributed consistent with
+    this project or the open source license(s) involved.
+
+===[ end quote from http://developercertificate.org/ ]=========================
+
+
+The license terms used for hostap.git files
+-------------------------------------------
+
+Modified BSD license (no advertisement clause):
+
+Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name(s) of the above-listed copyright holder(s) nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 7ecbc65..dc7d557 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -59,3 +59,6 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/wpa_supplicant_intermediates/*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/wpa_supplicant_intermediates/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
diff --git a/README b/README
index 805c6cf..8de14a6 100644
--- a/README
+++ b/README
@@ -1,12 +1,15 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 These programs are licensed under the BSD license (the one with
 advertisement clause removed).
 
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
+
 
 This package may include either wpa_supplicant, hostapd, or both. See
 README file respective subdirectories (wpa_supplicant/README or
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 695e99c..edaf6fc 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -24,16 +24,13 @@
 # Set Android log name
 L_CFLAGS += -DANDROID_LOG_NAME=\"hostapd\"
 
-ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
-L_CFLAGS += -DANDROID_P2P
-endif
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
 
-ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
+# Set Android extended P2P functionality
 L_CFLAGS += -DANDROID_P2P
-endif
-
-ifeq ($(BOARD_WLAN_DEVICE), mrvl)
-L_CFLAGS += -DANDROID_P2P
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_P2P_STUB
 endif
 
 # Use Android specific directory for control interface sockets
@@ -45,17 +42,18 @@
 L_CFLAGS += -mabi=aapcs-linux
 endif
 
-# To allow non-ASCII characters in SSID
-L_CFLAGS += -DWPA_UNICODE_SSID
-
 INCLUDES = $(LOCAL_PATH)
 INCLUDES += $(LOCAL_PATH)/src
 INCLUDES += $(LOCAL_PATH)/src/utils
 INCLUDES += external/openssl/include
 INCLUDES += system/security/keystore/include
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+INCLUDES += external/libnl/include
+else
 INCLUDES += external/libnl-headers
 endif
+endif
 
 
 ifndef CONFIG_OS
@@ -108,7 +106,6 @@
 NEED_AES=y
 NEED_MD5=y
 NEED_SHA1=y
-NEED_SHA256=y
 
 OBJS += src/drivers/drivers.c
 L_CFLAGS += -DHOSTAPD
@@ -141,10 +138,10 @@
 
 
 ifndef CONFIG_NO_DUMP_STATE
-# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to
-# a file (undefine it, if you want to save in binary size)
+# define HOSTAPD_DUMP_STATE to include support for dumping internal state
+# through control interface commands (undefine it, if you want to save in
+# binary size)
 L_CFLAGS += -DHOSTAPD_DUMP_STATE
-OBJS += dump_state.c
 OBJS += src/eapol_auth/eapol_auth_dump.c
 endif
 
@@ -201,6 +198,10 @@
 OBJS += src/ap/peerkey_auth.c
 endif
 
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+endif
+
 ifdef CONFIG_IEEE80211W
 L_CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -355,7 +356,7 @@
 L_CFLAGS += -DEAP_SERVER_GPSK
 OBJS += src/eap_server/eap_server_gpsk.c src/eap_common/eap_gpsk_common.c
 ifdef CONFIG_EAP_GPSK_SHA256
-L_CFLAGS += -DEAP_SERVER_GPSK_SHA256
+L_CFLAGS += -DEAP_GPSK_SHA256
 endif
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -389,10 +390,6 @@
 endif
 
 ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-L_CFLAGS += -DCONFIG_WPS2
-endif
-
 L_CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
 OBJS += src/utils/uuid.c
 OBJS += src/ap/wps_hostapd.c
@@ -531,15 +528,12 @@
 ifdef TLS_FUNCS
 OBJS += src/crypto/tls_gnutls.c
 LIBS += -lgnutls -lgpg-error
-ifdef CONFIG_GNUTLS_EXTRA
-L_CFLAGS += -DCONFIG_GNUTLS_EXTRA
-LIBS += -lgnutls-extra
-endif
 endif
 OBJS += src/crypto/crypto_gnutls.c
 HOBJS += src/crypto/crypto_gnutls.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_gnutls.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 LIBS += -lgcrypt
 LIBS_h += -lgcrypt
@@ -566,7 +560,8 @@
 endif
 OBJS += src/crypto/crypto_nss.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_nss.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 LIBS += -lnss3
 LIBS_h += -lnss3
@@ -831,6 +826,7 @@
 OBJS += src/ap/ap_list.c
 OBJS += src/ap/ieee802_11.c
 OBJS += src/ap/hw_features.c
+OBJS += src/ap/dfs.c
 L_CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_IEEE80211N
@@ -860,10 +856,20 @@
 
 OBJS += src/drivers/driver_common.c
 
+ifdef CONFIG_ACS
+L_CFLAGS += -DCONFIG_ACS
+OBJS += src/ap/acs.c
+LIBS += -lm
+endif
+
 ifdef CONFIG_NO_STDOUT_DEBUG
 L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
 endif
 
+ifdef CONFIG_DEBUG_LINUX_TRACING
+L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
 ifdef CONFIG_DEBUG_FILE
 L_CFLAGS += -DCONFIG_DEBUG_FILE
 endif
@@ -884,12 +890,6 @@
 OBJS_c += src/utils/edit_simple.c
 endif
 
-ifdef CONFIG_ACS
-L_CFLAGS += -DCONFIG_ACS
-OBJS += src/ap/acs.c
-LIBS += -lm
-endif
-
 ########################
 
 include $(CLEAR_VARS)
@@ -913,8 +913,12 @@
 endif
 LOCAL_SHARED_LIBRARIES := libc libcutils liblog libcrypto libssl
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+LOCAL_SHARED_LIBRARIES += libnl
+else
 LOCAL_STATIC_LIBRARIES += libnl_2
 endif
+endif
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS)
 LOCAL_C_INCLUDES := $(INCLUDES)
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 1a4e566..9de9438 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,8 +1,127 @@
 ChangeLog for hostapd
 
-????-??-?? - v2.1
-	* added support for simulataneous authentication of equals (SAE) for
+2014-06-04 - v2.2
+	* fixed SAE confirm-before-commit validation to avoid a potential
+	  segmentation fault in an unexpected message sequence that could be
+	  triggered remotely
+	* extended VHT support
+	  - Operating Mode Notification
+	  - Power Constraint element (local_pwr_constraint)
+	  - Spectrum management capability (spectrum_mgmt_required=1)
+	  - fix VHT80 segment picking in ACS
+	  - fix vht_capab 'Maximum A-MPDU Length Exponent' handling
+	  - fix VHT20
+	* fixed HT40 co-ex scan for some pri/sec channel switches
+	* extended HT40 co-ex support to allow dynamic channel width changes
+	  during the lifetime of the BSS
+	* fixed HT40 co-ex support to check for overlapping 20 MHz BSS
+	* fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding;
+	  this fixes password with include UTF-8 characters that use
+	  three-byte encoding EAP methods that use NtPasswordHash
+	* reverted TLS certificate validation step change in v2.1 that rejected
+	  any AAA server certificate with id-kp-clientAuth even if
+	  id-kp-serverAuth EKU was included
+	* fixed STA validation step for WPS ER commands to prevent a potential
+	  crash if an ER sends an unexpected PutWLANResponse to a station that
+	  is disassociated, but not fully removed
+	* enforce full EAP authentication after RADIUS Disconnect-Request by
+	  removing the PMKSA cache entry
+	* added support for NAS-IP-Address, NAS-identifier, and NAS-IPv6-Address
+	  in RADIUS Disconnect-Request
+	* added mechanism for removing addresses for MAC ACLs by prefixing an
+	  entry with "-"
+	* Interworking/Hotspot 2.0 enhancements
+	  - support Hotspot 2.0 Release 2
+	    * OSEN network for online signup connection
+	    * subscription remediation (based on RADIUS server request or
+	      control interface HS20_WNM_NOTIF for testing purposes)
+	    * Hotspot 2.0 release number indication in WFA RADIUS VSA
+	    * deauthentication request (based on RADIUS server request or
+	      control interface WNM_DEAUTH_REQ for testing purposes)
+	    * Session Info URL RADIUS AVP to trigger ESS Disassociation Imminent
+	    * hs20_icon config parameter to configure icon files for OSU
+	    * osu_* config parameters for OSU Providers list
+	  - do not use Interworking filtering rules on Probe Request if
+	    Interworking is disabled to avoid interop issues
+	* added/fixed nl80211 functionality
+	  - AP interface teardown optimization
+	  - support vendor specific driver command
+	    (VENDOR <vendor id> <sub command id> [<hex formatted data>])
+	* fixed PMF protection of Deauthentication frame when this is triggered
+	  by session timeout
+	* internal TLS implementation enhancements/fixes
+	  - add SHA256-based cipher suites
+	  - add DHE-RSA cipher suites
+	  - fix X.509 validation of PKCS#1 signature to check for extra data
+	* RADIUS server functionality
+	  - add minimal RADIUS accounting server support (hostapd-as-server);
+	    this is mainly to enable testing coverage with hwsim scripts
+	  - allow authentication log to be written into SQLite databse
+	  - added option for TLS protocol testing of an EAP peer by simulating
+	    various misbehaviors/known attacks
+	  - MAC ACL support for testing purposes
+	* fixed PTK derivation for CCMP-256 and GCMP-256
+	* extended WPS per-station PSK to support ER case
+	* added option to configure the management group cipher
+	  (group_mgmt_cipher=AES-128-CMAC (default), BIP-GMAC-128, BIP-GMAC-256,
+	  BIP-CMAC-256)
+	* fixed AP mode default TXOP Limit values for AC_VI and AC_VO (these
+	  were rounded incorrectly)
+	* added support for postponing FT response in case PMK-R1 needs to be
+	  pulled from R0KH
+	* added option to advertise 40 MHz intolerant HT capability with
+	  ht_capab=[40-INTOLERANT]
+	* remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled
+	  whenever CONFIG_WPS=y is set
+	* EAP-pwd fixes
+	  - fix possible segmentation fault on EAP method deinit if an invalid
+	    group is negotiated
+	* fixed RADIUS client retransmit/failover behavior
+	  - there was a potential ctash due to freed memory being accessed
+	  - failover to a backup server mechanism did not work properly
+	* fixed a possible crash on double DISABLE command when multiple BSSes
+	  are enabled
+	* fixed a memory leak in SAE random number generation
+	* fixed GTK rekeying when the station uses FT protocol
+	* fixed off-by-one bounds checking in printf_encode()
+	  - this could result in deinial of service in some EAP server cases
+	* various bug fixes
+
+2014-02-04 - v2.1
+	* added support for simultaneous authentication of equals (SAE) for
 	  stronger password-based authentication with WPA2-Personal
+	* added nl80211 functionality
+	  - VHT configuration for nl80211
+	  - support split wiphy dump
+	  - driver-based MAC ACL
+	  - QoS Mapping configuration
+	* added fully automated regression testing with mac80211_hwsim
+	* allow ctrl_iface group to be specified on command line (-G<group>)
+	* allow single hostapd process to control independent WPS interfaces
+	  (wps_independent=1) instead of synchronized operations through all
+	  configured interfaces within a process
+	* avoid processing received management frames multiple times when using
+	  nl80211 with multiple BSSes
+	* added support for DFS (processing radar detection events, CAC, channel
+	  re-selection)
+	* added EAP-EKE server
+	* added automatic channel selection (ACS)
+	* added option for using per-BSS (vif) configuration files with
+	  -b<phyname>:<config file name>
+	* extended global control interface ADD/REMOVE commands to allow BSSes
+	  of a radio to be removed individually without having to add/remove all
+	  other BSSes of the radio at the same time
+	* added support for sending debug info to Linux tracing (-T on command
+	  line)
+	* replace dump_file functionality with same information being available
+	  through the hostapd control interface
+	* added support for using Protected Dual of Public Action frames for
+	  GAS/ANQP exchanges when PMF is enabled
+	* added support for WPS+NFC updates
+	  - improved protocol
+	  - option to fetch and report alternative carrier records for external
+	    NFC operations
+	* various bug fixes
 
 2013-01-12 - v2.0
 	* added AP-STA-DISCONNECTED ctrl_iface event
diff --git a/hostapd/Makefile b/hostapd/Makefile
index fda4e4e..ac6373e 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -6,8 +6,8 @@
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
+CFLAGS += -I$(abspath ../src)
+CFLAGS += -I$(abspath ../src/utils)
 
 # Uncomment following line and set the path to your kernel tree include
 # directory if your C library does not include all header files.
@@ -15,6 +15,11 @@
 
 -include .config
 
+ifdef CONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CONFIG_WPS_TESTING=y
+endif
+
 ifndef CONFIG_OS
 ifdef CONFIG_NATIVE_WINDOWS
 CONFIG_OS=win32
@@ -65,6 +70,11 @@
 OBJS += ../src/drivers/drivers.o
 CFLAGS += -DHOSTAPD
 
+ifdef CONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_MODULE_TESTS
+OBJS += hapd_module_tests.o
+endif
+
 ifdef CONFIG_WPA_TRACE
 CFLAGS += -DWPA_TRACE
 OBJS += ../src/utils/trace.o
@@ -72,10 +82,10 @@
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_c += -lbfd
-LIBS_h += -lbfd
+CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD
+LIBS += -lbfd -ldl -liberty -lz
+LIBS_c += -lbfd -ldl -liberty -lz
+LIBS_h += -lbfd -ldl -liberty -lz
 endif
 endif
 
@@ -84,6 +94,15 @@
 endif
 OBJS += ../src/utils/$(CONFIG_ELOOP).o
 OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
+
+ifeq ($(CONFIG_ELOOP), eloop)
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+LIBS_c += -lrt
+LIBS_h += -lrt
+LIBS_n += -lrt
+endif
+
 OBJS += ../src/utils/common.o
 OBJS += ../src/utils/wpa_debug.o
 OBJS_c += ../src/utils/wpa_debug.o
@@ -97,11 +116,19 @@
 OBJS += ../src/eapol_auth/eapol_auth_sm.o
 
 
+ifdef CONFIG_CODE_COVERAGE
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+LIBS += -lgcov
+LIBS_c += -lgcov
+LIBS_h += -lgcov
+LIBS_n += -lgcov
+endif
+
 ifndef CONFIG_NO_DUMP_STATE
-# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to
-# a file (undefine it, if you want to save in binary size)
+# define HOSTAPD_DUMP_STATE to include support for dumping internal state
+# through control interface commands (undefine it, if you want to save in
+# binary size)
 CFLAGS += -DHOSTAPD_DUMP_STATE
-OBJS += dump_state.o
 OBJS += ../src/eapol_auth/eapol_auth_dump.o
 endif
 
@@ -158,6 +185,10 @@
 OBJS += ../src/ap/peerkey_auth.o
 endif
 
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+endif
+
 ifdef CONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -311,7 +342,7 @@
 CFLAGS += -DEAP_SERVER_GPSK
 OBJS += ../src/eap_server/eap_server_gpsk.o ../src/eap_common/eap_gpsk_common.o
 ifdef CONFIG_EAP_GPSK_SHA256
-CFLAGS += -DEAP_SERVER_GPSK_SHA256
+CFLAGS += -DEAP_GPSK_SHA256
 endif
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -345,10 +376,6 @@
 endif
 
 ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-CFLAGS += -DCONFIG_WPS2
-endif
-
 CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
 OBJS += ../src/utils/uuid.o
 OBJS += ../src/ap/wps_hostapd.o
@@ -491,7 +518,8 @@
 OBJS += ../src/crypto/crypto_gnutls.o
 HOBJS += ../src/crypto/crypto_gnutls.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 LIBS += -lgcrypt
 LIBS_h += -lgcrypt
@@ -518,7 +546,8 @@
 endif
 OBJS += ../src/crypto/crypto_nss.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 LIBS += -lnss3
 LIBS_h += -lnss3
@@ -782,6 +811,7 @@
 OBJS += ../src/ap/ap_list.o
 OBJS += ../src/ap/ieee802_11.o
 OBJS += ../src/ap/hw_features.o
+OBJS += ../src/ap/dfs.o
 CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_IEEE80211N
@@ -827,6 +857,10 @@
 CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
 endif
 
+ifdef CONFIG_DEBUG_LINUX_TRACING
+CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
 ifdef CONFIG_DEBUG_FILE
 CFLAGS += -DCONFIG_DEBUG_FILE
 endif
@@ -837,10 +871,6 @@
 LIBS_h += -lsqlite3
 endif
 
-ifdef CONFIG_TESTING_OPTIONS
-CFLAGS += -DCONFIG_TESTING_OPTIONS
-endif
-
 ALL=hostapd hostapd_cli
 
 all: verify_config $(ALL)
@@ -852,9 +882,15 @@
 E=true
 endif
 
+ifdef CONFIG_CODE_COVERAGE
+%.o: %.c
+	@$(E) "  CC " $<
+	$(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
+else
 %.o: %.c
 	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
 	@$(E) "  CC " $<
+endif
 
 verify_config:
 	@if [ ! -r .config ]; then \
@@ -923,9 +959,15 @@
 	$(Q)$(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h)
 	@$(E) "  LD " $@
 
+lcov-html:
+	lcov -c -d .. > lcov.info
+	genhtml lcov.info --output-directory lcov-html
+
 clean:
 	$(MAKE) -C ../src clean
 	rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw
-	rm -f *.d
+	rm -f *.d *.gcno *.gcda *.gcov
+	rm -f lcov.info
+	rm -rf lcov-html
 
 -include $(OBJS:%.o=%.d)
diff --git a/hostapd/README b/hostapd/README
index 39b70ca..50868ee 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -2,7 +2,7 @@
 	  Authenticator and RADIUS authentication server
 ================================================================
 
-Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
diff --git a/hostapd/README-WPS b/hostapd/README-WPS
index 654b5bc..bb7d35f 100644
--- a/hostapd/README-WPS
+++ b/hostapd/README-WPS
@@ -63,7 +63,6 @@
 CONFIG_DRIVER_MADWIFI=y
 CFLAGS += -I/usr/src/madwifi-0.9.3
 CONFIG_WPS=y
-CONFIG_WPS2=y
 CONFIG_WPS_UPNP=y
 
 Following parameter can be used to enable support for NFC config method:
diff --git a/hostapd/android.config b/hostapd/android.config
index f51a5bf..ad83308 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -52,7 +52,7 @@
 # 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
+CONFIG_IEEE80211W=y
 
 # Integrated EAP server
 #CONFIG_EAP=y
@@ -108,8 +108,6 @@
 
 # Wi-Fi Protected Setup (WPS)
 CONFIG_WPS=y
-# Enable WSC 2.0 support
-CONFIG_WPS2=y
 # Enable UPnP support for external WPS Registrars
 #CONFIG_WPS_UPNP=y
 
@@ -157,7 +155,7 @@
 # Remove support for VLANs
 #CONFIG_NO_VLAN=y
 
-# Remove support for dumping state into a file on SIGUSR1 signal
+# Remove support for dumping internal state through control interface commands
 # This can be used to reduce binary size at the cost of disabling a debugging
 # option.
 #CONFIG_NO_DUMP_STATE=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 0b4fd77..4c0e3f8 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration file parser
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,7 +22,12 @@
 #include "config_file.h"
 
 
-extern struct wpa_driver_ops *wpa_drivers[];
+#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
@@ -132,6 +137,8 @@
 	}
 
 	while (fgets(buf, sizeof(buf), f)) {
+		int i, rem = 0;
+
 		line++;
 
 		if (buf[0] == '#')
@@ -146,14 +153,32 @@
 		}
 		if (buf[0] == '\0')
 			continue;
+		pos = buf;
+		if (buf[0] == '-') {
+			rem = 1;
+			pos++;
+		}
 
-		if (hwaddr_aton(buf, addr)) {
+		if (hwaddr_aton(pos, addr)) {
 			wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at "
-				   "line %d in '%s'", buf, line, fname);
+				   "line %d in '%s'", pos, line, fname);
 			fclose(f);
 			return -1;
 		}
 
+		if (rem) {
+			i = 0;
+			while (i < *num) {
+				if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) ==
+				    0) {
+					os_remove_in_array(*acl, *num,
+							   sizeof(**acl), i);
+					(*num)--;
+				} else
+					i++;
+			}
+			continue;
+		}
 		vlan_id = 0;
 		pos = buf;
 		while (*pos != '\0' && *pos != ' ' && *pos != '\t')
@@ -191,7 +216,7 @@
 	FILE *f;
 	char buf[512], *pos, *start, *pos2;
 	int line = 0, ret = 0, num_methods;
-	struct hostapd_eap_user *user, *tail = NULL;
+	struct hostapd_eap_user *user = NULL, *tail = NULL;
 
 	if (!fname)
 		return 0;
@@ -225,6 +250,28 @@
 		if (buf[0] == '\0')
 			continue;
 
+#ifndef CONFIG_NO_RADIUS
+		if (user && os_strncmp(buf, "radius_accept_attr=", 19) == 0) {
+			struct hostapd_radius_attr *attr, *a;
+			attr = hostapd_parse_radius_attr(buf + 19);
+			if (attr == NULL) {
+				wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s",
+					   buf + 19);
+				user = NULL; /* already in the BSS list */
+				goto failed;
+			}
+			if (user->accept_attr == NULL) {
+				user->accept_attr = attr;
+			} else {
+				a = user->accept_attr;
+				while (a->next)
+					a = a->next;
+				a->next = attr;
+			}
+			continue;
+		}
+#endif /* CONFIG_NO_RADIUS */
+
 		user = NULL;
 
 		if (buf[0] != '"' && buf[0] != '*') {
@@ -319,6 +366,10 @@
 						EAP_TTLS_AUTH_MSCHAPV2;
 					goto skip_eap;
 				}
+				if (os_strcmp(start, "MACACL") == 0) {
+					user->macacl = 1;
+					goto skip_eap;
+				}
 				wpa_printf(MSG_ERROR, "Unsupported EAP type "
 					   "'%s' on line %d in '%s'",
 					   start, line, fname);
@@ -333,7 +384,7 @@
 				break;
 			start = pos3;
 		}
-		if (num_methods == 0 && user->ttls_auth == 0) {
+		if (num_methods == 0 && user->ttls_auth == 0 && !user->macacl) {
 			wpa_printf(MSG_ERROR, "No EAP types configured on "
 				   "line %d in '%s'", line, fname);
 			goto failed;
@@ -451,11 +502,8 @@
 		continue;
 
 	failed:
-		if (user) {
-			os_free(user->password);
-			os_free(user->identity);
-			os_free(user);
-		}
+		if (user)
+			hostapd_config_free_eap_user(user);
 		ret = -1;
 		break;
 	}
@@ -748,30 +796,32 @@
 
 static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
 {
-	struct hostapd_bss_config *bss;
+	struct hostapd_bss_config **all, *bss;
 
 	if (*ifname == '\0')
 		return -1;
 
-	bss = os_realloc_array(conf->bss, conf->num_bss + 1,
-			       sizeof(struct hostapd_bss_config));
-	if (bss == NULL) {
+	all = os_realloc_array(conf->bss, conf->num_bss + 1,
+			       sizeof(struct hostapd_bss_config *));
+	if (all == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to allocate memory for "
 			   "multi-BSS entry");
 		return -1;
 	}
-	conf->bss = bss;
+	conf->bss = all;
 
-	bss = &(conf->bss[conf->num_bss]);
-	os_memset(bss, 0, sizeof(*bss));
+	bss = os_zalloc(sizeof(*bss));
+	if (bss == NULL)
+		return -1;
 	bss->radius = os_zalloc(sizeof(*bss->radius));
 	if (bss->radius == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to allocate memory for "
 			   "multi-BSS RADIUS data");
+		os_free(bss);
 		return -1;
 	}
 
-	conf->num_bss++;
+	conf->bss[conf->num_bss++] = bss;
 	conf->last_bss = bss;
 
 	hostapd_config_defaults_bss(bss);
@@ -1019,8 +1069,8 @@
 		conf->ht_capab |= HT_CAP_INFO_MAX_AMSDU_SIZE;
 	if (os_strstr(capab, "[DSSS_CCK-40]"))
 		conf->ht_capab |= HT_CAP_INFO_DSSS_CCK40MHZ;
-	if (os_strstr(capab, "[PSMP]"))
-		conf->ht_capab |= HT_CAP_INFO_PSMP_SUPP;
+	if (os_strstr(capab, "[40-INTOLERANT]"))
+		conf->ht_capab |= HT_CAP_INFO_40MHZ_INTOLERANT;
 	if (os_strstr(capab, "[LSIG-TXOP-PROT]"))
 		conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT;
 
@@ -1041,8 +1091,6 @@
 		conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 	if (os_strstr(capab, "[VHT160-80PLUS80]"))
 		conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
-	if (os_strstr(capab, "[VHT160-80PLUS80]"))
-		conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 	if (os_strstr(capab, "[RXLDPC]"))
 		conf->vht_capab |= VHT_CAP_RXLDPC;
 	if (os_strstr(capab, "[SHORT-GI-80]"))
@@ -1060,15 +1108,15 @@
 	if (os_strstr(capab, "[RX-STBC-1234]"))
 		conf->vht_capab |= VHT_CAP_RXSTBC_4;
 	if (os_strstr(capab, "[SU-BEAMFORMER]"))
-		conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
+		conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE;
 	if (os_strstr(capab, "[SU-BEAMFORMEE]"))
-		conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+		conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE;
 	if (os_strstr(capab, "[BF-ANTENNA-2]") &&
-	    (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
-		conf->vht_capab |= VHT_CAP_BEAMFORMER_ANTENNAS_MAX;
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
 	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
-	    (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
-		conf->vht_capab |= VHT_CAP_SOUNDING_DIMENTION_MAX;
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
 	if (os_strstr(capab, "[MU-BEAMFORMER]"))
 		conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
 	if (os_strstr(capab, "[MU-BEAMFORMEE]"))
@@ -1077,8 +1125,20 @@
 		conf->vht_capab |= VHT_CAP_VHT_TXOP_PS;
 	if (os_strstr(capab, "[HTC-VHT]"))
 		conf->vht_capab |= VHT_CAP_HTC_VHT;
-	if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP0]"))
-		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT;
+	if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP7]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX;
+	else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP6]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6;
+	else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP5]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5;
+	else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP4]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4;
+	else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP3]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3;
+	else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP2]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2;
+	else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP1]"))
+		conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1;
 	if (os_strstr(capab, "[VHT-LINK-ADAPT2]") &&
 	    (conf->vht_capab & VHT_CAP_HTC_VHT))
 		conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB;
@@ -1094,165 +1154,6 @@
 #endif /* CONFIG_IEEE80211AC */
 
 
-static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
-				    struct hostapd_config *conf)
-{
-	if (bss->ieee802_1x && !bss->eap_server &&
-	    !bss->radius->auth_servers) {
-		wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
-			   "EAP authenticator configured).");
-		return -1;
-	}
-
-	if (bss->wpa) {
-		int wep, i;
-
-		wep = bss->default_wep_key_len > 0 ||
-		       bss->individual_wep_key_len > 0;
-		for (i = 0; i < NUM_WEP_KEYS; i++) {
-			if (bss->ssid.wep.keys_set) {
-				wep = 1;
-				break;
-			}
-		}
-
-		if (wep) {
-			wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
-			return -1;
-		}
-	}
-
-	if (bss->wpa && bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
-	    bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
-		wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
-			   "RADIUS checking (macaddr_acl=2) enabled.");
-		return -1;
-	}
-
-	if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
-	    bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
-	    bss->ssid.wpa_psk_file == NULL &&
-	    (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
-	     bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
-		wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
-			   "is not configured.");
-		return -1;
-	}
-
-	if (hostapd_mac_comp_empty(bss->bssid) != 0) {
-		size_t i;
-
-		for (i = 0; i < conf->num_bss; i++) {
-			if ((&conf->bss[i] != bss) &&
-			    (hostapd_mac_comp(conf->bss[i].bssid,
-					      bss->bssid) == 0)) {
-				wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
-					   " on interface '%s' and '%s'.",
-					   MAC2STR(bss->bssid),
-					   conf->bss[i].iface, bss->iface);
-				return -1;
-			}
-		}
-	}
-
-#ifdef CONFIG_IEEE80211R
-	if (wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
-	    (bss->nas_identifier == NULL ||
-	     os_strlen(bss->nas_identifier) < 1 ||
-	     os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
-		wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
-			   "nas_identifier to be configured as a 1..48 octet "
-			   "string");
-		return -1;
-	}
-#endif /* CONFIG_IEEE80211R */
-
-#ifdef CONFIG_IEEE80211N
-	if (conf->ieee80211n && conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
-		bss->disable_11n = 1;
-		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
-			   "allowed, disabling HT capabilites");
-	}
-
-	if (conf->ieee80211n &&
-	    bss->ssid.security_policy == SECURITY_STATIC_WEP) {
-		bss->disable_11n = 1;
-		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
-			   "allowed, disabling HT capabilities");
-	}
-
-	if (conf->ieee80211n && bss->wpa &&
-	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
-	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))) {
-		bss->disable_11n = 1;
-		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
-			   "requires CCMP/GCMP to be enabled, disabling HT "
-			   "capabilities");
-	}
-#endif /* CONFIG_IEEE80211N */
-
-#ifdef CONFIG_WPS2
-	if (bss->wps_state && bss->ignore_broadcast_ssid) {
-		wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
-			   "configuration forced WPS to be disabled");
-		bss->wps_state = 0;
-	}
-
-	if (bss->wps_state && bss->ssid.wep.keys_set && bss->wpa == 0) {
-		wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
-			   "disabled");
-		bss->wps_state = 0;
-	}
-
-	if (bss->wps_state && bss->wpa &&
-	    (!(bss->wpa & 2) ||
-	     !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
-		wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
-			   "WPA2/CCMP forced WPS to be disabled");
-		bss->wps_state = 0;
-	}
-#endif /* CONFIG_WPS2 */
-
-#ifdef CONFIG_HS20
-	if (bss->hs20 &&
-	    (!(bss->wpa & 2) ||
-	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
-		wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
-			   "configuration is required for Hotspot 2.0 "
-			   "functionality");
-		return -1;
-	}
-#endif /* CONFIG_HS20 */
-
-	return 0;
-}
-
-
-static int hostapd_config_check(struct hostapd_config *conf)
-{
-	size_t i;
-
-	if (conf->ieee80211d && (!conf->country[0] || !conf->country[1])) {
-		wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
-			   "setting the country_code");
-		return -1;
-	}
-
-	if (conf->ieee80211h && !conf->ieee80211d) {
-		wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
-			   "IEEE 802.11d enabled");
-		return -1;
-	}
-
-	for (i = 0; i < conf->num_bss; i++) {
-		if (hostapd_config_check_bss(&conf->bss[i], conf))
-			return -1;
-	}
-
-	return 0;
-}
-
-
 #ifdef CONFIG_INTERWORKING
 static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
 				    int line)
@@ -1289,26 +1190,34 @@
 static int parse_lang_string(struct hostapd_lang_string **array,
 			     unsigned int *count, char *pos)
 {
-	char *sep;
-	size_t clen, nlen;
+	char *sep, *str = NULL;
+	size_t clen, nlen, slen;
 	struct hostapd_lang_string *ls;
+	int ret = -1;
+
+	if (*pos == '"' || (*pos == 'P' && pos[1] == '"')) {
+		str = wpa_config_parse_string(pos, &slen);
+		if (!str)
+			return -1;
+		pos = str;
+	}
 
 	sep = os_strchr(pos, ':');
 	if (sep == NULL)
-		return -1;
+		goto fail;
 	*sep++ = '\0';
 
 	clen = os_strlen(pos);
-	if (clen < 2)
-		return -1;
+	if (clen < 2 || clen > sizeof(ls->lang))
+		goto fail;
 	nlen = os_strlen(sep);
 	if (nlen > 252)
-		return -1;
+		goto fail;
 
 	ls = os_realloc_array(*array, *count + 1,
 			      sizeof(struct hostapd_lang_string));
 	if (ls == NULL)
-		return -1;
+		goto fail;
 
 	*array = ls;
 	ls = &(*array)[*count];
@@ -1319,7 +1228,10 @@
 	ls->name_len = nlen;
 	os_memcpy(ls->name, sep, nlen);
 
-	return 0;
+	ret = 0;
+fail:
+	os_free(str);
+	return ret;
 }
 
 
@@ -1346,7 +1258,7 @@
 
 	count = 1;
 	for (pos = buf; *pos; pos++) {
-		if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',')
+		if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',')
 			goto fail;
 		if (*pos == ';')
 			count++;
@@ -1550,6 +1462,47 @@
 	return -1;
 }
 
+
+static int parse_qos_map_set(struct hostapd_bss_config *bss,
+			     char *buf, int line)
+{
+	u8 qos_map_set[16 + 2 * 21], count = 0;
+	char *pos = buf;
+	int val;
+
+	for (;;) {
+		if (count == sizeof(qos_map_set)) {
+			wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set "
+				   "parameters '%s'", line, buf);
+			return -1;
+		}
+
+		val = atoi(pos);
+		if (val > 255 || val < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set "
+				   "'%s'", line, buf);
+			return -1;
+		}
+
+		qos_map_set[count++] = val;
+		pos = os_strchr(pos, ',');
+		if (!pos)
+			break;
+		pos++;
+	}
+
+	if (count < 16 || count & 1) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'",
+			   line, buf);
+		return -1;
+	}
+
+	os_memcpy(bss->qos_map_set, qos_map_set, count);
+	bss->qos_map_set_len = count;
+
+	return 0;
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
@@ -1647,7 +1600,7 @@
 
 fail:
 	wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
-		   line, pos);
+		   line, buf);
 	os_free(wan_metrics);
 	return -1;
 }
@@ -1664,6 +1617,197 @@
 	}
 	return 0;
 }
+
+
+static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
+{
+	struct hs20_icon *icon;
+	char *end;
+
+	icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
+				sizeof(struct hs20_icon));
+	if (icon == NULL)
+		return -1;
+	bss->hs20_icons = icon;
+	icon = &bss->hs20_icons[bss->hs20_icons_count];
+	os_memset(icon, 0, sizeof(*icon));
+
+	icon->width = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+
+	icon->height = atoi(pos);
+	pos = os_strchr(pos, ':');
+	if (pos == NULL)
+		return -1;
+	pos++;
+
+	end = os_strchr(pos, ':');
+	if (end == NULL || end - pos > 3)
+		return -1;
+	os_memcpy(icon->language, pos, end - pos);
+	pos = end + 1;
+
+	end = os_strchr(pos, ':');
+	if (end == NULL || end - pos > 255)
+		return -1;
+	os_memcpy(icon->type, pos, end - pos);
+	pos = end + 1;
+
+	end = os_strchr(pos, ':');
+	if (end == NULL || end - pos > 255)
+		return -1;
+	os_memcpy(icon->name, pos, end - pos);
+	pos = end + 1;
+
+	if (os_strlen(pos) > 255)
+		return -1;
+	os_memcpy(icon->file, pos, os_strlen(pos));
+
+	bss->hs20_icons_count++;
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
+			       char *pos, int line)
+{
+	size_t slen;
+	char *str;
+
+	str = wpa_config_parse_string(pos, &slen);
+	if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
+		os_free(str);
+		return -1;
+	}
+
+	os_memcpy(bss->osu_ssid, str, slen);
+	bss->osu_ssid_len = slen;
+	os_free(str);
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
+				     char *pos, int line)
+{
+	struct hs20_osu_provider *p;
+
+	p = os_realloc_array(bss->hs20_osu_providers,
+			     bss->hs20_osu_providers_count + 1, sizeof(*p));
+	if (p == NULL)
+		return -1;
+
+	bss->hs20_osu_providers = p;
+	bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
+	bss->hs20_osu_providers_count++;
+	os_memset(bss->last_osu, 0, sizeof(*p));
+	bss->last_osu->server_uri = os_strdup(pos);
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
+					char *pos, int line)
+{
+	if (bss->last_osu == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+		return -1;
+	}
+
+	if (parse_lang_string(&bss->last_osu->friendly_name,
+			      &bss->last_osu->friendly_name_count, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
+			   line, pos);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
+			      char *pos, int line)
+{
+	if (bss->last_osu == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+		return -1;
+	}
+
+	os_free(bss->last_osu->osu_nai);
+	bss->last_osu->osu_nai = os_strdup(pos);
+	if (bss->last_osu->osu_nai == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
+				      int line)
+{
+	if (bss->last_osu == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+		return -1;
+	}
+
+	if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
+			       int line)
+{
+	char **n;
+	struct hs20_osu_provider *p = bss->last_osu;
+
+	if (p == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+		return -1;
+	}
+
+	n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
+	if (n == NULL)
+		return -1;
+	p->icons = n;
+	p->icons[p->icons_count] = os_strdup(pos);
+	if (p->icons[p->icons_count] == NULL)
+		return -1;
+	p->icons_count++;
+
+	return 0;
+}
+
+
+static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
+				       char *pos, int line)
+{
+	if (bss->last_osu == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+		return -1;
+	}
+
+	if (parse_lang_string(&bss->last_osu->service_desc,
+			      &bss->last_osu->service_desc_count, pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
+			   line, pos);
+		return -1;
+	}
+
+	return 0;
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -1696,1349 +1840,1333 @@
 			       struct hostapd_bss_config *bss,
 			       char *buf, char *pos, int line)
 {
-	int errors = 0;
-
-	{
-		if (os_strcmp(buf, "interface") == 0) {
-			os_strlcpy(conf->bss[0].iface, pos,
-				   sizeof(conf->bss[0].iface));
-		} else if (os_strcmp(buf, "bridge") == 0) {
-			os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
-		} else if (os_strcmp(buf, "vlan_bridge") == 0) {
-			os_strlcpy(bss->vlan_bridge, pos,
-			           sizeof(bss->vlan_bridge));
-		} else if (os_strcmp(buf, "wds_bridge") == 0) {
-			os_strlcpy(bss->wds_bridge, pos,
-				   sizeof(bss->wds_bridge));
-		} else if (os_strcmp(buf, "driver") == 0) {
-			int j;
-			/* clear to get error below if setting is invalid */
-			conf->driver = NULL;
-			for (j = 0; wpa_drivers[j]; j++) {
-				if (os_strcmp(pos, wpa_drivers[j]->name) == 0)
-				{
-					conf->driver = wpa_drivers[j];
-					break;
-				}
+	if (os_strcmp(buf, "interface") == 0) {
+		os_strlcpy(conf->bss[0]->iface, pos,
+			   sizeof(conf->bss[0]->iface));
+	} else if (os_strcmp(buf, "bridge") == 0) {
+		os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
+	} else if (os_strcmp(buf, "vlan_bridge") == 0) {
+		os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
+	} else if (os_strcmp(buf, "wds_bridge") == 0) {
+		os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+	} else if (os_strcmp(buf, "driver") == 0) {
+		int j;
+		/* clear to get error below if setting is invalid */
+		conf->driver = NULL;
+		for (j = 0; wpa_drivers[j]; j++) {
+			if (os_strcmp(pos, wpa_drivers[j]->name) == 0) {
+				conf->driver = wpa_drivers[j];
+				break;
 			}
-			if (conf->driver == NULL) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid/"
-					   "unknown driver '%s'", line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "debug") == 0) {
-			wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' "
-				   "configuration variable is not used "
-				   "anymore", line);
-		} else if (os_strcmp(buf, "logger_syslog_level") == 0) {
-			bss->logger_syslog_level = atoi(pos);
-		} else if (os_strcmp(buf, "logger_stdout_level") == 0) {
-			bss->logger_stdout_level = atoi(pos);
-		} else if (os_strcmp(buf, "logger_syslog") == 0) {
-			bss->logger_syslog = atoi(pos);
-		} else if (os_strcmp(buf, "logger_stdout") == 0) {
-			bss->logger_stdout = atoi(pos);
-		} else if (os_strcmp(buf, "dump_file") == 0) {
-			bss->dump_log_name = os_strdup(pos);
-		} else if (os_strcmp(buf, "ssid") == 0) {
-			bss->ssid.ssid_len = os_strlen(pos);
-			if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
-			    bss->ssid.ssid_len < 1) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
-					   "'%s'", line, pos);
-				errors++;
-			} else {
-				os_memcpy(bss->ssid.ssid, pos,
-					  bss->ssid.ssid_len);
-				bss->ssid.ssid_set = 1;
-			}
-		} else if (os_strcmp(buf, "ssid2") == 0) {
-			size_t slen;
-			char *str = wpa_config_parse_string(pos, &slen);
-			if (str == NULL || slen < 1 ||
-				   slen > HOSTAPD_MAX_SSID_LEN) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
-					   "'%s'", line, pos);
-				errors++;
-			} else {
-				os_memcpy(bss->ssid.ssid, str, slen);
-				bss->ssid.ssid_len = slen;
-				bss->ssid.ssid_set = 1;
-			}
+		}
+		if (conf->driver == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid/unknown driver '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "debug") == 0) {
+		wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore",
+			   line);
+	} else if (os_strcmp(buf, "logger_syslog_level") == 0) {
+		bss->logger_syslog_level = atoi(pos);
+	} else if (os_strcmp(buf, "logger_stdout_level") == 0) {
+		bss->logger_stdout_level = atoi(pos);
+	} else if (os_strcmp(buf, "logger_syslog") == 0) {
+		bss->logger_syslog = atoi(pos);
+	} else if (os_strcmp(buf, "logger_stdout") == 0) {
+		bss->logger_stdout = atoi(pos);
+	} else if (os_strcmp(buf, "dump_file") == 0) {
+		wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore",
+			   line);
+	} else if (os_strcmp(buf, "ssid") == 0) {
+		bss->ssid.ssid_len = os_strlen(pos);
+		if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
+		    bss->ssid.ssid_len < 1) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+				   line, pos);
+			return 1;
+		}
+		os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len);
+		bss->ssid.ssid_set = 1;
+	} else if (os_strcmp(buf, "ssid2") == 0) {
+		size_t slen;
+		char *str = wpa_config_parse_string(pos, &slen);
+		if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+				   line, pos);
 			os_free(str);
-		} else if (os_strcmp(buf, "utf8_ssid") == 0) {
-			bss->ssid.utf8_ssid = atoi(pos) > 0;
-		} else if (os_strcmp(buf, "macaddr_acl") == 0) {
-			bss->macaddr_acl = atoi(pos);
-			if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
-			    bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
-			    bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
-				wpa_printf(MSG_ERROR, "Line %d: unknown "
-					   "macaddr_acl %d",
-					   line, bss->macaddr_acl);
-			}
-		} else if (os_strcmp(buf, "accept_mac_file") == 0) {
-			if (hostapd_config_read_maclist(pos, &bss->accept_mac,
-							&bss->num_accept_mac))
-			{
-				wpa_printf(MSG_ERROR, "Line %d: Failed to "
-					   "read accept_mac_file '%s'",
-					   line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "deny_mac_file") == 0) {
-			if (hostapd_config_read_maclist(pos, &bss->deny_mac,
-							&bss->num_deny_mac)) {
-				wpa_printf(MSG_ERROR, "Line %d: Failed to "
-					   "read deny_mac_file '%s'",
-					   line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wds_sta") == 0) {
-			bss->wds_sta = atoi(pos);
-		} else if (os_strcmp(buf, "start_disabled") == 0) {
-			bss->start_disabled = atoi(pos);
-		} else if (os_strcmp(buf, "ap_isolate") == 0) {
-			bss->isolate = atoi(pos);
-		} else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
-			bss->ap_max_inactivity = atoi(pos);
-		} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
-			bss->skip_inactivity_poll = atoi(pos);
-		} else if (os_strcmp(buf, "country_code") == 0) {
-			os_memcpy(conf->country, pos, 2);
-			/* FIX: make this configurable */
-			conf->country[2] = ' ';
-		} else if (os_strcmp(buf, "ieee80211d") == 0) {
-			conf->ieee80211d = atoi(pos);
-		} else if (os_strcmp(buf, "ieee80211h") == 0) {
-			conf->ieee80211h = atoi(pos);
-		} else if (os_strcmp(buf, "ieee8021x") == 0) {
-			bss->ieee802_1x = atoi(pos);
-		} else if (os_strcmp(buf, "eapol_version") == 0) {
-			bss->eapol_version = atoi(pos);
-			if (bss->eapol_version < 1 ||
-			    bss->eapol_version > 2) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL "
-					   "version (%d): '%s'.",
-					   line, bss->eapol_version, pos);
-				errors++;
-			} else
-				wpa_printf(MSG_DEBUG, "eapol_version=%d",
-					   bss->eapol_version);
+			return 1;
+		}
+		os_memcpy(bss->ssid.ssid, str, slen);
+		bss->ssid.ssid_len = slen;
+		bss->ssid.ssid_set = 1;
+		os_free(str);
+	} else if (os_strcmp(buf, "utf8_ssid") == 0) {
+		bss->ssid.utf8_ssid = atoi(pos) > 0;
+	} else if (os_strcmp(buf, "macaddr_acl") == 0) {
+		bss->macaddr_acl = atoi(pos);
+		if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
+		    bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
+		    bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+			wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d",
+				   line, bss->macaddr_acl);
+		}
+	} else if (os_strcmp(buf, "accept_mac_file") == 0) {
+		if (hostapd_config_read_maclist(pos, &bss->accept_mac,
+						&bss->num_accept_mac)) {
+			wpa_printf(MSG_ERROR, "Line %d: Failed to read accept_mac_file '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "deny_mac_file") == 0) {
+		if (hostapd_config_read_maclist(pos, &bss->deny_mac,
+						&bss->num_deny_mac)) {
+			wpa_printf(MSG_ERROR, "Line %d: Failed to read deny_mac_file '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wds_sta") == 0) {
+		bss->wds_sta = atoi(pos);
+	} else if (os_strcmp(buf, "start_disabled") == 0) {
+		bss->start_disabled = atoi(pos);
+	} else if (os_strcmp(buf, "ap_isolate") == 0) {
+		bss->isolate = atoi(pos);
+	} else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
+		bss->ap_max_inactivity = atoi(pos);
+	} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
+		bss->skip_inactivity_poll = atoi(pos);
+	} else if (os_strcmp(buf, "country_code") == 0) {
+		os_memcpy(conf->country, pos, 2);
+		/* FIX: make this configurable */
+		conf->country[2] = ' ';
+	} else if (os_strcmp(buf, "ieee80211d") == 0) {
+		conf->ieee80211d = atoi(pos);
+	} else if (os_strcmp(buf, "ieee80211h") == 0) {
+		conf->ieee80211h = atoi(pos);
+	} else if (os_strcmp(buf, "ieee8021x") == 0) {
+		bss->ieee802_1x = atoi(pos);
+	} else if (os_strcmp(buf, "eapol_version") == 0) {
+		bss->eapol_version = atoi(pos);
+		if (bss->eapol_version < 1 || bss->eapol_version > 2) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid EAPOL version (%d): '%s'.",
+				   line, bss->eapol_version, pos);
+			return 1;
+		}
+		wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version);
 #ifdef EAP_SERVER
-		} else if (os_strcmp(buf, "eap_authenticator") == 0) {
-			bss->eap_server = atoi(pos);
-			wpa_printf(MSG_ERROR, "Line %d: obsolete "
-				   "eap_authenticator used; this has been "
-				   "renamed to eap_server", line);
-		} else if (os_strcmp(buf, "eap_server") == 0) {
-			bss->eap_server = atoi(pos);
-		} else if (os_strcmp(buf, "eap_user_file") == 0) {
-			if (hostapd_config_read_eap_user(pos, bss))
-				errors++;
-		} else if (os_strcmp(buf, "ca_cert") == 0) {
-			os_free(bss->ca_cert);
-			bss->ca_cert = os_strdup(pos);
-		} else if (os_strcmp(buf, "server_cert") == 0) {
-			os_free(bss->server_cert);
-			bss->server_cert = 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_key_passwd") == 0) {
-			os_free(bss->private_key_passwd);
-			bss->private_key_passwd = os_strdup(pos);
-		} else if (os_strcmp(buf, "check_crl") == 0) {
-			bss->check_crl = atoi(pos);
-		} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
-			os_free(bss->ocsp_stapling_response);
-			bss->ocsp_stapling_response = os_strdup(pos);
-		} else if (os_strcmp(buf, "dh_file") == 0) {
-			os_free(bss->dh_file);
-			bss->dh_file = os_strdup(pos);
-		} else if (os_strcmp(buf, "fragment_size") == 0) {
-			bss->fragment_size = atoi(pos);
+	} else if (os_strcmp(buf, "eap_authenticator") == 0) {
+		bss->eap_server = atoi(pos);
+		wpa_printf(MSG_ERROR, "Line %d: obsolete eap_authenticator used; this has been renamed to eap_server", line);
+	} else if (os_strcmp(buf, "eap_server") == 0) {
+		bss->eap_server = atoi(pos);
+	} else if (os_strcmp(buf, "eap_user_file") == 0) {
+		if (hostapd_config_read_eap_user(pos, bss))
+			return 1;
+	} else if (os_strcmp(buf, "ca_cert") == 0) {
+		os_free(bss->ca_cert);
+		bss->ca_cert = os_strdup(pos);
+	} else if (os_strcmp(buf, "server_cert") == 0) {
+		os_free(bss->server_cert);
+		bss->server_cert = 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_key_passwd") == 0) {
+		os_free(bss->private_key_passwd);
+		bss->private_key_passwd = os_strdup(pos);
+	} else if (os_strcmp(buf, "check_crl") == 0) {
+		bss->check_crl = atoi(pos);
+	} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
+		os_free(bss->ocsp_stapling_response);
+		bss->ocsp_stapling_response = os_strdup(pos);
+	} else if (os_strcmp(buf, "dh_file") == 0) {
+		os_free(bss->dh_file);
+		bss->dh_file = os_strdup(pos);
+	} else if (os_strcmp(buf, "fragment_size") == 0) {
+		bss->fragment_size = atoi(pos);
 #ifdef EAP_SERVER_FAST
-		} else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
-			os_free(bss->pac_opaque_encr_key);
-			bss->pac_opaque_encr_key = os_malloc(16);
-			if (bss->pac_opaque_encr_key == NULL) {
-				wpa_printf(MSG_ERROR, "Line %d: No memory for "
-					   "pac_opaque_encr_key", line);
-				errors++;
-			} else if (hexstr2bin(pos, bss->pac_opaque_encr_key,
-					      16)) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "pac_opaque_encr_key", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
-			size_t idlen = os_strlen(pos);
-			if (idlen & 1) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "eap_fast_a_id", line);
-				errors++;
-			} else {
-				os_free(bss->eap_fast_a_id);
-				bss->eap_fast_a_id = os_malloc(idlen / 2);
-				if (bss->eap_fast_a_id == NULL ||
-				    hexstr2bin(pos, bss->eap_fast_a_id,
-					       idlen / 2)) {
-					wpa_printf(MSG_ERROR, "Line %d: "
-						   "Failed to parse "
-						   "eap_fast_a_id", line);
-					errors++;
-				} else
-					bss->eap_fast_a_id_len = idlen / 2;
-			}
-		} else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
-			os_free(bss->eap_fast_a_id_info);
-			bss->eap_fast_a_id_info = os_strdup(pos);
-		} else if (os_strcmp(buf, "eap_fast_prov") == 0) {
-			bss->eap_fast_prov = atoi(pos);
-		} else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
-			bss->pac_key_lifetime = atoi(pos);
-		} else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
-			bss->pac_key_refresh_time = atoi(pos);
+	} else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
+		os_free(bss->pac_opaque_encr_key);
+		bss->pac_opaque_encr_key = os_malloc(16);
+		if (bss->pac_opaque_encr_key == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: No memory for pac_opaque_encr_key",
+				   line);
+			return 1;
+		} else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid pac_opaque_encr_key",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
+		size_t idlen = os_strlen(pos);
+		if (idlen & 1) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid eap_fast_a_id",
+				   line);
+			return 1;
+		}
+		os_free(bss->eap_fast_a_id);
+		bss->eap_fast_a_id = os_malloc(idlen / 2);
+		if (bss->eap_fast_a_id == NULL ||
+		    hexstr2bin(pos, bss->eap_fast_a_id, idlen / 2)) {
+			wpa_printf(MSG_ERROR, "Line %d: Failed to parse eap_fast_a_id",
+				   line);
+			os_free(bss->eap_fast_a_id);
+			bss->eap_fast_a_id = NULL;
+			return 1;
+		} else {
+			bss->eap_fast_a_id_len = idlen / 2;
+		}
+	} else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
+		os_free(bss->eap_fast_a_id_info);
+		bss->eap_fast_a_id_info = os_strdup(pos);
+	} else if (os_strcmp(buf, "eap_fast_prov") == 0) {
+		bss->eap_fast_prov = atoi(pos);
+	} else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
+		bss->pac_key_lifetime = atoi(pos);
+	} else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
+		bss->pac_key_refresh_time = atoi(pos);
 #endif /* EAP_SERVER_FAST */
 #ifdef EAP_SERVER_SIM
-		} else if (os_strcmp(buf, "eap_sim_db") == 0) {
-			os_free(bss->eap_sim_db);
-			bss->eap_sim_db = os_strdup(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_db") == 0) {
+		os_free(bss->eap_sim_db);
+		bss->eap_sim_db = os_strdup(pos);
+	} else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
+		bss->eap_sim_aka_result_ind = atoi(pos);
 #endif /* EAP_SERVER_SIM */
 #ifdef EAP_SERVER_TNC
-		} else if (os_strcmp(buf, "tnc") == 0) {
-			bss->tnc = atoi(pos);
+	} else if (os_strcmp(buf, "tnc") == 0) {
+		bss->tnc = atoi(pos);
 #endif /* EAP_SERVER_TNC */
 #ifdef EAP_SERVER_PWD
-		} else if (os_strcmp(buf, "pwd_group") == 0) {
-			bss->pwd_group = atoi(pos);
+	} else if (os_strcmp(buf, "pwd_group") == 0) {
+		bss->pwd_group = atoi(pos);
 #endif /* EAP_SERVER_PWD */
 #endif /* EAP_SERVER */
-		} else if (os_strcmp(buf, "eap_message") == 0) {
-			char *term;
-			bss->eap_req_id_text = os_strdup(pos);
-			if (bss->eap_req_id_text == NULL) {
-				wpa_printf(MSG_ERROR, "Line %d: Failed to "
-					   "allocate memory for "
-					   "eap_req_id_text", line);
-				errors++;
-				return errors;
-			}
-			bss->eap_req_id_text_len =
-				os_strlen(bss->eap_req_id_text);
-			term = os_strstr(bss->eap_req_id_text, "\\0");
-			if (term) {
-				*term++ = '\0';
-				os_memmove(term, term + 1,
-					   bss->eap_req_id_text_len -
-					   (term - bss->eap_req_id_text) - 1);
-				bss->eap_req_id_text_len--;
-			}
-		} else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
-			bss->default_wep_key_len = atoi(pos);
-			if (bss->default_wep_key_len > 13) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
-					   "key len %lu (= %lu bits)", line,
-					   (unsigned long)
-					   bss->default_wep_key_len,
-					   (unsigned long)
-					   bss->default_wep_key_len * 8);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
-			bss->individual_wep_key_len = atoi(pos);
-			if (bss->individual_wep_key_len < 0 ||
-			    bss->individual_wep_key_len > 13) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
-					   "key len %d (= %d bits)", line,
-					   bss->individual_wep_key_len,
-					   bss->individual_wep_key_len * 8);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wep_rekey_period") == 0) {
-			bss->wep_rekeying_period = atoi(pos);
-			if (bss->wep_rekeying_period < 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "period %d",
-					   line, bss->wep_rekeying_period);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "eap_reauth_period") == 0) {
-			bss->eap_reauth_period = atoi(pos);
-			if (bss->eap_reauth_period < 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "period %d",
-					   line, bss->eap_reauth_period);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
-			bss->eapol_key_index_workaround = atoi(pos);
+	} else if (os_strcmp(buf, "eap_message") == 0) {
+		char *term;
+		os_free(bss->eap_req_id_text);
+		bss->eap_req_id_text = os_strdup(pos);
+		if (bss->eap_req_id_text == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: Failed to allocate memory for eap_req_id_text",
+				   line);
+			return 1;
+		}
+		bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text);
+		term = os_strstr(bss->eap_req_id_text, "\\0");
+		if (term) {
+			*term++ = '\0';
+			os_memmove(term, term + 1,
+				   bss->eap_req_id_text_len -
+				   (term - bss->eap_req_id_text) - 1);
+			bss->eap_req_id_text_len--;
+		}
+	} else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
+		bss->default_wep_key_len = atoi(pos);
+		if (bss->default_wep_key_len > 13) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)",
+				   line,
+				   (unsigned long) bss->default_wep_key_len,
+				   (unsigned long)
+				   bss->default_wep_key_len * 8);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
+		bss->individual_wep_key_len = atoi(pos);
+		if (bss->individual_wep_key_len < 0 ||
+		    bss->individual_wep_key_len > 13) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)",
+				   line, bss->individual_wep_key_len,
+				   bss->individual_wep_key_len * 8);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wep_rekey_period") == 0) {
+		bss->wep_rekeying_period = atoi(pos);
+		if (bss->wep_rekeying_period < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+				   line, bss->wep_rekeying_period);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "eap_reauth_period") == 0) {
+		bss->eap_reauth_period = atoi(pos);
+		if (bss->eap_reauth_period < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+				   line, bss->eap_reauth_period);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
+		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));
+	} else if (os_strcmp(buf, "iapp_interface") == 0) {
+		bss->ieee802_11f = 1;
+		os_strlcpy(bss->iapp_iface, pos, sizeof(bss->iapp_iface));
 #endif /* CONFIG_IAPP */
-		} else if (os_strcmp(buf, "own_ip_addr") == 0) {
-			if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid IP "
-					   "address '%s'", line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "nas_identifier") == 0) {
-			bss->nas_identifier = os_strdup(pos);
+	} else if (os_strcmp(buf, "own_ip_addr") == 0) {
+		if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "nas_identifier") == 0) {
+		os_free(bss->nas_identifier);
+		bss->nas_identifier = os_strdup(pos);
 #ifndef CONFIG_NO_RADIUS
-		} else if (os_strcmp(buf, "auth_server_addr") == 0) {
-			if (hostapd_config_read_radius_addr(
-				    &bss->radius->auth_servers,
-				    &bss->radius->num_auth_servers, pos, 1812,
-				    &bss->radius->auth_server)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid IP "
-					   "address '%s'", line, pos);
-				errors++;
-			}
-		} else if (bss->radius->auth_server &&
-			   os_strcmp(buf, "auth_server_port") == 0) {
-			bss->radius->auth_server->port = atoi(pos);
-		} else if (bss->radius->auth_server &&
-			   os_strcmp(buf, "auth_server_shared_secret") == 0) {
-			int len = os_strlen(pos);
-			if (len == 0) {
-				/* RFC 2865, Ch. 3 */
-				wpa_printf(MSG_ERROR, "Line %d: empty shared "
-					   "secret is not allowed.", line);
-				errors++;
-			}
-			bss->radius->auth_server->shared_secret =
-				(u8 *) os_strdup(pos);
-			bss->radius->auth_server->shared_secret_len = len;
-		} else if (os_strcmp(buf, "acct_server_addr") == 0) {
-			if (hostapd_config_read_radius_addr(
-				    &bss->radius->acct_servers,
-				    &bss->radius->num_acct_servers, pos, 1813,
-				    &bss->radius->acct_server)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid IP "
-					   "address '%s'", line, pos);
-				errors++;
-			}
-		} else if (bss->radius->acct_server &&
-			   os_strcmp(buf, "acct_server_port") == 0) {
-			bss->radius->acct_server->port = atoi(pos);
-		} else if (bss->radius->acct_server &&
-			   os_strcmp(buf, "acct_server_shared_secret") == 0) {
-			int len = os_strlen(pos);
-			if (len == 0) {
-				/* RFC 2865, Ch. 3 */
-				wpa_printf(MSG_ERROR, "Line %d: empty shared "
-					   "secret is not allowed.", line);
-				errors++;
-			}
-			bss->radius->acct_server->shared_secret =
-				(u8 *) os_strdup(pos);
-			bss->radius->acct_server->shared_secret_len = len;
-		} else if (os_strcmp(buf, "radius_retry_primary_interval") ==
-			   0) {
-			bss->radius->retry_primary_interval = atoi(pos);
-		} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0)
-		{
-			bss->acct_interim_interval = atoi(pos);
-		} else if (os_strcmp(buf, "radius_request_cui") == 0) {
-			bss->radius_request_cui = atoi(pos);
-		} else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
-			struct hostapd_radius_attr *attr, *a;
-			attr = hostapd_parse_radius_attr(pos);
-			if (attr == NULL) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "radius_auth_req_attr", line);
-				errors++;
-			} else if (bss->radius_auth_req_attr == NULL) {
-				bss->radius_auth_req_attr = attr;
-			} else {
-				a = bss->radius_auth_req_attr;
-				while (a->next)
-					a = a->next;
-				a->next = attr;
-			}
-		} else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
-			struct hostapd_radius_attr *attr, *a;
-			attr = hostapd_parse_radius_attr(pos);
-			if (attr == NULL) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "radius_acct_req_attr", line);
-				errors++;
-			} else if (bss->radius_acct_req_attr == NULL) {
-				bss->radius_acct_req_attr = attr;
-			} else {
-				a = bss->radius_acct_req_attr;
-				while (a->next)
-					a = a->next;
-				a->next = attr;
-			}
-		} else if (os_strcmp(buf, "radius_das_port") == 0) {
-			bss->radius_das_port = atoi(pos);
-		} else if (os_strcmp(buf, "radius_das_client") == 0) {
-			if (hostapd_parse_das_client(bss, pos) < 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "DAS client", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "radius_das_time_window") == 0) {
-			bss->radius_das_time_window = atoi(pos);
-		} else if (os_strcmp(buf, "radius_das_require_event_timestamp")
-			   == 0) {
-			bss->radius_das_require_event_timestamp = atoi(pos);
+	} else if (os_strcmp(buf, "auth_server_addr") == 0) {
+		if (hostapd_config_read_radius_addr(
+			    &bss->radius->auth_servers,
+			    &bss->radius->num_auth_servers, pos, 1812,
+			    &bss->radius->auth_server)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (bss->radius->auth_server &&
+		   os_strcmp(buf, "auth_server_port") == 0) {
+		bss->radius->auth_server->port = atoi(pos);
+	} else if (bss->radius->auth_server &&
+		   os_strcmp(buf, "auth_server_shared_secret") == 0) {
+		int len = os_strlen(pos);
+		if (len == 0) {
+			/* RFC 2865, Ch. 3 */
+			wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+				   line);
+			return 1;
+		}
+		os_free(bss->radius->auth_server->shared_secret);
+		bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
+		bss->radius->auth_server->shared_secret_len = len;
+	} else if (os_strcmp(buf, "acct_server_addr") == 0) {
+		if (hostapd_config_read_radius_addr(
+			    &bss->radius->acct_servers,
+			    &bss->radius->num_acct_servers, pos, 1813,
+			    &bss->radius->acct_server)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (bss->radius->acct_server &&
+		   os_strcmp(buf, "acct_server_port") == 0) {
+		bss->radius->acct_server->port = atoi(pos);
+	} else if (bss->radius->acct_server &&
+		   os_strcmp(buf, "acct_server_shared_secret") == 0) {
+		int len = os_strlen(pos);
+		if (len == 0) {
+			/* RFC 2865, Ch. 3 */
+			wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+				   line);
+			return 1;
+		}
+		os_free(bss->radius->acct_server->shared_secret);
+		bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
+		bss->radius->acct_server->shared_secret_len = len;
+	} else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
+		bss->radius->retry_primary_interval = atoi(pos);
+	} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {
+		bss->acct_interim_interval = atoi(pos);
+	} else if (os_strcmp(buf, "radius_request_cui") == 0) {
+		bss->radius_request_cui = atoi(pos);
+	} else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
+		struct hostapd_radius_attr *attr, *a;
+		attr = hostapd_parse_radius_attr(pos);
+		if (attr == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid radius_auth_req_attr",
+				   line);
+			return 1;
+		} else if (bss->radius_auth_req_attr == NULL) {
+			bss->radius_auth_req_attr = attr;
+		} else {
+			a = bss->radius_auth_req_attr;
+			while (a->next)
+				a = a->next;
+			a->next = attr;
+		}
+	} else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
+		struct hostapd_radius_attr *attr, *a;
+		attr = hostapd_parse_radius_attr(pos);
+		if (attr == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid radius_acct_req_attr",
+				   line);
+			return 1;
+		} else if (bss->radius_acct_req_attr == NULL) {
+			bss->radius_acct_req_attr = attr;
+		} else {
+			a = bss->radius_acct_req_attr;
+			while (a->next)
+				a = a->next;
+			a->next = attr;
+		}
+	} else if (os_strcmp(buf, "radius_das_port") == 0) {
+		bss->radius_das_port = atoi(pos);
+	} else if (os_strcmp(buf, "radius_das_client") == 0) {
+		if (hostapd_parse_das_client(bss, pos) < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid DAS client",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "radius_das_time_window") == 0) {
+		bss->radius_das_time_window = atoi(pos);
+	} else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) {
+		bss->radius_das_require_event_timestamp = atoi(pos);
 #endif /* CONFIG_NO_RADIUS */
-		} else if (os_strcmp(buf, "auth_algs") == 0) {
-			bss->auth_algs = atoi(pos);
-			if (bss->auth_algs == 0) {
-				wpa_printf(MSG_ERROR, "Line %d: no "
-					   "authentication algorithms allowed",
-					   line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "max_num_sta") == 0) {
-			bss->max_num_sta = atoi(pos);
-			if (bss->max_num_sta < 0 ||
-			    bss->max_num_sta > MAX_STA_COUNT) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "max_num_sta=%d; allowed range "
-					   "0..%d", line, bss->max_num_sta,
-					   MAX_STA_COUNT);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wpa") == 0) {
-			bss->wpa = atoi(pos);
-		} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
-			bss->wpa_group_rekey = atoi(pos);
-		} else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
-			bss->wpa_strict_rekey = atoi(pos);
-		} else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
-			bss->wpa_gmk_rekey = atoi(pos);
-		} else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
-			bss->wpa_ptk_rekey = atoi(pos);
-		} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
-			int len = os_strlen(pos);
-			if (len < 8 || len > 63) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid WPA "
-					   "passphrase length %d (expected "
-					   "8..63)", line, len);
-				errors++;
-			} else {
-				os_free(bss->ssid.wpa_passphrase);
-				bss->ssid.wpa_passphrase = os_strdup(pos);
-				os_free(bss->ssid.wpa_psk);
-				bss->ssid.wpa_psk = NULL;
-			}
-		} else if (os_strcmp(buf, "wpa_psk") == 0) {
+	} else if (os_strcmp(buf, "auth_algs") == 0) {
+		bss->auth_algs = atoi(pos);
+		if (bss->auth_algs == 0) {
+			wpa_printf(MSG_ERROR, "Line %d: no authentication algorithms allowed",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "max_num_sta") == 0) {
+		bss->max_num_sta = atoi(pos);
+		if (bss->max_num_sta < 0 ||
+		    bss->max_num_sta > MAX_STA_COUNT) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
+				   line, bss->max_num_sta, MAX_STA_COUNT);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wpa") == 0) {
+		bss->wpa = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
+		bss->wpa_group_rekey = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
+		bss->wpa_strict_rekey = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
+		bss->wpa_gmk_rekey = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
+		bss->wpa_ptk_rekey = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
+		int len = os_strlen(pos);
+		if (len < 8 || len > 63) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid WPA passphrase length %d (expected 8..63)",
+				   line, len);
+			return 1;
+		}
+		os_free(bss->ssid.wpa_passphrase);
+		bss->ssid.wpa_passphrase = os_strdup(pos);
+		if (bss->ssid.wpa_passphrase) {
 			os_free(bss->ssid.wpa_psk);
-			bss->ssid.wpa_psk =
-				os_zalloc(sizeof(struct hostapd_wpa_psk));
-			if (bss->ssid.wpa_psk == NULL)
-				errors++;
-			else if (hexstr2bin(pos, bss->ssid.wpa_psk->psk,
-					    PMK_LEN) ||
-				 pos[PMK_LEN * 2] != '\0') {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid PSK "
-					   "'%s'.", line, pos);
-				errors++;
-			} else {
-				bss->ssid.wpa_psk->group = 1;
-				os_free(bss->ssid.wpa_passphrase);
-				bss->ssid.wpa_passphrase = NULL;
-			}
-		} else if (os_strcmp(buf, "wpa_psk_file") == 0) {
-			os_free(bss->ssid.wpa_psk_file);
-			bss->ssid.wpa_psk_file = os_strdup(pos);
-			if (!bss->ssid.wpa_psk_file) {
-				wpa_printf(MSG_ERROR, "Line %d: allocation "
-					   "failed", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
-			bss->wpa_key_mgmt =
-				hostapd_config_parse_key_mgmt(line, pos);
-			if (bss->wpa_key_mgmt == -1)
-				errors++;
-		} else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
-			bss->wpa_psk_radius = atoi(pos);
-			if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
-			    bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
-			    bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
-				wpa_printf(MSG_ERROR, "Line %d: unknown "
-					   "wpa_psk_radius %d",
-					   line, bss->wpa_psk_radius);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wpa_pairwise") == 0) {
-			bss->wpa_pairwise =
-				hostapd_config_parse_cipher(line, pos);
-			if (bss->wpa_pairwise == -1 ||
-			    bss->wpa_pairwise == 0)
-				errors++;
-			else if (bss->wpa_pairwise &
-				 (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 |
-				  WPA_CIPHER_WEP104)) {
-				wpa_printf(MSG_ERROR, "Line %d: unsupported "
-					   "pairwise cipher suite '%s'",
-					   bss->wpa_pairwise, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "rsn_pairwise") == 0) {
-			bss->rsn_pairwise =
-				hostapd_config_parse_cipher(line, pos);
-			if (bss->rsn_pairwise == -1 ||
-			    bss->rsn_pairwise == 0)
-				errors++;
-			else if (bss->rsn_pairwise &
-				 (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 |
-				  WPA_CIPHER_WEP104)) {
-				wpa_printf(MSG_ERROR, "Line %d: unsupported "
-					   "pairwise cipher suite '%s'",
-					   bss->rsn_pairwise, pos);
-				errors++;
-			}
+			bss->ssid.wpa_psk = NULL;
+			bss->ssid.wpa_passphrase_set = 1;
+		}
+	} else if (os_strcmp(buf, "wpa_psk") == 0) {
+		os_free(bss->ssid.wpa_psk);
+		bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+		if (bss->ssid.wpa_psk == NULL)
+			return 1;
+		if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, PMK_LEN) ||
+		    pos[PMK_LEN * 2] != '\0') {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+				   line, pos);
+			os_free(bss->ssid.wpa_psk);
+			bss->ssid.wpa_psk = NULL;
+			return 1;
+		}
+		bss->ssid.wpa_psk->group = 1;
+		os_free(bss->ssid.wpa_passphrase);
+		bss->ssid.wpa_passphrase = NULL;
+		bss->ssid.wpa_psk_set = 1;
+	} else if (os_strcmp(buf, "wpa_psk_file") == 0) {
+		os_free(bss->ssid.wpa_psk_file);
+		bss->ssid.wpa_psk_file = os_strdup(pos);
+		if (!bss->ssid.wpa_psk_file) {
+			wpa_printf(MSG_ERROR, "Line %d: allocation failed",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
+		bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos);
+		if (bss->wpa_key_mgmt == -1)
+			return 1;
+	} else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
+		bss->wpa_psk_radius = atoi(pos);
+		if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+		    bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
+		    bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: unknown wpa_psk_radius %d",
+				   line, bss->wpa_psk_radius);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wpa_pairwise") == 0) {
+		bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos);
+		if (bss->wpa_pairwise == -1 || bss->wpa_pairwise == 0)
+			return 1;
+		if (bss->wpa_pairwise &
+		    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+			wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+				   bss->wpa_pairwise, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "rsn_pairwise") == 0) {
+		bss->rsn_pairwise = hostapd_config_parse_cipher(line, pos);
+		if (bss->rsn_pairwise == -1 || bss->rsn_pairwise == 0)
+			return 1;
+		if (bss->rsn_pairwise &
+		    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+			wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+				   bss->rsn_pairwise, pos);
+			return 1;
+		}
 #ifdef CONFIG_RSN_PREAUTH
-		} else if (os_strcmp(buf, "rsn_preauth") == 0) {
-			bss->rsn_preauth = atoi(pos);
-		} else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
-			bss->rsn_preauth_interfaces = os_strdup(pos);
+	} else if (os_strcmp(buf, "rsn_preauth") == 0) {
+		bss->rsn_preauth = atoi(pos);
+	} else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
+		os_free(bss->rsn_preauth_interfaces);
+		bss->rsn_preauth_interfaces = os_strdup(pos);
 #endif /* CONFIG_RSN_PREAUTH */
 #ifdef CONFIG_PEERKEY
-		} else if (os_strcmp(buf, "peerkey") == 0) {
-			bss->peerkey = atoi(pos);
+	} else if (os_strcmp(buf, "peerkey") == 0) {
+		bss->peerkey = atoi(pos);
 #endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211R
-		} else if (os_strcmp(buf, "mobility_domain") == 0) {
-			if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
-			    hexstr2bin(pos, bss->mobility_domain,
-				       MOBILITY_DOMAIN_ID_LEN) != 0) {
-				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-					   "mobility_domain '%s'", line, pos);
-				errors++;
-				return errors;
-			}
-		} else if (os_strcmp(buf, "r1_key_holder") == 0) {
-			if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
-			    hexstr2bin(pos, bss->r1_key_holder,
-				       FT_R1KH_ID_LEN) != 0) {
-				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-					   "r1_key_holder '%s'", line, pos);
-				errors++;
-				return errors;
-			}
-		} else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
-			bss->r0_key_lifetime = atoi(pos);
-		} else if (os_strcmp(buf, "reassociation_deadline") == 0) {
-			bss->reassociation_deadline = atoi(pos);
-		} else if (os_strcmp(buf, "r0kh") == 0) {
-			if (add_r0kh(bss, pos) < 0) {
-				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-					   "r0kh '%s'", line, pos);
-				errors++;
-				return errors;
-			}
-		} else if (os_strcmp(buf, "r1kh") == 0) {
-			if (add_r1kh(bss, pos) < 0) {
-				wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-					   "r1kh '%s'", line, pos);
-				errors++;
-				return errors;
-			}
-		} else if (os_strcmp(buf, "pmk_r1_push") == 0) {
-			bss->pmk_r1_push = atoi(pos);
-		} else if (os_strcmp(buf, "ft_over_ds") == 0) {
-			bss->ft_over_ds = atoi(pos);
+	} else if (os_strcmp(buf, "mobility_domain") == 0) {
+		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+		    hexstr2bin(pos, bss->mobility_domain,
+			       MOBILITY_DOMAIN_ID_LEN) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid mobility_domain '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "r1_key_holder") == 0) {
+		if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
+		    hexstr2bin(pos, bss->r1_key_holder, FT_R1KH_ID_LEN) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid r1_key_holder '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
+		bss->r0_key_lifetime = atoi(pos);
+	} else if (os_strcmp(buf, "reassociation_deadline") == 0) {
+		bss->reassociation_deadline = atoi(pos);
+	} else if (os_strcmp(buf, "r0kh") == 0) {
+		if (add_r0kh(bss, pos) < 0) {
+			wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "r1kh") == 0) {
+		if (add_r1kh(bss, pos) < 0) {
+			wpa_printf(MSG_DEBUG, "Line %d: Invalid r1kh '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "pmk_r1_push") == 0) {
+		bss->pmk_r1_push = atoi(pos);
+	} else if (os_strcmp(buf, "ft_over_ds") == 0) {
+		bss->ft_over_ds = atoi(pos);
 #endif /* CONFIG_IEEE80211R */
 #ifndef CONFIG_NO_CTRL_IFACE
-		} else if (os_strcmp(buf, "ctrl_interface") == 0) {
-			os_free(bss->ctrl_interface);
-			bss->ctrl_interface = os_strdup(pos);
-		} else if (os_strcmp(buf, "ctrl_interface_group") == 0) {
+	} else if (os_strcmp(buf, "ctrl_interface") == 0) {
+		os_free(bss->ctrl_interface);
+		bss->ctrl_interface = os_strdup(pos);
+	} else if (os_strcmp(buf, "ctrl_interface_group") == 0) {
 #ifndef CONFIG_NATIVE_WINDOWS
-			struct group *grp;
-			char *endp;
-			const char *group = pos;
+		struct group *grp;
+		char *endp;
+		const char *group = pos;
 
-			grp = getgrnam(group);
-			if (grp) {
-				bss->ctrl_interface_gid = grp->gr_gid;
-				bss->ctrl_interface_gid_set = 1;
-				wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
-					   " (from group name '%s')",
-					   bss->ctrl_interface_gid, group);
-				return errors;
-			}
-
-			/* Group name not found - try to parse this as gid */
-			bss->ctrl_interface_gid = strtol(group, &endp, 10);
-			if (*group == '\0' || *endp != '\0') {
-				wpa_printf(MSG_DEBUG, "Line %d: Invalid group "
-					   "'%s'", line, group);
-				errors++;
-				return errors;
-			}
+		grp = getgrnam(group);
+		if (grp) {
+			bss->ctrl_interface_gid = grp->gr_gid;
 			bss->ctrl_interface_gid_set = 1;
-			wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
-				   bss->ctrl_interface_gid);
+			wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d (from group name '%s')",
+				   bss->ctrl_interface_gid, group);
+			return 0;
+		}
+
+		/* Group name not found - try to parse this as gid */
+		bss->ctrl_interface_gid = strtol(group, &endp, 10);
+		if (*group == '\0' || *endp != '\0') {
+			wpa_printf(MSG_DEBUG, "Line %d: Invalid group '%s'",
+				   line, group);
+			return 1;
+		}
+		bss->ctrl_interface_gid_set = 1;
+		wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+			   bss->ctrl_interface_gid);
 #endif /* CONFIG_NATIVE_WINDOWS */
 #endif /* CONFIG_NO_CTRL_IFACE */
 #ifdef RADIUS_SERVER
-		} else if (os_strcmp(buf, "radius_server_clients") == 0) {
-			os_free(bss->radius_server_clients);
-			bss->radius_server_clients = os_strdup(pos);
-		} else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
-			bss->radius_server_auth_port = atoi(pos);
-		} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
-			bss->radius_server_ipv6 = atoi(pos);
+	} else if (os_strcmp(buf, "radius_server_clients") == 0) {
+		os_free(bss->radius_server_clients);
+		bss->radius_server_clients = os_strdup(pos);
+	} else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
+		bss->radius_server_auth_port = atoi(pos);
+	} else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
+		bss->radius_server_acct_port = atoi(pos);
+	} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
+		bss->radius_server_ipv6 = atoi(pos);
 #endif /* RADIUS_SERVER */
-		} else if (os_strcmp(buf, "test_socket") == 0) {
-			os_free(bss->test_socket);
-			bss->test_socket = os_strdup(pos);
-		} else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
-			bss->use_pae_group_addr = atoi(pos);
-		} else if (os_strcmp(buf, "hw_mode") == 0) {
-			if (os_strcmp(pos, "a") == 0)
-				conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
-			else if (os_strcmp(pos, "b") == 0)
-				conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
-			else if (os_strcmp(pos, "g") == 0)
-				conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
-			else if (os_strcmp(pos, "ad") == 0)
-				conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
-			else {
-				wpa_printf(MSG_ERROR, "Line %d: unknown "
-					   "hw_mode '%s'", line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wps_rf_bands") == 0) {
-			if (os_strcmp(pos, "a") == 0)
-				bss->wps_rf_bands = WPS_RF_50GHZ;
-			else if (os_strcmp(pos, "g") == 0 ||
-				 os_strcmp(pos, "b") == 0)
-				bss->wps_rf_bands = WPS_RF_24GHZ;
-			else if (os_strcmp(pos, "ag") == 0 ||
-				 os_strcmp(pos, "ga") == 0)
-				bss->wps_rf_bands =
-					WPS_RF_24GHZ | WPS_RF_50GHZ;
-			else {
-				wpa_printf(MSG_ERROR, "Line %d: unknown "
-					   "wps_rf_band '%s'", line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "channel") == 0) {
-			if (os_strcmp(pos, "acs_survey") == 0) {
+	} else if (os_strcmp(buf, "test_socket") == 0) {
+		os_free(bss->test_socket);
+		bss->test_socket = os_strdup(pos);
+	} else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
+		bss->use_pae_group_addr = atoi(pos);
+	} else if (os_strcmp(buf, "hw_mode") == 0) {
+		if (os_strcmp(pos, "a") == 0)
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+		else if (os_strcmp(pos, "b") == 0)
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+		else if (os_strcmp(pos, "g") == 0)
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+		else if (os_strcmp(pos, "ad") == 0)
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+		else {
+			wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wps_rf_bands") == 0) {
+		if (os_strcmp(pos, "a") == 0)
+			bss->wps_rf_bands = WPS_RF_50GHZ;
+		else if (os_strcmp(pos, "g") == 0 ||
+			 os_strcmp(pos, "b") == 0)
+			bss->wps_rf_bands = WPS_RF_24GHZ;
+		else if (os_strcmp(pos, "ag") == 0 ||
+			 os_strcmp(pos, "ga") == 0)
+			bss->wps_rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+		else {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: unknown wps_rf_band '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "channel") == 0) {
+		if (os_strcmp(pos, "acs_survey") == 0) {
 #ifndef CONFIG_ACS
-				wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
-					   line);
-				errors++;
+			wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
+				   line);
+			return 1;
+#else /* CONFIG_ACS */
+			conf->channel = 0;
 #endif /* CONFIG_ACS */
-				conf->channel = 0;
-			} else
-				conf->channel = atoi(pos);
-		} else if (os_strcmp(buf, "beacon_int") == 0) {
-			int val = atoi(pos);
-			/* MIB defines range as 1..65535, but very small values
-			 * cause problems with the current implementation.
-			 * Since it is unlikely that this small numbers are
-			 * useful in real life scenarios, do not allow beacon
-			 * period to be set below 15 TU. */
-			if (val < 15 || val > 65535) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "beacon_int %d (expected "
-					   "15..65535)", line, val);
-				errors++;
-			} else
-				conf->beacon_int = val;
+		} else
+			conf->channel = atoi(pos);
+	} else if (os_strcmp(buf, "chanlist") == 0) {
+		if (hostapd_parse_intlist(&conf->chanlist, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "beacon_int") == 0) {
+		int val = atoi(pos);
+		/* MIB defines range as 1..65535, but very small values
+		 * cause problems with the current implementation.
+		 * Since it is unlikely that this small numbers are
+		 * useful in real life scenarios, do not allow beacon
+		 * period to be set below 15 TU. */
+		if (val < 15 || val > 65535) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid beacon_int %d (expected 15..65535)",
+				   line, val);
+			return 1;
+		}
+		conf->beacon_int = val;
 #ifdef CONFIG_ACS
-		} else if (os_strcmp(buf, "acs_num_scans") == 0) {
-			int val = atoi(pos);
-			if (val <= 0 || val > 100) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
-					   line, val);
-				errors++;
-			} else
-				conf->acs_num_scans = val;
+	} else if (os_strcmp(buf, "acs_num_scans") == 0) {
+		int val = atoi(pos);
+		if (val <= 0 || val > 100) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
+				   line, val);
+			return 1;
+		}
+		conf->acs_num_scans = val;
 #endif /* CONFIG_ACS */
-		} else if (os_strcmp(buf, "dtim_period") == 0) {
-			bss->dtim_period = atoi(pos);
-			if (bss->dtim_period < 1 || bss->dtim_period > 255) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "dtim_period %d",
-					   line, bss->dtim_period);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "rts_threshold") == 0) {
-			conf->rts_threshold = atoi(pos);
-			if (conf->rts_threshold < 0 ||
-			    conf->rts_threshold > 2347) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "rts_threshold %d",
-					   line, conf->rts_threshold);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "fragm_threshold") == 0) {
-			conf->fragm_threshold = atoi(pos);
-			if (conf->fragm_threshold < 256 ||
-			    conf->fragm_threshold > 2346) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "fragm_threshold %d",
-					   line, conf->fragm_threshold);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "send_probe_response") == 0) {
-			int val = atoi(pos);
-			if (val != 0 && val != 1) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "send_probe_response %d (expected "
-					   "0 or 1)", line, val);
-			} else
-				conf->send_probe_response = val;
-		} else if (os_strcmp(buf, "supported_rates") == 0) {
-			if (hostapd_parse_intlist(&conf->supported_rates, pos))
-			{
-				wpa_printf(MSG_ERROR, "Line %d: invalid rate "
-					   "list", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "basic_rates") == 0) {
-			if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid rate "
-					   "list", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "preamble") == 0) {
-			if (atoi(pos))
-				conf->preamble = SHORT_PREAMBLE;
-			else
-				conf->preamble = LONG_PREAMBLE;
-		} else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
-			bss->ignore_broadcast_ssid = atoi(pos);
-		} else if (os_strcmp(buf, "wep_default_key") == 0) {
-			bss->ssid.wep.idx = atoi(pos);
-			if (bss->ssid.wep.idx > 3) {
-				wpa_printf(MSG_ERROR, "Invalid "
-					   "wep_default_key index %d",
-					   bss->ssid.wep.idx);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wep_key0") == 0 ||
-			   os_strcmp(buf, "wep_key1") == 0 ||
-			   os_strcmp(buf, "wep_key2") == 0 ||
-			   os_strcmp(buf, "wep_key3") == 0) {
-			if (hostapd_config_read_wep(&bss->ssid.wep,
-						    buf[7] - '0', pos)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
-					   "key '%s'", line, buf);
-				errors++;
-			}
+	} else if (os_strcmp(buf, "dtim_period") == 0) {
+		bss->dtim_period = atoi(pos);
+		if (bss->dtim_period < 1 || bss->dtim_period > 255) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d",
+				   line, bss->dtim_period);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "rts_threshold") == 0) {
+		conf->rts_threshold = atoi(pos);
+		if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid rts_threshold %d",
+				   line, conf->rts_threshold);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "fragm_threshold") == 0) {
+		conf->fragm_threshold = atoi(pos);
+		if (conf->fragm_threshold < 256 ||
+		    conf->fragm_threshold > 2346) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid fragm_threshold %d",
+				   line, conf->fragm_threshold);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "send_probe_response") == 0) {
+		int val = atoi(pos);
+		if (val != 0 && val != 1) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid send_probe_response %d (expected 0 or 1)",
+				   line, val);
+			return 1;
+		}
+		conf->send_probe_response = val;
+	} else if (os_strcmp(buf, "supported_rates") == 0) {
+		if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "basic_rates") == 0) {
+		if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "preamble") == 0) {
+		if (atoi(pos))
+			conf->preamble = SHORT_PREAMBLE;
+		else
+			conf->preamble = LONG_PREAMBLE;
+	} else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
+		bss->ignore_broadcast_ssid = atoi(pos);
+	} else if (os_strcmp(buf, "wep_default_key") == 0) {
+		bss->ssid.wep.idx = atoi(pos);
+		if (bss->ssid.wep.idx > 3) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid wep_default_key index %d",
+				   bss->ssid.wep.idx);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wep_key0") == 0 ||
+		   os_strcmp(buf, "wep_key1") == 0 ||
+		   os_strcmp(buf, "wep_key2") == 0 ||
+		   os_strcmp(buf, "wep_key3") == 0) {
+		if (hostapd_config_read_wep(&bss->ssid.wep,
+					    buf[7] - '0', pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid WEP key '%s'",
+				   line, buf);
+			return 1;
+		}
 #ifndef CONFIG_NO_VLAN
-		} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
-			bss->ssid.dynamic_vlan = atoi(pos);
-		} else if (os_strcmp(buf, "vlan_file") == 0) {
-			if (hostapd_config_read_vlan_file(bss, pos)) {
-				wpa_printf(MSG_ERROR, "Line %d: failed to "
-					   "read VLAN file '%s'", line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "vlan_naming") == 0) {
-			bss->ssid.vlan_naming = atoi(pos);
-			if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
-			    bss->ssid.vlan_naming < 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "naming scheme %d", line,
-                                           bss->ssid.vlan_naming);
-				errors++;
-                        }
+	} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+		bss->ssid.dynamic_vlan = atoi(pos);
+	} else if (os_strcmp(buf, "vlan_file") == 0) {
+		if (hostapd_config_read_vlan_file(bss, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "vlan_naming") == 0) {
+		bss->ssid.vlan_naming = atoi(pos);
+		if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
+		    bss->ssid.vlan_naming < 0) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid naming scheme %d",
+				   line, bss->ssid.vlan_naming);
+			return 1;
+		}
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
-		} else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
-			bss->ssid.vlan_tagged_interface = os_strdup(pos);
+	} else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
+		os_free(bss->ssid.vlan_tagged_interface);
+		bss->ssid.vlan_tagged_interface = os_strdup(pos);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 #endif /* CONFIG_NO_VLAN */
-		} else if (os_strcmp(buf, "ap_table_max_size") == 0) {
-			conf->ap_table_max_size = atoi(pos);
-		} else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
-			conf->ap_table_expiration_time = atoi(pos);
-		} else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
-			if (hostapd_config_tx_queue(conf, buf, pos)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid TX "
-					   "queue item", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wme_enabled") == 0 ||
-			   os_strcmp(buf, "wmm_enabled") == 0) {
-			bss->wmm_enabled = atoi(pos);
-		} else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
-			bss->wmm_uapsd = atoi(pos);
-		} else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
-			   os_strncmp(buf, "wmm_ac_", 7) == 0) {
-			if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf,
-						  pos)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
-					   "ac item", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "bss") == 0) {
-			if (hostapd_config_bss(conf, pos)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid bss "
-					   "item", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "bssid") == 0) {
-			if (hwaddr_aton(pos, bss->bssid)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid bssid "
-					   "item", line);
-				errors++;
-			}
+	} else if (os_strcmp(buf, "ap_table_max_size") == 0) {
+		conf->ap_table_max_size = atoi(pos);
+	} else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
+		conf->ap_table_expiration_time = atoi(pos);
+	} else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
+		if (hostapd_config_tx_queue(conf, buf, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wme_enabled") == 0 ||
+		   os_strcmp(buf, "wmm_enabled") == 0) {
+		bss->wmm_enabled = atoi(pos);
+	} else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
+		bss->wmm_uapsd = atoi(pos);
+	} else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
+		   os_strncmp(buf, "wmm_ac_", 7) == 0) {
+		if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid WMM ac item",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "bss") == 0) {
+		if (hostapd_config_bss(conf, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid bss item",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "bssid") == 0) {
+		if (hwaddr_aton(pos, bss->bssid)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid bssid item",
+				   line);
+			return 1;
+		}
 #ifdef CONFIG_IEEE80211W
-		} else if (os_strcmp(buf, "ieee80211w") == 0) {
-			bss->ieee80211w = atoi(pos);
-		} else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
-			bss->assoc_sa_query_max_timeout = atoi(pos);
-			if (bss->assoc_sa_query_max_timeout == 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "assoc_sa_query_max_timeout", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0)
-		{
-			bss->assoc_sa_query_retry_timeout = atoi(pos);
-			if (bss->assoc_sa_query_retry_timeout == 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "assoc_sa_query_retry_timeout",
-					   line);
-				errors++;
-			}
+	} else if (os_strcmp(buf, "ieee80211w") == 0) {
+		bss->ieee80211w = atoi(pos);
+	} else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
+		if (os_strcmp(pos, "AES-128-CMAC") == 0) {
+			bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+		} else if (os_strcmp(pos, "BIP-GMAC-128") == 0) {
+			bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_128;
+		} else if (os_strcmp(pos, "BIP-GMAC-256") == 0) {
+			bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_256;
+		} else if (os_strcmp(pos, "BIP-CMAC-256") == 0) {
+			bss->group_mgmt_cipher = WPA_CIPHER_BIP_CMAC_256;
+		} else {
+			wpa_printf(MSG_ERROR, "Line %d: invalid group_mgmt_cipher: %s",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
+		bss->assoc_sa_query_max_timeout = atoi(pos);
+		if (bss->assoc_sa_query_max_timeout == 0) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_max_timeout",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) {
+		bss->assoc_sa_query_retry_timeout = atoi(pos);
+		if (bss->assoc_sa_query_retry_timeout == 0) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_retry_timeout",
+				   line);
+			return 1;
+		}
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211N
-		} else if (os_strcmp(buf, "ieee80211n") == 0) {
-			conf->ieee80211n = atoi(pos);
-		} else if (os_strcmp(buf, "ht_capab") == 0) {
-			if (hostapd_config_ht_capab(conf, pos) < 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "ht_capab", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "require_ht") == 0) {
-			conf->require_ht = atoi(pos);
+	} else if (os_strcmp(buf, "ieee80211n") == 0) {
+		conf->ieee80211n = atoi(pos);
+	} else if (os_strcmp(buf, "ht_capab") == 0) {
+		if (hostapd_config_ht_capab(conf, pos) < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid ht_capab",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "require_ht") == 0) {
+		conf->require_ht = atoi(pos);
+	} else if (os_strcmp(buf, "obss_interval") == 0) {
+		conf->obss_interval = atoi(pos);
 #endif /* CONFIG_IEEE80211N */
 #ifdef CONFIG_IEEE80211AC
-		} else if (os_strcmp(buf, "ieee80211ac") == 0) {
-			conf->ieee80211ac = atoi(pos);
-		} else if (os_strcmp(buf, "vht_capab") == 0) {
-			if (hostapd_config_vht_capab(conf, pos) < 0) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "vht_capab", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "require_vht") == 0) {
-			conf->require_vht = atoi(pos);
-		} else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
-			conf->vht_oper_chwidth = atoi(pos);
-		} else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0)
-		{
-			conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
-		} else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0)
-		{
-			conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
+	} else if (os_strcmp(buf, "ieee80211ac") == 0) {
+		conf->ieee80211ac = atoi(pos);
+	} else if (os_strcmp(buf, "vht_capab") == 0) {
+		if (hostapd_config_vht_capab(conf, pos) < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid vht_capab",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "require_vht") == 0) {
+		conf->require_vht = atoi(pos);
+	} else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
+		conf->vht_oper_chwidth = atoi(pos);
+	} else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) {
+		conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
+	} else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) {
+		conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
 #endif /* CONFIG_IEEE80211AC */
-		} else if (os_strcmp(buf, "max_listen_interval") == 0) {
-			bss->max_listen_interval = atoi(pos);
-		} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
-			bss->disable_pmksa_caching = atoi(pos);
-		} else if (os_strcmp(buf, "okc") == 0) {
-			bss->okc = atoi(pos);
+	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
+		bss->max_listen_interval = atoi(pos);
+	} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
+		bss->disable_pmksa_caching = atoi(pos);
+	} else if (os_strcmp(buf, "okc") == 0) {
+		bss->okc = atoi(pos);
 #ifdef CONFIG_WPS
-		} else if (os_strcmp(buf, "wps_state") == 0) {
-			bss->wps_state = atoi(pos);
-			if (bss->wps_state < 0 || bss->wps_state > 2) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "wps_state", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wps_independent") == 0) {
-			bss->wps_independent = atoi(pos);
-		} else if (os_strcmp(buf, "ap_setup_locked") == 0) {
-			bss->ap_setup_locked = atoi(pos);
-		} else if (os_strcmp(buf, "uuid") == 0) {
-			if (uuid_str2bin(pos, bss->uuid)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid UUID",
-					   line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wps_pin_requests") == 0) {
-			os_free(bss->wps_pin_requests);
-			bss->wps_pin_requests = os_strdup(pos);
-		} else if (os_strcmp(buf, "device_name") == 0) {
-			if (os_strlen(pos) > 32) {
-				wpa_printf(MSG_ERROR, "Line %d: Too long "
-					   "device_name", line);
-				errors++;
-			}
-			os_free(bss->device_name);
-			bss->device_name = os_strdup(pos);
-		} else if (os_strcmp(buf, "manufacturer") == 0) {
-			if (os_strlen(pos) > 64) {
-				wpa_printf(MSG_ERROR, "Line %d: Too long "
-					   "manufacturer", line);
-				errors++;
-			}
-			os_free(bss->manufacturer);
-			bss->manufacturer = os_strdup(pos);
-		} else if (os_strcmp(buf, "model_name") == 0) {
-			if (os_strlen(pos) > 32) {
-				wpa_printf(MSG_ERROR, "Line %d: Too long "
-					   "model_name", line);
-				errors++;
-			}
-			os_free(bss->model_name);
-			bss->model_name = os_strdup(pos);
-		} else if (os_strcmp(buf, "model_number") == 0) {
-			if (os_strlen(pos) > 32) {
-				wpa_printf(MSG_ERROR, "Line %d: Too long "
-					   "model_number", line);
-				errors++;
-			}
-			os_free(bss->model_number);
-			bss->model_number = os_strdup(pos);
-		} else if (os_strcmp(buf, "serial_number") == 0) {
-			if (os_strlen(pos) > 32) {
-				wpa_printf(MSG_ERROR, "Line %d: Too long "
-					   "serial_number", line);
-				errors++;
-			}
-			os_free(bss->serial_number);
-			bss->serial_number = os_strdup(pos);
-		} else if (os_strcmp(buf, "device_type") == 0) {
-			if (wps_dev_type_str2bin(pos, bss->device_type))
-				errors++;
-		} else if (os_strcmp(buf, "config_methods") == 0) {
-			os_free(bss->config_methods);
-			bss->config_methods = os_strdup(pos);
-		} else if (os_strcmp(buf, "os_version") == 0) {
-			if (hexstr2bin(pos, bss->os_version, 4)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "os_version", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "ap_pin") == 0) {
-			os_free(bss->ap_pin);
-			bss->ap_pin = os_strdup(pos);
-		} else if (os_strcmp(buf, "skip_cred_build") == 0) {
-			bss->skip_cred_build = atoi(pos);
-		} else if (os_strcmp(buf, "extra_cred") == 0) {
-			os_free(bss->extra_cred);
-			bss->extra_cred =
-				(u8 *) os_readfile(pos, &bss->extra_cred_len);
-			if (bss->extra_cred == NULL) {
-				wpa_printf(MSG_ERROR, "Line %d: could not "
-					   "read Credentials from '%s'",
-					   line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "wps_cred_processing") == 0) {
-			bss->wps_cred_processing = atoi(pos);
-		} else if (os_strcmp(buf, "ap_settings") == 0) {
-			os_free(bss->ap_settings);
-			bss->ap_settings =
-				(u8 *) os_readfile(pos, &bss->ap_settings_len);
-			if (bss->ap_settings == NULL) {
-				wpa_printf(MSG_ERROR, "Line %d: could not "
-					   "read AP Settings from '%s'",
-					   line, pos);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "upnp_iface") == 0) {
-			bss->upnp_iface = os_strdup(pos);
-		} else if (os_strcmp(buf, "friendly_name") == 0) {
-			os_free(bss->friendly_name);
-			bss->friendly_name = os_strdup(pos);
-		} else if (os_strcmp(buf, "manufacturer_url") == 0) {
-			os_free(bss->manufacturer_url);
-			bss->manufacturer_url = os_strdup(pos);
-		} else if (os_strcmp(buf, "model_description") == 0) {
-			os_free(bss->model_description);
-			bss->model_description = os_strdup(pos);
-		} else if (os_strcmp(buf, "model_url") == 0) {
-			os_free(bss->model_url);
-			bss->model_url = os_strdup(pos);
-		} else if (os_strcmp(buf, "upc") == 0) {
-			os_free(bss->upc);
-			bss->upc = os_strdup(pos);
-		} else if (os_strcmp(buf, "pbc_in_m1") == 0) {
-			bss->pbc_in_m1 = atoi(pos);
-		} else if (os_strcmp(buf, "server_id") == 0) {
-			os_free(bss->server_id);
-			bss->server_id = os_strdup(pos);
+	} else if (os_strcmp(buf, "wps_state") == 0) {
+		bss->wps_state = atoi(pos);
+		if (bss->wps_state < 0 || bss->wps_state > 2) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid wps_state",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wps_independent") == 0) {
+		bss->wps_independent = atoi(pos);
+	} else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+		bss->ap_setup_locked = atoi(pos);
+	} else if (os_strcmp(buf, "uuid") == 0) {
+		if (uuid_str2bin(pos, bss->uuid)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wps_pin_requests") == 0) {
+		os_free(bss->wps_pin_requests);
+		bss->wps_pin_requests = os_strdup(pos);
+	} else if (os_strcmp(buf, "device_name") == 0) {
+		if (os_strlen(pos) > 32) {
+			wpa_printf(MSG_ERROR, "Line %d: Too long "
+				   "device_name", line);
+			return 1;
+		}
+		os_free(bss->device_name);
+		bss->device_name = os_strdup(pos);
+	} else if (os_strcmp(buf, "manufacturer") == 0) {
+		if (os_strlen(pos) > 64) {
+			wpa_printf(MSG_ERROR, "Line %d: Too long manufacturer",
+				   line);
+			return 1;
+		}
+		os_free(bss->manufacturer);
+		bss->manufacturer = os_strdup(pos);
+	} else if (os_strcmp(buf, "model_name") == 0) {
+		if (os_strlen(pos) > 32) {
+			wpa_printf(MSG_ERROR, "Line %d: Too long model_name",
+				   line);
+			return 1;
+		}
+		os_free(bss->model_name);
+		bss->model_name = os_strdup(pos);
+	} else if (os_strcmp(buf, "model_number") == 0) {
+		if (os_strlen(pos) > 32) {
+			wpa_printf(MSG_ERROR, "Line %d: Too long model_number",
+				   line);
+			return 1;
+		}
+		os_free(bss->model_number);
+		bss->model_number = os_strdup(pos);
+	} else if (os_strcmp(buf, "serial_number") == 0) {
+		if (os_strlen(pos) > 32) {
+			wpa_printf(MSG_ERROR, "Line %d: Too long serial_number",
+				   line);
+			return 1;
+		}
+		os_free(bss->serial_number);
+		bss->serial_number = os_strdup(pos);
+	} else if (os_strcmp(buf, "device_type") == 0) {
+		if (wps_dev_type_str2bin(pos, bss->device_type))
+			return 1;
+	} else if (os_strcmp(buf, "config_methods") == 0) {
+		os_free(bss->config_methods);
+		bss->config_methods = os_strdup(pos);
+	} else if (os_strcmp(buf, "os_version") == 0) {
+		if (hexstr2bin(pos, bss->os_version, 4)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid os_version",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "ap_pin") == 0) {
+		os_free(bss->ap_pin);
+		bss->ap_pin = os_strdup(pos);
+	} else if (os_strcmp(buf, "skip_cred_build") == 0) {
+		bss->skip_cred_build = atoi(pos);
+	} else if (os_strcmp(buf, "extra_cred") == 0) {
+		os_free(bss->extra_cred);
+		bss->extra_cred = (u8 *) os_readfile(pos, &bss->extra_cred_len);
+		if (bss->extra_cred == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: could not read Credentials from '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "wps_cred_processing") == 0) {
+		bss->wps_cred_processing = atoi(pos);
+	} else if (os_strcmp(buf, "ap_settings") == 0) {
+		os_free(bss->ap_settings);
+		bss->ap_settings =
+			(u8 *) os_readfile(pos, &bss->ap_settings_len);
+		if (bss->ap_settings == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: could not read AP Settings from '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "upnp_iface") == 0) {
+		os_free(bss->upnp_iface);
+		bss->upnp_iface = os_strdup(pos);
+	} else if (os_strcmp(buf, "friendly_name") == 0) {
+		os_free(bss->friendly_name);
+		bss->friendly_name = os_strdup(pos);
+	} else if (os_strcmp(buf, "manufacturer_url") == 0) {
+		os_free(bss->manufacturer_url);
+		bss->manufacturer_url = os_strdup(pos);
+	} else if (os_strcmp(buf, "model_description") == 0) {
+		os_free(bss->model_description);
+		bss->model_description = os_strdup(pos);
+	} else if (os_strcmp(buf, "model_url") == 0) {
+		os_free(bss->model_url);
+		bss->model_url = os_strdup(pos);
+	} else if (os_strcmp(buf, "upc") == 0) {
+		os_free(bss->upc);
+		bss->upc = os_strdup(pos);
+	} else if (os_strcmp(buf, "pbc_in_m1") == 0) {
+		bss->pbc_in_m1 = atoi(pos);
+	} else if (os_strcmp(buf, "server_id") == 0) {
+		os_free(bss->server_id);
+		bss->server_id = os_strdup(pos);
 #ifdef CONFIG_WPS_NFC
-		} else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
-			bss->wps_nfc_dev_pw_id = atoi(pos);
-			if (bss->wps_nfc_dev_pw_id < 0x10 ||
-			    bss->wps_nfc_dev_pw_id > 0xffff) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "wps_nfc_dev_pw_id value", line);
-				errors++;
-			}
-			bss->wps_nfc_pw_from_config = 1;
-		} else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
-			wpabuf_free(bss->wps_nfc_dh_pubkey);
-			bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
-			bss->wps_nfc_pw_from_config = 1;
-		} else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
-			wpabuf_free(bss->wps_nfc_dh_privkey);
-			bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
-			bss->wps_nfc_pw_from_config = 1;
-		} else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
-			wpabuf_free(bss->wps_nfc_dev_pw);
-			bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
-			bss->wps_nfc_pw_from_config = 1;
+	} else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
+		bss->wps_nfc_dev_pw_id = atoi(pos);
+		if (bss->wps_nfc_dev_pw_id < 0x10 ||
+		    bss->wps_nfc_dev_pw_id > 0xffff) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid wps_nfc_dev_pw_id value",
+				   line);
+			return 1;
+		}
+		bss->wps_nfc_pw_from_config = 1;
+	} else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
+		wpabuf_free(bss->wps_nfc_dh_pubkey);
+		bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
+		bss->wps_nfc_pw_from_config = 1;
+	} else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
+		wpabuf_free(bss->wps_nfc_dh_privkey);
+		bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
+		bss->wps_nfc_pw_from_config = 1;
+	} else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
+		wpabuf_free(bss->wps_nfc_dev_pw);
+		bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
+		bss->wps_nfc_pw_from_config = 1;
 #endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P_MANAGER
-		} else if (os_strcmp(buf, "manage_p2p") == 0) {
-			int manage = atoi(pos);
-			if (manage)
-				bss->p2p |= P2P_MANAGE;
-			else
-				bss->p2p &= ~P2P_MANAGE;
-		} else if (os_strcmp(buf, "allow_cross_connection") == 0) {
-			if (atoi(pos))
-				bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
-			else
-				bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
+	} else if (os_strcmp(buf, "manage_p2p") == 0) {
+		if (atoi(pos))
+			bss->p2p |= P2P_MANAGE;
+		else
+			bss->p2p &= ~P2P_MANAGE;
+	} else if (os_strcmp(buf, "allow_cross_connection") == 0) {
+		if (atoi(pos))
+			bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
+		else
+			bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
 #endif /* CONFIG_P2P_MANAGER */
-		} else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
-			bss->disassoc_low_ack = atoi(pos);
-		} else if (os_strcmp(buf, "tdls_prohibit") == 0) {
-			int val = atoi(pos);
-			if (val)
-				bss->tdls |= TDLS_PROHIBIT;
-			else
-				bss->tdls &= ~TDLS_PROHIBIT;
-		} else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
-			int val = atoi(pos);
-			if (val)
-				bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
-			else
-				bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
+	} else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
+		bss->disassoc_low_ack = atoi(pos);
+	} else if (os_strcmp(buf, "tdls_prohibit") == 0) {
+		if (atoi(pos))
+			bss->tdls |= TDLS_PROHIBIT;
+		else
+			bss->tdls &= ~TDLS_PROHIBIT;
+	} else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
+		if (atoi(pos))
+			bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
+		else
+			bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
 #ifdef CONFIG_RSN_TESTING
-		} else if (os_strcmp(buf, "rsn_testing") == 0) {
-			extern int rsn_testing;
-			rsn_testing = atoi(pos);
+	} else if (os_strcmp(buf, "rsn_testing") == 0) {
+		extern int rsn_testing;
+		rsn_testing = atoi(pos);
 #endif /* CONFIG_RSN_TESTING */
-		} else if (os_strcmp(buf, "time_advertisement") == 0) {
-			bss->time_advertisement = atoi(pos);
-		} else if (os_strcmp(buf, "time_zone") == 0) {
-			size_t tz_len = os_strlen(pos);
-			if (tz_len < 4 || tz_len > 255) {
-				wpa_printf(MSG_DEBUG, "Line %d: invalid "
-					   "time_zone", line);
-				errors++;
-				return errors;
-			}
-			os_free(bss->time_zone);
-			bss->time_zone = os_strdup(pos);
-			if (bss->time_zone == NULL)
-				errors++;
+	} else if (os_strcmp(buf, "time_advertisement") == 0) {
+		bss->time_advertisement = atoi(pos);
+	} else if (os_strcmp(buf, "time_zone") == 0) {
+		size_t tz_len = os_strlen(pos);
+		if (tz_len < 4 || tz_len > 255) {
+			wpa_printf(MSG_DEBUG, "Line %d: invalid time_zone",
+				   line);
+			return 1;
+		}
+		os_free(bss->time_zone);
+		bss->time_zone = os_strdup(pos);
+		if (bss->time_zone == NULL)
+			return 1;
 #ifdef CONFIG_WNM
-		} else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
-			bss->wnm_sleep_mode = atoi(pos);
-		} else if (os_strcmp(buf, "bss_transition") == 0) {
-			bss->bss_transition = atoi(pos);
+	} else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
+		bss->wnm_sleep_mode = atoi(pos);
+	} else if (os_strcmp(buf, "bss_transition") == 0) {
+		bss->bss_transition = atoi(pos);
 #endif /* CONFIG_WNM */
 #ifdef CONFIG_INTERWORKING
-		} else if (os_strcmp(buf, "interworking") == 0) {
-			bss->interworking = atoi(pos);
-		} else if (os_strcmp(buf, "access_network_type") == 0) {
-			bss->access_network_type = atoi(pos);
-			if (bss->access_network_type < 0 ||
-			    bss->access_network_type > 15) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "access_network_type", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "internet") == 0) {
-			bss->internet = atoi(pos);
-		} else if (os_strcmp(buf, "asra") == 0) {
-			bss->asra = atoi(pos);
-		} else if (os_strcmp(buf, "esr") == 0) {
-			bss->esr = atoi(pos);
-		} else if (os_strcmp(buf, "uesa") == 0) {
-			bss->uesa = atoi(pos);
-		} else if (os_strcmp(buf, "venue_group") == 0) {
-			bss->venue_group = atoi(pos);
-			bss->venue_info_set = 1;
-		} else if (os_strcmp(buf, "venue_type") == 0) {
-			bss->venue_type = atoi(pos);
-			bss->venue_info_set = 1;
-		} else if (os_strcmp(buf, "hessid") == 0) {
-			if (hwaddr_aton(pos, bss->hessid)) {
-				wpa_printf(MSG_ERROR, "Line %d: invalid "
-					   "hessid", line);
-				errors++;
-			}
-		} else if (os_strcmp(buf, "roaming_consortium") == 0) {
-			if (parse_roaming_consortium(bss, pos, line) < 0)
-				errors++;
-		} else if (os_strcmp(buf, "venue_name") == 0) {
-			if (parse_venue_name(bss, pos, line) < 0)
-				errors++;
-		} else if (os_strcmp(buf, "network_auth_type") == 0) {
-			u8 auth_type;
-			u16 redirect_url_len;
-			if (hexstr2bin(pos, &auth_type, 1)) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "network_auth_type '%s'",
-					   line, pos);
-				errors++;
-				return errors;
-			}
-			if (auth_type == 0 || auth_type == 2)
-				redirect_url_len = os_strlen(pos + 2);
-			else
-				redirect_url_len = 0;
-			os_free(bss->network_auth_type);
-			bss->network_auth_type =
-				os_malloc(redirect_url_len + 3 + 1);
-			if (bss->network_auth_type == NULL) {
-				errors++;
-				return errors;
-			}
-			*bss->network_auth_type = auth_type;
-			WPA_PUT_LE16(bss->network_auth_type + 1,
-				     redirect_url_len);
-			if (redirect_url_len)
-				os_memcpy(bss->network_auth_type + 3,
-					  pos + 2, redirect_url_len);
-			bss->network_auth_type_len = 3 + redirect_url_len;
-		} else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
-			if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1))
-			{
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "ipaddr_type_availability '%s'",
-					   line, pos);
-				bss->ipaddr_type_configured = 0;
-				errors++;
-				return errors;
-			}
-			bss->ipaddr_type_configured = 1;
-		} else if (os_strcmp(buf, "domain_name") == 0) {
-			int j, num_domains, domain_len, domain_list_len = 0;
-			char *tok_start, *tok_prev;
-			u8 *domain_list, *domain_ptr;
+	} else if (os_strcmp(buf, "interworking") == 0) {
+		bss->interworking = atoi(pos);
+	} else if (os_strcmp(buf, "access_network_type") == 0) {
+		bss->access_network_type = atoi(pos);
+		if (bss->access_network_type < 0 ||
+		    bss->access_network_type > 15) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid access_network_type",
+				   line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "internet") == 0) {
+		bss->internet = atoi(pos);
+	} else if (os_strcmp(buf, "asra") == 0) {
+		bss->asra = atoi(pos);
+	} else if (os_strcmp(buf, "esr") == 0) {
+		bss->esr = atoi(pos);
+	} else if (os_strcmp(buf, "uesa") == 0) {
+		bss->uesa = atoi(pos);
+	} else if (os_strcmp(buf, "venue_group") == 0) {
+		bss->venue_group = atoi(pos);
+		bss->venue_info_set = 1;
+	} else if (os_strcmp(buf, "venue_type") == 0) {
+		bss->venue_type = atoi(pos);
+		bss->venue_info_set = 1;
+	} else if (os_strcmp(buf, "hessid") == 0) {
+		if (hwaddr_aton(pos, bss->hessid)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid hessid", line);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "roaming_consortium") == 0) {
+		if (parse_roaming_consortium(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "venue_name") == 0) {
+		if (parse_venue_name(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "network_auth_type") == 0) {
+		u8 auth_type;
+		u16 redirect_url_len;
+		if (hexstr2bin(pos, &auth_type, 1)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid network_auth_type '%s'",
+				   line, pos);
+			return 1;
+		}
+		if (auth_type == 0 || auth_type == 2)
+			redirect_url_len = os_strlen(pos + 2);
+		else
+			redirect_url_len = 0;
+		os_free(bss->network_auth_type);
+		bss->network_auth_type = os_malloc(redirect_url_len + 3 + 1);
+		if (bss->network_auth_type == NULL)
+			return 1;
+		*bss->network_auth_type = auth_type;
+		WPA_PUT_LE16(bss->network_auth_type + 1, redirect_url_len);
+		if (redirect_url_len)
+			os_memcpy(bss->network_auth_type + 3, pos + 2,
+				  redirect_url_len);
+		bss->network_auth_type_len = 3 + redirect_url_len;
+	} else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
+		if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid ipaddr_type_availability '%s'",
+				   line, pos);
+			bss->ipaddr_type_configured = 0;
+			return 1;
+		}
+		bss->ipaddr_type_configured = 1;
+	} else if (os_strcmp(buf, "domain_name") == 0) {
+		int j, num_domains, domain_len, domain_list_len = 0;
+		char *tok_start, *tok_prev;
+		u8 *domain_list, *domain_ptr;
 
-			domain_list_len = os_strlen(pos) + 1;
-			domain_list = os_malloc(domain_list_len);
-			if (domain_list == NULL) {
-				errors++;
-				return errors;
-			}
+		domain_list_len = os_strlen(pos) + 1;
+		domain_list = os_malloc(domain_list_len);
+		if (domain_list == NULL)
+			return 1;
 
-			domain_ptr = domain_list;
-			tok_prev = pos;
-			num_domains = 1;
-			while ((tok_prev = os_strchr(tok_prev, ','))) {
-				num_domains++;
-				tok_prev++;
+		domain_ptr = domain_list;
+		tok_prev = pos;
+		num_domains = 1;
+		while ((tok_prev = os_strchr(tok_prev, ','))) {
+			num_domains++;
+			tok_prev++;
+		}
+		tok_prev = pos;
+		for (j = 0; j < num_domains; j++) {
+			tok_start = os_strchr(tok_prev, ',');
+			if (tok_start) {
+				domain_len = tok_start - tok_prev;
+				*domain_ptr = domain_len;
+				os_memcpy(domain_ptr + 1, tok_prev, domain_len);
+				domain_ptr += domain_len + 1;
+				tok_prev = ++tok_start;
+			} else {
+				domain_len = os_strlen(tok_prev);
+				*domain_ptr = domain_len;
+				os_memcpy(domain_ptr + 1, tok_prev, domain_len);
+				domain_ptr += domain_len + 1;
 			}
-			tok_prev = pos;
-			for (j = 0; j < num_domains; j++) {
-				tok_start = os_strchr(tok_prev, ',');
-				if (tok_start) {
-					domain_len = tok_start - tok_prev;
-					*domain_ptr = domain_len;
-					os_memcpy(domain_ptr + 1, tok_prev,
-						  domain_len);
-					domain_ptr += domain_len + 1;
-					tok_prev = ++tok_start;
-				} else {
-					domain_len = os_strlen(tok_prev);
-					*domain_ptr = domain_len;
-					os_memcpy(domain_ptr + 1, tok_prev,
-						  domain_len);
-					domain_ptr += domain_len + 1;
-				}
-			}
+		}
 
-			os_free(bss->domain_name);
-			bss->domain_name = domain_list;
-			bss->domain_name_len = domain_list_len;
-		} else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
-			if (parse_3gpp_cell_net(bss, pos, line) < 0)
-				errors++;
-		} else if (os_strcmp(buf, "nai_realm") == 0) {
-			if (parse_nai_realm(bss, pos, line) < 0)
-				errors++;
-		} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
-			bss->gas_frag_limit = atoi(pos);
-		} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
-			bss->gas_comeback_delay = atoi(pos);
+		os_free(bss->domain_name);
+		bss->domain_name = domain_list;
+		bss->domain_name_len = domain_list_len;
+	} else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
+		if (parse_3gpp_cell_net(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "nai_realm") == 0) {
+		if (parse_nai_realm(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
+		bss->gas_frag_limit = atoi(pos);
+	} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+		bss->gas_comeback_delay = atoi(pos);
+	} else if (os_strcmp(buf, "qos_map_set") == 0) {
+		if (parse_qos_map_set(bss, pos, line) < 0)
+			return 1;
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_RADIUS_TEST
-		} else if (os_strcmp(buf, "dump_msk_file") == 0) {
-			os_free(bss->dump_msk_file);
-			bss->dump_msk_file = os_strdup(pos);
+	} else if (os_strcmp(buf, "dump_msk_file") == 0) {
+		os_free(bss->dump_msk_file);
+		bss->dump_msk_file = os_strdup(pos);
 #endif /* CONFIG_RADIUS_TEST */
 #ifdef CONFIG_HS20
-		} else if (os_strcmp(buf, "hs20") == 0) {
-			bss->hs20 = atoi(pos);
-		} else if (os_strcmp(buf, "disable_dgaf") == 0) {
-			bss->disable_dgaf = atoi(pos);
-		} else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
-			if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
-				errors++;
-		} else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
-			if (hs20_parse_wan_metrics(bss, pos, line) < 0) {
-				errors++;
-				return errors;
-			}
-		} else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
-			if (hs20_parse_conn_capab(bss, pos, line) < 0) {
-				errors++;
-				return errors;
-			}
-		} else if (os_strcmp(buf, "hs20_operating_class") == 0) {
-			u8 *oper_class;
-			size_t oper_class_len;
-			oper_class_len = os_strlen(pos);
-			if (oper_class_len < 2 || (oper_class_len & 0x01)) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "hs20_operating_class '%s'",
-					   line, pos);
-				errors++;
-				return errors;
-			}
-			oper_class_len /= 2;
-			oper_class = os_malloc(oper_class_len);
-			if (oper_class == NULL) {
-				errors++;
-				return errors;
-			}
-			if (hexstr2bin(pos, oper_class, oper_class_len)) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "hs20_operating_class '%s'",
-					   line, pos);
-				os_free(oper_class);
-				errors++;
-				return errors;
-			}
-			os_free(bss->hs20_operating_class);
-			bss->hs20_operating_class = oper_class;
-			bss->hs20_operating_class_len = oper_class_len;
+	} else if (os_strcmp(buf, "hs20") == 0) {
+		bss->hs20 = atoi(pos);
+	} else if (os_strcmp(buf, "disable_dgaf") == 0) {
+		bss->disable_dgaf = atoi(pos);
+	} else if (os_strcmp(buf, "osen") == 0) {
+		bss->osen = atoi(pos);
+	} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
+		bss->anqp_domain_id = atoi(pos);
+	} else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
+		bss->hs20_deauth_req_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
+		if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
+		if (hs20_parse_wan_metrics(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
+		if (hs20_parse_conn_capab(bss, pos, line) < 0) {
+			return 1;
+		}
+	} else if (os_strcmp(buf, "hs20_operating_class") == 0) {
+		u8 *oper_class;
+		size_t oper_class_len;
+		oper_class_len = os_strlen(pos);
+		if (oper_class_len < 2 || (oper_class_len & 0x01)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid hs20_operating_class '%s'",
+				   line, pos);
+			return 1;
+		}
+		oper_class_len /= 2;
+		oper_class = os_malloc(oper_class_len);
+		if (oper_class == NULL)
+			return 1;
+		if (hexstr2bin(pos, oper_class, oper_class_len)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid hs20_operating_class '%s'",
+				   line, pos);
+			os_free(oper_class);
+			return 1;
+		}
+		os_free(bss->hs20_operating_class);
+		bss->hs20_operating_class = oper_class;
+		bss->hs20_operating_class_len = oper_class_len;
+	} else if (os_strcmp(buf, "hs20_icon") == 0) {
+		if (hs20_parse_icon(bss, pos) < 0) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "osu_ssid") == 0) {
+		if (hs20_parse_osu_ssid(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "osu_server_uri") == 0) {
+		if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "osu_friendly_name") == 0) {
+		if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "osu_nai") == 0) {
+		if (hs20_parse_osu_nai(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "osu_method_list") == 0) {
+		if (hs20_parse_osu_method_list(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "osu_icon") == 0) {
+		if (hs20_parse_osu_icon(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "osu_service_desc") == 0) {
+		if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
+		os_free(bss->subscr_remediation_url);
+		bss->subscr_remediation_url = os_strdup(pos);
+	} else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
+		bss->subscr_remediation_method = atoi(pos);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_TESTING_OPTIONS
-#define PARSE_TEST_PROBABILITY(_val)					\
-		} else if (os_strcmp(buf, #_val) == 0) {		\
-			char *end;					\
-									\
-			conf->_val = strtod(pos, &end);			\
-			if (*end || conf->_val < 0.0d ||		\
-			    conf->_val > 1.0d) {			\
-				wpa_printf(MSG_ERROR,			\
-					   "Line %d: Invalid value '%s'", \
-					   line, pos);			\
-				errors++;				\
-				return errors;				\
-			}
-		PARSE_TEST_PROBABILITY(ignore_probe_probability)
-		PARSE_TEST_PROBABILITY(ignore_auth_probability)
-		PARSE_TEST_PROBABILITY(ignore_assoc_probability)
-		PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
-		PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
-#endif /* CONFIG_TESTING_OPTIONS */
-		} else if (os_strcmp(buf, "vendor_elements") == 0) {
-			struct wpabuf *elems;
-			size_t len = os_strlen(pos);
-			if (len & 0x01) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "vendor_elements '%s'", line, pos);
-				return 1;
-			}
-			len /= 2;
-			if (len == 0) {
-				wpabuf_free(bss->vendor_elements);
-				bss->vendor_elements = NULL;
-				return 0;
-			}
-
-			elems = wpabuf_alloc(len);
-			if (elems == NULL)
-				return 1;
-
-			if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
-				wpabuf_free(elems);
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "vendor_elements '%s'", line, pos);
-				return 1;
-			}
-
-			wpabuf_free(bss->vendor_elements);
-			bss->vendor_elements = elems;
-		} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
-			bss->sae_anti_clogging_threshold = atoi(pos);
-		} else if (os_strcmp(buf, "sae_groups") == 0) {
-			if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "sae_groups value '%s'", line, pos);
-				return 1;
-			}
-		} else {
-			wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
-				   "item '%s'", line, buf);
-			errors++;
+#define PARSE_TEST_PROBABILITY(_val)				\
+	} else if (os_strcmp(buf, #_val) == 0) {		\
+		char *end;					\
+								\
+		conf->_val = strtod(pos, &end);			\
+		if (*end || conf->_val < 0.0 ||			\
+		    conf->_val > 1.0) {				\
+			wpa_printf(MSG_ERROR,			\
+				   "Line %d: Invalid value '%s'", \
+				   line, pos);			\
+			return 1;				\
 		}
-	}
+	PARSE_TEST_PROBABILITY(ignore_probe_probability)
+	PARSE_TEST_PROBABILITY(ignore_auth_probability)
+	PARSE_TEST_PROBABILITY(ignore_assoc_probability)
+	PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
+	PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
+	} else if (os_strcmp(buf, "bss_load_test") == 0) {
+		WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
+		pos = os_strchr(pos, ':');
+		if (pos == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
+				   line);
+			return 1;
+		}
+		pos++;
+		bss->bss_load_test[2] = atoi(pos);
+		pos = os_strchr(pos, ':');
+		if (pos == NULL) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
+				   line);
+			return 1;
+		}
+		pos++;
+		WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
+		bss->bss_load_test_set = 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+	} else if (os_strcmp(buf, "vendor_elements") == 0) {
+		struct wpabuf *elems;
+		size_t len = os_strlen(pos);
+		if (len & 0x01) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid vendor_elements '%s'",
+				   line, pos);
+			return 1;
+		}
+		len /= 2;
+		if (len == 0) {
+			wpabuf_free(bss->vendor_elements);
+			bss->vendor_elements = NULL;
+			return 0;
+		}
 
-	return errors;
-}
+		elems = wpabuf_alloc(len);
+		if (elems == NULL)
+			return 1;
 
+		if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+			wpabuf_free(elems);
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid vendor_elements '%s'",
+				   line, pos);
+			return 1;
+		}
 
-static void hostapd_set_security_params(struct hostapd_bss_config *bss)
-{
-	if (bss->individual_wep_key_len == 0) {
-		/* individual keys are not use; can use key idx0 for
-		 * broadcast keys */
-		bss->broadcast_key_idx_min = 0;
-	}
-
-	if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
-		bss->rsn_pairwise = bss->wpa_pairwise;
-	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
-						    bss->rsn_pairwise);
-
-	bss->radius->auth_server = bss->radius->auth_servers;
-	bss->radius->acct_server = bss->radius->acct_servers;
-
-	if (bss->wpa && bss->ieee802_1x) {
-		bss->ssid.security_policy = SECURITY_WPA;
-	} else if (bss->wpa) {
-		bss->ssid.security_policy = SECURITY_WPA_PSK;
-	} else if (bss->ieee802_1x) {
-		int cipher = WPA_CIPHER_NONE;
-		bss->ssid.security_policy = SECURITY_IEEE_802_1X;
-		bss->ssid.wep.default_len = bss->default_wep_key_len;
-		if (bss->default_wep_key_len)
-			cipher = bss->default_wep_key_len >= 13 ?
-				WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
-		bss->wpa_group = cipher;
-		bss->wpa_pairwise = cipher;
-		bss->rsn_pairwise = cipher;
-	} else if (bss->ssid.wep.keys_set) {
-		int cipher = WPA_CIPHER_WEP40;
-		if (bss->ssid.wep.len[0] >= 13)
-			cipher = WPA_CIPHER_WEP104;
-		bss->ssid.security_policy = SECURITY_STATIC_WEP;
-		bss->wpa_group = cipher;
-		bss->wpa_pairwise = cipher;
-		bss->rsn_pairwise = cipher;
+		wpabuf_free(bss->vendor_elements);
+		bss->vendor_elements = elems;
+	} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
+		bss->sae_anti_clogging_threshold = atoi(pos);
+	} else if (os_strcmp(buf, "sae_groups") == 0) {
+		if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid sae_groups value '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
+		int val = atoi(pos);
+		if (val < 0 || val > 255) {
+			wpa_printf(MSG_ERROR, "Line %d: Invalid local_pwr_constraint %d (expected 0..255)",
+				   line, val);
+			return 1;
+		}
+		conf->local_pwr_constraint = val;
+	} else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) {
+		conf->spectrum_mgmt_required = atoi(pos);
+	} else if (os_strcmp(buf, "wowlan_triggers") == 0) {
+		os_free(bss->wowlan_triggers);
+		bss->wowlan_triggers = os_strdup(pos);
 	} else {
-		bss->ssid.security_policy = SECURITY_PLAINTEXT;
-		bss->wpa_group = WPA_CIPHER_NONE;
-		bss->wpa_pairwise = WPA_CIPHER_NONE;
-		bss->rsn_pairwise = WPA_CIPHER_NONE;
+		wpa_printf(MSG_ERROR,
+			   "Line %d: unknown configuration item '%s'",
+			   line, buf);
+		return 1;
 	}
+
+	return 0;
 }
 
 
@@ -3079,7 +3207,7 @@
 		return NULL;
 	}
 
-	bss = conf->last_bss = conf->bss;
+	bss = conf->last_bss = conf->bss[0];
 
 	while (fgets(buf, sizeof(buf), f)) {
 		bss = conf->last_bss;
@@ -3113,9 +3241,9 @@
 	fclose(f);
 
 	for (i = 0; i < conf->num_bss; i++)
-		hostapd_set_security_params(&conf->bss[i]);
+		hostapd_set_security_params(conf->bss[i], 1);
 
-	if (hostapd_config_check(conf))
+	if (hostapd_config_check(conf, 1))
 		errors++;
 
 #ifndef WPA_IGNORE_CONFIG_ERRORS
@@ -3145,9 +3273,9 @@
 	}
 
 	for (i = 0; i < conf->num_bss; i++)
-		hostapd_set_security_params(&conf->bss[i]);
+		hostapd_set_security_params(conf->bss[i], 0);
 
-	if (hostapd_config_check(conf)) {
+	if (hostapd_config_check(conf, 0)) {
 		wpa_printf(MSG_ERROR, "Configuration check failed");
 		return -1;
 	}
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index be941c4..9ce7829 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / UNIX domain socket -based control interface
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -20,6 +20,7 @@
 #include "common/ieee802_11_defs.h"
 #include "drivers/driver.h"
 #include "radius/radius_client.h"
+#include "radius/radius_server.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ieee802_1x.h"
@@ -29,6 +30,8 @@
 #include "ap/wps_hostapd.h"
 #include "ap/ctrl_iface_ap.h"
 #include "ap/ap_drv_ops.h"
+#include "ap/hs20.h"
+#include "ap/wnm_ap.h"
 #include "ap/wpa_auth.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
@@ -82,15 +85,15 @@
 		    os_memcmp(from->sun_path, dst->addr.sun_path,
 			      fromlen - offsetof(struct sockaddr_un, sun_path))
 		    == 0) {
+			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
+				    (u8 *) from->sun_path,
+				    fromlen -
+				    offsetof(struct sockaddr_un, sun_path));
 			if (prev == NULL)
 				hapd->ctrl_dst = dst->next;
 			else
 				prev->next = dst->next;
 			os_free(dst);
-			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
-				    (u8 *) from->sun_path,
-				    fromlen -
-				    offsetof(struct sockaddr_un, sun_path));
 			return 0;
 		}
 		prev = dst;
@@ -397,13 +400,70 @@
 static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
 						  char *cmd)
 {
-	/*
-	 * Since NFC connection handover provided full WPS Credential, there is
-	 * no need for additional operations within hostapd. Just report this in
-	 * debug log.
-	 */
-	wpa_printf(MSG_DEBUG, "NFC: Connection handover reported: %s", cmd);
-	return 0;
+	size_t len;
+	struct wpabuf *req, *sel;
+	int ret;
+	char *pos, *role, *type, *pos2;
+
+	role = cmd;
+	pos = os_strchr(role, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	type = pos;
+	pos = os_strchr(type, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	pos2 = os_strchr(pos, ' ');
+	if (pos2 == NULL)
+		return -1;
+	*pos2++ = '\0';
+
+	len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+	len /= 2;
+
+	req = wpabuf_alloc(len);
+	if (req == NULL)
+		return -1;
+	if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+		wpabuf_free(req);
+		return -1;
+	}
+
+	len = os_strlen(pos2);
+	if (len & 0x01) {
+		wpabuf_free(req);
+		return -1;
+	}
+	len /= 2;
+
+	sel = wpabuf_alloc(len);
+	if (sel == NULL) {
+		wpabuf_free(req);
+		return -1;
+	}
+	if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+		wpabuf_free(req);
+		wpabuf_free(sel);
+		return -1;
+	}
+
+	if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) {
+		ret = hostapd_wps_nfc_report_handover(hapd, req, sel);
+	} else {
+		wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
+			   "reported: role=%s type=%s", role, type);
+		ret = -1;
+	}
+	wpabuf_free(req);
+	wpabuf_free(sel);
+
+	return ret;
 }
 
 #endif /* CONFIG_WPS_NFC */
@@ -558,6 +618,182 @@
 
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_HS20
+
+static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
+					     const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *url;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+	url = cmd + 17;
+	if (*url == '\0') {
+		url = NULL;
+	} else {
+		if (*url != ' ')
+			return -1;
+		url++;
+		if (*url == '\0')
+			url = NULL;
+	}
+
+	return hs20_send_wnm_notification(hapd, addr, 1, url);
+}
+
+
+static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
+					      const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	int code, reauth_delay, ret;
+	const char *pos;
+	size_t url_len;
+	struct wpabuf *req;
+
+	/* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	code = atoi(pos);
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	reauth_delay = atoi(pos);
+
+	url_len = 0;
+	pos = os_strchr(pos, ' ');
+	if (pos) {
+		pos++;
+		url_len = os_strlen(pos);
+	}
+
+	req = wpabuf_alloc(4 + url_len);
+	if (req == NULL)
+		return -1;
+	wpabuf_put_u8(req, code);
+	wpabuf_put_le16(req, reauth_delay);
+	wpabuf_put_u8(req, url_len);
+	if (pos)
+		wpabuf_put_data(req, pos, url_len);
+
+	wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
+		   " to indicate imminent deauthentication (code=%d "
+		   "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
+	ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
+	wpabuf_free(req);
+	return ret;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_INTERWORKING
+
+static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd,
+					      const char *cmd)
+{
+	u8 qos_map_set[16 + 2 * 21], count = 0;
+	const char *pos = cmd;
+	int val, ret;
+
+	for (;;) {
+		if (count == sizeof(qos_map_set)) {
+			wpa_printf(MSG_ERROR, "Too many qos_map_set parameters");
+			return -1;
+		}
+
+		val = atoi(pos);
+		if (val < 0 || val > 255) {
+			wpa_printf(MSG_INFO, "Invalid QoS Map Set");
+			return -1;
+		}
+
+		qos_map_set[count++] = val;
+		pos = os_strchr(pos, ',');
+		if (!pos)
+			break;
+		pos++;
+	}
+
+	if (count < 16 || count & 1) {
+		wpa_printf(MSG_INFO, "Invalid QoS Map Set");
+		return -1;
+	}
+
+	ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count);
+	if (ret) {
+		wpa_printf(MSG_INFO, "Failed to set QoS Map Set");
+		return -1;
+	}
+
+	os_memcpy(hapd->conf->qos_map_set, qos_map_set, count);
+	hapd->conf->qos_map_set_len = count;
+
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
+						const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	struct wpabuf *buf;
+	u8 *qos_map_set = hapd->conf->qos_map_set;
+	u8 qos_map_set_len = hapd->conf->qos_map_set_len;
+	int ret;
+
+	if (!qos_map_set_len) {
+		wpa_printf(MSG_INFO, "QoS Map Set is not set");
+		return -1;
+	}
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
+			   "for QoS Map Configuration message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	if (!sta->qos_map_enabled) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate "
+			   "support for QoS Map", MAC2STR(addr));
+		return -1;
+	}
+
+	buf = wpabuf_alloc(2 + 2 + qos_map_set_len);
+	if (buf == NULL)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_QOS);
+	wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG);
+
+	/* QoS Map Set Element */
+	wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET);
+	wpabuf_put_u8(buf, qos_map_set_len);
+	wpabuf_put_data(buf, qos_map_set, qos_map_set_len);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+#endif /* CONFIG_INTERWORKING */
+
 
 #ifdef CONFIG_WNM
 
@@ -565,9 +801,8 @@
 						const char *cmd)
 {
 	u8 addr[ETH_ALEN];
-	u8 buf[1000], *pos;
-	struct ieee80211_mgmt *mgmt;
 	int disassoc_timer;
+	struct sta_info *sta;
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -575,31 +810,15 @@
 		return -1;
 	disassoc_timer = atoi(cmd + 17);
 
-	os_memset(buf, 0, sizeof(buf));
-	mgmt = (struct ieee80211_mgmt *) buf;
-	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_ACTION);
-	os_memcpy(mgmt->da, addr, ETH_ALEN);
-	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
-	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
-	mgmt->u.action.category = WLAN_ACTION_WNM;
-	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
-	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
-	mgmt->u.action.u.bss_tm_req.req_mode =
-		WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
-	mgmt->u.action.u.bss_tm_req.disassoc_timer =
-		host_to_le16(disassoc_timer);
-	mgmt->u.action.u.bss_tm_req.validity_interval = 0;
-
-	pos = mgmt->u.action.u.bss_tm_req.variable;
-
-	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
-		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
-			   "Management Request frame");
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for disassociation imminent message",
+			   MAC2STR(addr));
 		return -1;
 	}
 
-	return 0;
+	return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
 }
 
 
@@ -608,14 +827,20 @@
 {
 	u8 addr[ETH_ALEN];
 	const char *url, *timerstr;
-	u8 buf[1000], *pos;
-	struct ieee80211_mgmt *mgmt;
-	size_t url_len;
 	int disassoc_timer;
+	struct sta_info *sta;
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
 
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for ESS disassociation imminent message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
 	timerstr = cmd + 17;
 	if (*timerstr != ' ')
 		return -1;
@@ -628,75 +853,8 @@
 	if (url == NULL)
 		return -1;
 	url++;
-	url_len = os_strlen(url);
-	if (url_len > 255)
-		return -1;
 
-	os_memset(buf, 0, sizeof(buf));
-	mgmt = (struct ieee80211_mgmt *) buf;
-	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_ACTION);
-	os_memcpy(mgmt->da, addr, ETH_ALEN);
-	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
-	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
-	mgmt->u.action.category = WLAN_ACTION_WNM;
-	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
-	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
-	mgmt->u.action.u.bss_tm_req.req_mode =
-		WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
-	mgmt->u.action.u.bss_tm_req.disassoc_timer =
-		host_to_le16(disassoc_timer);
-	mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
-
-	pos = mgmt->u.action.u.bss_tm_req.variable;
-
-	/* Session Information URL */
-	*pos++ = url_len;
-	os_memcpy(pos, url, url_len);
-	pos += url_len;
-
-	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
-		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
-			   "Management Request frame");
-		return -1;
-	}
-
-	/* send disassociation frame after time-out */
-	if (disassoc_timer) {
-		struct sta_info *sta;
-		int timeout, beacon_int;
-
-		/*
-		 * Prevent STA from reconnecting using cached PMKSA to force
-		 * full authentication with the authentication server (which may
-		 * decide to reject the connection),
-		 */
-		wpa_auth_pmksa_remove(hapd->wpa_auth, addr);
-
-		sta = ap_get_sta(hapd, addr);
-		if (sta == NULL) {
-			wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
-				   "for ESS disassociation imminent message",
-				   MAC2STR(addr));
-			return -1;
-		}
-
-		beacon_int = hapd->iconf->beacon_int;
-		if (beacon_int < 1)
-			beacon_int = 100; /* best guess */
-		/* Calculate timeout in ms based on beacon_int in TU */
-		timeout = disassoc_timer * beacon_int * 128 / 125;
-		wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
-			   " set to %d ms", MAC2STR(addr), timeout);
-
-		sta->timeout_next = STA_DISASSOC_FROM_CLI;
-		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-		eloop_register_timeout(timeout / 1000,
-				       timeout % 1000 * 1000,
-				       ap_handle_timer, hapd, sta);
-	}
-
-	return 0;
+	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
 }
 
 #endif /* CONFIG_WNM */
@@ -782,6 +940,14 @@
 				return pos - buf;
 			pos += ret;
 		}
+#ifdef CONFIG_SAE
+		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+			ret = os_snprintf(pos, end - pos, "FT-SAE ");
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
+#endif /* CONFIG_SAE */
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
 		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
@@ -797,6 +963,14 @@
 			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 ");
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
+#endif /* CONFIG_SAE */
 
 		ret = os_snprintf(pos, end - pos, "\n");
 		if (ret < 0 || ret >= end - pos)
@@ -884,6 +1058,10 @@
 		wps_testing_dummy_cred = atoi(value);
 		wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
 			   wps_testing_dummy_cred);
+	} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
+		wps_corrupt_pkhash = atoi(value);
+		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+			   wps_corrupt_pkhash);
 #endif /* CONFIG_WPS_TESTING */
 #ifdef CONFIG_INTERWORKING
 	} else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
@@ -893,8 +1071,42 @@
 		else
 			hapd->gas_frag_limit = val;
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
+		hapd->ext_mgmt_frame_handling = atoi(value);
+#endif /* CONFIG_TESTING_OPTIONS */
 	} else {
+		struct sta_info *sta;
+		int vlan_id;
+
 		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
+		if (ret)
+			return ret;
+
+		if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
+			for (sta = hapd->sta_list; sta; sta = sta->next) {
+				if (hostapd_maclist_found(
+					    hapd->conf->deny_mac,
+					    hapd->conf->num_deny_mac, sta->addr,
+					    &vlan_id) &&
+				    (!vlan_id || vlan_id == sta->vlan_id))
+					ap_sta_disconnect(
+						hapd, sta, sta->addr,
+						WLAN_REASON_UNSPECIFIED);
+			}
+		} else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED &&
+			   os_strcasecmp(cmd, "accept_mac_file") == 0) {
+			for (sta = hapd->sta_list; sta; sta = sta->next) {
+				if (!hostapd_maclist_found(
+					    hapd->conf->accept_mac,
+					    hapd->conf->num_accept_mac,
+					    sta->addr, &vlan_id) ||
+				    (vlan_id && vlan_id != sta->vlan_id))
+					ap_sta_disconnect(
+						hapd, sta, sta->addr,
+						WLAN_REASON_UNSPECIFIED);
+			}
+		}
 	}
 
 	return ret;
@@ -949,11 +1161,200 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+
+static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
+{
+	union wpa_event_data data;
+	char *pos, *param;
+	enum wpa_event_type event;
+
+	wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd);
+
+	os_memset(&data, 0, sizeof(data));
+
+	param = os_strchr(cmd, ' ');
+	if (param == NULL)
+		return -1;
+	*param++ = '\0';
+
+	if (os_strcmp(cmd, "DETECTED") == 0)
+		event = EVENT_DFS_RADAR_DETECTED;
+	else if (os_strcmp(cmd, "CAC-FINISHED") == 0)
+		event = EVENT_DFS_CAC_FINISHED;
+	else if (os_strcmp(cmd, "CAC-ABORTED") == 0)
+		event = EVENT_DFS_CAC_ABORTED;
+	else if (os_strcmp(cmd, "NOP-FINISHED") == 0)
+		event = EVENT_DFS_NOP_FINISHED;
+	else {
+		wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s",
+			   cmd);
+		return -1;
+	}
+
+	pos = os_strstr(param, "freq=");
+	if (pos)
+		data.dfs_event.freq = atoi(pos + 5);
+
+	pos = os_strstr(param, "ht_enabled=1");
+	if (pos)
+		data.dfs_event.ht_enabled = 1;
+
+	pos = os_strstr(param, "chan_offset=");
+	if (pos)
+		data.dfs_event.chan_offset = atoi(pos + 12);
+
+	pos = os_strstr(param, "chan_width=");
+	if (pos)
+		data.dfs_event.chan_width = atoi(pos + 11);
+
+	pos = os_strstr(param, "cf1=");
+	if (pos)
+		data.dfs_event.cf1 = atoi(pos + 4);
+
+	pos = os_strstr(param, "cf2=");
+	if (pos)
+		data.dfs_event.cf2 = atoi(pos + 4);
+
+	wpa_supplicant_event(hapd, event, &data);
+
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
+{
+	size_t len;
+	u8 *buf;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
+
+	len = os_strlen(cmd);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(cmd, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	res = hostapd_drv_send_mlme(hapd, buf, len, 0);
+	os_free(buf);
+	return res;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+					  char *pos)
+{
+#ifdef NEED_AP_MLME
+	struct csa_settings settings;
+	int ret;
+	unsigned int i;
+
+	ret = hostapd_parse_csa_settings(pos, &settings);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		ret = hostapd_switch_channel(iface->bss[i], &settings);
+		if (ret) {
+			/* FIX: What do we do if CSA fails in the middle of
+			 * submitting multi-BSS CSA requests? */
+			return ret;
+		}
+	}
+
+	return 0;
+#else /* NEED_AP_MLME */
+	return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
+static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
+				  int reply_size, const char *param)
+{
+#ifdef RADIUS_SERVER
+	if (os_strcmp(param, "radius_server") == 0) {
+		return radius_server_get_mib(hapd->radius_srv, reply,
+					     reply_size);
+	}
+#endif /* RADIUS_SERVER */
+	return -1;
+}
+
+
+static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
+				     char *buf, size_t buflen)
+{
+	int ret;
+	char *pos;
+	u8 *data = NULL;
+	unsigned int vendor_id, subcmd;
+	struct wpabuf *reply;
+	size_t data_len = 0;
+
+	/* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+	vendor_id = strtoul(cmd, &pos, 16);
+	if (!isblank(*pos))
+		return -EINVAL;
+
+	subcmd = strtoul(pos, &pos, 10);
+
+	if (*pos != '\0') {
+		if (!isblank(*pos++))
+			return -EINVAL;
+		data_len = os_strlen(pos);
+	}
+
+	if (data_len) {
+		data_len /= 2;
+		data = os_malloc(data_len);
+		if (!data)
+			return -ENOBUFS;
+
+		if (hexstr2bin(pos, data, data_len)) {
+			wpa_printf(MSG_DEBUG,
+				   "Vendor command: wrong parameter format");
+			os_free(data);
+			return -EINVAL;
+		}
+	}
+
+	reply = wpabuf_alloc((buflen - 1) / 2);
+	if (!reply) {
+		os_free(data);
+		return -ENOBUFS;
+	}
+
+	ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
+				     reply);
+
+	if (ret == 0)
+		ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+				       wpabuf_len(reply));
+
+	wpabuf_free(reply);
+	os_free(data);
+
+	return ret;
+}
+
+
 static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
 				       void *sock_ctx)
 {
 	struct hostapd_data *hapd = eloop_ctx;
-	char buf[256];
+	char buf[4096];
 	int res;
 	struct sockaddr_un from;
 	socklen_t fromlen = sizeof(from);
@@ -989,6 +1390,11 @@
 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
 		if (wpa_debug_reopen_file() < 0)
 			reply_len = -1;
+	} else if (os_strcmp(buf, "STATUS") == 0) {
+		reply_len = hostapd_ctrl_iface_status(hapd, reply,
+						      reply_size);
+	} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
+		reply_len = hostapd_drv_status(hapd, reply, reply_size);
 	} else if (os_strcmp(buf, "MIB") == 0) {
 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
 		if (reply_len >= 0) {
@@ -1018,6 +1424,9 @@
 				reply_len += res;
 		}
 #endif /* CONFIG_NO_RADIUS */
+	} else if (os_strncmp(buf, "MIB ", 4) == 0) {
+		reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size,
+						   buf + 4);
 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
 		reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
 							 reply_size);
@@ -1093,6 +1502,22 @@
 			reply_len = -1;
 #endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_INTERWORKING
+	} else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) {
+		if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) {
+		if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18))
+			reply_len = -1;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	} else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
+		if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
+		if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
+			reply_len = -1;
+#endif /* CONFIG_HS20 */
 #ifdef CONFIG_WNM
 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
 		if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
@@ -1119,6 +1544,21 @@
 	} else if (os_strncmp(buf, "DISABLE", 7) == 0) {
 		if (hostapd_ctrl_iface_disable(hapd->iface))
 			reply_len = -1;
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strncmp(buf, "RADAR ", 6) == 0) {
+		if (hostapd_ctrl_iface_radar(hapd, buf + 6))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
+		if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
+			reply_len = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
+	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
+		reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
+						      reply_size);
+
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -1367,6 +1807,16 @@
 }
 
 
+static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_WPS_TESTING
+	wps_version_number = 0x20;
+	wps_testing_dummy_cred = 0;
+	wps_corrupt_pkhash = 0;
+#endif /* CONFIG_WPS_TESTING */
+}
+
+
 static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 					      void *sock_ctx)
 {
@@ -1385,6 +1835,7 @@
 		return;
 	}
 	buf[res] = '\0';
+	wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
 
 	os_memcpy(reply, "OK\n", 3);
 	reply_len = 3;
@@ -1392,12 +1843,23 @@
 	if (os_strcmp(buf, "PING") == 0) {
 		os_memcpy(reply, "PONG\n", 5);
 		reply_len = 5;
+	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
+		if (wpa_debug_reopen_file() < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "FLUSH") == 0) {
+		hostapd_ctrl_iface_flush(interfaces);
 	} else if (os_strncmp(buf, "ADD ", 4) == 0) {
 		if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
 			reply_len = -1;
 	} else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
 		if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
 			reply_len = -1;
+#ifdef CONFIG_MODULE_TESTS
+	} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+		int hapd_module_tests(void);
+		if (hapd_module_tests() < 0)
+			reply_len = -1;
+#endif /* CONFIG_MODULE_TESTS */
 	} else {
 		wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
 			   "ignored");
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 2dd6fc8..5b74b64 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -22,6 +22,19 @@
 # Driver interface for drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# driver_nl80211.c requires libnl. If you are compiling it yourself
+# you may need to point hostapd to your version of libnl.
+#
+#CFLAGS += -I$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
+
+# Use libnl v2.0 (or 3.0) libraries.
+#CONFIG_LIBNL20=y
+
+# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
+#CONFIG_LIBNL32=y
+
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -42,10 +55,7 @@
 CONFIG_PEERKEY=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
+CONFIG_IEEE80211W=y
 
 # Integrated EAP server
 CONFIG_EAP=y
@@ -96,16 +106,13 @@
 #CONFIG_EAP_GPSK_SHA256=y
 
 # EAP-FAST for the integrated EAP server
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch)
-# to add the needed functions.
+# 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
 
 # Wi-Fi Protected Setup (WPS)
 #CONFIG_WPS=y
-# Enable WSC 2.0 support
-#CONFIG_WPS2=y
 # Enable UPnP support for external WPS Registrars
 #CONFIG_WPS_UPNP=y
 # Enable WPS support with NFC config method
@@ -157,6 +164,12 @@
 # Disabled by default.
 #CONFIG_DEBUG_FILE=y
 
+# Add support for sending all debug messages (regardless of debug verbosity)
+# to the Linux kernel tracing facility. This helps debug the entire stack by
+# making it easy to record everything happening from the driver up into the
+# same file, e.g., using trace-cmd.
+#CONFIG_DEBUG_LINUX_TRACING=y
+
 # Remove support for RADIUS accounting
 #CONFIG_NO_ACCOUNTING=y
 
@@ -174,7 +187,7 @@
 # Note: This requires libnl 3.1 or newer.
 #CONFIG_VLAN_NETLINK=y
 
-# Remove support for dumping state into a file on SIGUSR1 signal
+# Remove support for dumping internal state through control interface commands
 # This can be used to reduce binary size at the cost of disabling a debugging
 # option.
 #CONFIG_NO_DUMP_STATE=y
diff --git a/hostapd/dump_state.c b/hostapd/dump_state.c
deleted file mode 100644
index fcd9890..0000000
--- a/hostapd/dump_state.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * hostapd / State dump
- * Copyright (c) 2002-2009, 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 <time.h>
-
-#include "utils/common.h"
-#include "radius/radius_client.h"
-#include "radius/radius_server.h"
-#include "eapol_auth/eapol_auth_sm.h"
-#include "eapol_auth/eapol_auth_sm_i.h"
-#include "eap_server/eap.h"
-#include "ap/hostapd.h"
-#include "ap/ap_config.h"
-#include "ap/sta_info.h"
-#include "dump_state.h"
-#include "ap/ap_drv_ops.h"
-
-
-static void fprint_char(FILE *f, char c)
-{
-	if (c >= 32 && c < 127)
-		fprintf(f, "%c", c);
-	else
-		fprintf(f, "<%02x>", c);
-}
-
-
-static void ieee802_1x_dump_state(FILE *f, const char *prefix,
-				  struct sta_info *sta)
-{
-	struct eapol_state_machine *sm = sta->eapol_sm;
-	if (sm == NULL)
-		return;
-
-	fprintf(f, "%sIEEE 802.1X:\n", prefix);
-
-	if (sm->identity) {
-		size_t i;
-		fprintf(f, "%sidentity=", prefix);
-		for (i = 0; i < sm->identity_len; i++)
-			fprint_char(f, sm->identity[i]);
-		fprintf(f, "\n");
-	}
-
-	fprintf(f, "%slast EAP type: Authentication Server: %d (%s) "
-		"Supplicant: %d (%s)\n", prefix,
-		sm->eap_type_authsrv,
-		eap_server_get_name(0, sm->eap_type_authsrv),
-		sm->eap_type_supp, eap_server_get_name(0, sm->eap_type_supp));
-
-	fprintf(f, "%scached_packets=%s\n", prefix,
-		sm->last_recv_radius ? "[RX RADIUS]" : "");
-
-	eapol_auth_dump_state(f, prefix, sm);
-}
-
-
-/**
- * hostapd_dump_state - SIGUSR1 handler to dump hostapd state to a text file
- */
-static void hostapd_dump_state(struct hostapd_data *hapd)
-{
-	FILE *f;
-	time_t now;
-	struct sta_info *sta;
-	int i;
-#ifndef CONFIG_NO_RADIUS
-	char *buf;
-#endif /* CONFIG_NO_RADIUS */
-	struct hostap_sta_driver_data data;
-
-	if (!hapd->conf->dump_log_name) {
-		wpa_printf(MSG_DEBUG, "Dump file not defined - ignoring dump "
-			   "request");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "Dumping hostapd state to '%s'",
-		   hapd->conf->dump_log_name);
-	f = fopen(hapd->conf->dump_log_name, "w");
-	if (f == NULL) {
-		wpa_printf(MSG_WARNING, "Could not open dump file '%s' for "
-			   "writing.", hapd->conf->dump_log_name);
-		return;
-	}
-
-	time(&now);
-	fprintf(f, "hostapd state dump - %s", ctime(&now));
-	fprintf(f, "num_sta=%d num_sta_non_erp=%d "
-		"num_sta_no_short_slot_time=%d\n"
-		"num_sta_no_short_preamble=%d\n",
-		hapd->num_sta, hapd->iface->num_sta_non_erp,
-		hapd->iface->num_sta_no_short_slot_time,
-		hapd->iface->num_sta_no_short_preamble);
-
-	for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
-		fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));
-
-		fprintf(f,
-			"  AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
-			"\n"
-			"  capability=0x%x listen_interval=%d\n",
-			sta->aid,
-			sta->flags,
-			(sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
-			(sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
-			(sta->flags & WLAN_STA_PS ? "[PS]" : ""),
-			(sta->flags & WLAN_STA_TIM ? "[TIM]" : ""),
-			(sta->flags & WLAN_STA_PERM ? "[PERM]" : ""),
-			(ap_sta_is_authorized(sta) ? "[AUTHORIZED]" : ""),
-			(sta->flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
-			 ""),
-			(sta->flags & WLAN_STA_SHORT_PREAMBLE ?
-			 "[SHORT_PREAMBLE]" : ""),
-			(sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
-			(sta->flags & WLAN_STA_WMM ? "[WMM]" : ""),
-			(sta->flags & WLAN_STA_MFP ? "[MFP]" : ""),
-			(sta->flags & WLAN_STA_WPS ? "[WPS]" : ""),
-			(sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
-			(sta->flags & WLAN_STA_WDS ? "[WDS]" : ""),
-			(sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
-			(sta->flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
-			sta->capability,
-			sta->listen_interval);
-
-		fprintf(f, "  supported_rates=");
-		for (i = 0; i < sta->supported_rates_len; i++)
-			fprintf(f, "%02x ", sta->supported_rates[i]);
-		fprintf(f, "\n");
-
-		fprintf(f,
-			"  timeout_next=%s\n",
-			(sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" :
-			 (sta->timeout_next == STA_DISASSOC ? "DISASSOC" :
-			  "DEAUTH")));
-
-		ieee802_1x_dump_state(f, "  ", sta);
-
-		if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) == 0) {
-			fprintf(f, "  rx_pkt=%lu tx_pkt=%lu\n"
-				"  rx_byte=%lu tx_byte=%lu\n",
-				data.rx_packets, data.tx_packets,
-				data.rx_bytes, data.tx_bytes);
-		}
-	}
-
-#ifndef CONFIG_NO_RADIUS
-	buf = os_malloc(4096);
-	if (buf) {
-		int count = radius_client_get_mib(hapd->radius, buf, 4096);
-		if (count < 0)
-			count = 0;
-		else if (count > 4095)
-			count = 4095;
-		buf[count] = '\0';
-		fprintf(f, "%s", buf);
-
-#ifdef RADIUS_SERVER
-		count = radius_server_get_mib(hapd->radius_srv, buf, 4096);
-		if (count < 0)
-			count = 0;
-		else if (count > 4095)
-			count = 4095;
-		buf[count] = '\0';
-		fprintf(f, "%s", buf);
-#endif /* RADIUS_SERVER */
-
-		os_free(buf);
-	}
-#endif /* CONFIG_NO_RADIUS */
-	fclose(f);
-}
-
-
-int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx)
-{
-	size_t i;
-
-	for (i = 0; i < iface->num_bss; i++)
-		hostapd_dump_state(iface->bss[i]);
-
-	return 0;
-}
diff --git a/hostapd/dump_state.h b/hostapd/dump_state.h
deleted file mode 100644
index a209d65..0000000
--- a/hostapd/dump_state.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * hostapd / State dump
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef DUMP_STATE_H
-#define DUMP_STATE_H
-
-int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx);
-
-#endif /* DUMP_STATE_H */
diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c
index 981e539..8477c21 100644
--- a/hostapd/eap_register.c
+++ b/hostapd/eap_register.c
@@ -44,6 +44,13 @@
 		ret = eap_server_unauth_tls_register();
 #endif /* EAP_SERVER_TLS */
 
+#ifdef EAP_SERVER_TLS
+#ifdef CONFIG_HS20
+	if (ret == 0)
+		ret = eap_server_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_SERVER_TLS */
+
 #ifdef EAP_SERVER_MSCHAPV2
 	if (ret == 0)
 		ret = eap_server_mschapv2_register();
diff --git a/hostapd/hapd_module_tests.c b/hostapd/hapd_module_tests.c
new file mode 100644
index 0000000..f7887eb
--- /dev/null
+++ b/hostapd/hapd_module_tests.c
@@ -0,0 +1,17 @@
+/*
+ * hostapd module tests
+ * Copyright (c) 2014, 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"
+
+int hapd_module_tests(void)
+{
+	wpa_printf(MSG_INFO, "hostapd module tests");
+	return 0;
+}
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index e04e2e9..c041887 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
 /*
  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,6 +18,9 @@
  * SIM-REQ-AUTH <IMSI> <max_chal>
  * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3]
  * SIM-RESP-AUTH <IMSI> FAILURE
+ * GSM-AUTH-REQ <IMSI> RAND1:RAND2[:RAND3]
+ * GSM-AUTH-RESP <IMSI> Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3]
+ * GSM-AUTH-RESP <IMSI> FAILURE
  *
  * EAP-AKA / UMTS query/response:
  * AKA-REQ-AUTH <IMSI>
@@ -30,12 +33,16 @@
  * IMSI and max_chal are sent as an ASCII string,
  * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
  *
- * The example implementation here reads GSM authentication triplets from a
+ * An example implementation here reads GSM authentication triplets from a
  * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex
  * strings. This is used to simulate an HLR/AuC. As such, it is not very useful
  * for real life authentication, but it is useful both as an example
  * implementation and for EAP-SIM/AKA/AKA' testing.
  *
+ * For a stronger example design, Milenage and GSM-Milenage algorithms can be
+ * used to dynamically generate authenticatipn information for EAP-AKA/AKA' and
+ * EAP-SIM, respectively, if Ki is known.
+ *
  * SQN generation follows the not time-based Profile 2 described in
  * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this
  * can be changed with a command line options if needed.
@@ -58,6 +65,7 @@
 static int update_milenage = 0;
 static int sqn_changes = 0;
 static int ind_len = 5;
+static int stdout_debug = 1;
 
 /* GSM triplets */
 struct gsm_triplet {
@@ -214,6 +222,9 @@
 {
 	char cmd[128], val[13], *pos;
 
+	if (sqlite_db == NULL)
+		return 0;
+
 	pos = val;
 	pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6);
 	*pos = '\0';
@@ -611,31 +622,30 @@
 }
 
 
-static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
-			 char *imsi)
+static int sim_req_auth(char *imsi, char *resp, size_t resp_len)
 {
 	int count, max_chal, ret;
 	char *pos;
-	char reply[1000], *rpos, *rend;
+	char *rpos, *rend;
 	struct milenage_parameters *m;
 	struct gsm_triplet *g;
 
-	reply[0] = '\0';
+	resp[0] = '\0';
 
 	pos = strchr(imsi, ' ');
 	if (pos) {
 		*pos++ = '\0';
 		max_chal = atoi(pos);
-		if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
+		if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL)
 			max_chal = EAP_SIM_MAX_CHAL;
 	} else
 		max_chal = EAP_SIM_MAX_CHAL;
 
-	rend = &reply[sizeof(reply)];
-	rpos = reply;
+	rend = resp + resp_len;
+	rpos = resp;
 	ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
 	if (ret < 0 || ret >= rend - rpos)
-		return;
+		return -1;
 	rpos += ret;
 
 	m = get_milenage(imsi);
@@ -643,7 +653,7 @@
 		u8 _rand[16], sres[4], kc[8];
 		for (count = 0; count < max_chal; count++) {
 			if (random_get_bytes(_rand, 16) < 0)
-				return;
+				return -1;
 			gsm_milenage(m->opc, m->ki, _rand, sres, kc);
 			*rpos++ = ' ';
 			rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
@@ -653,7 +663,7 @@
 			rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
 		}
 		*rpos = '\0';
-		goto send;
+		return 0;
 	}
 
 	count = 0;
@@ -677,15 +687,61 @@
 		printf("No GSM triplets found for %s\n", imsi);
 		ret = snprintf(rpos, rend - rpos, " FAILURE");
 		if (ret < 0 || ret >= rend - rpos)
-			return;
+			return -1;
 		rpos += ret;
 	}
 
-send:
-	printf("Send: %s\n", reply);
-	if (sendto(s, reply, rpos - reply, 0,
-		   (struct sockaddr *) from, fromlen) < 0)
-		perror("send");
+	return 0;
+}
+
+
+static int gsm_auth_req(char *imsi, char *resp, size_t resp_len)
+{
+	int count, ret;
+	char *pos, *rpos, *rend;
+	struct milenage_parameters *m;
+
+	resp[0] = '\0';
+
+	pos = os_strchr(imsi, ' ');
+	if (!pos)
+		return -1;
+	*pos++ = '\0';
+
+	rend = resp + resp_len;
+	rpos = resp;
+	ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi);
+	if (ret < 0 || ret >= rend - rpos)
+		return -1;
+	rpos += ret;
+
+	m = get_milenage(imsi);
+	if (m) {
+		u8 _rand[16], sres[4], kc[8];
+		for (count = 0; count < EAP_SIM_MAX_CHAL; count++) {
+			if (hexstr2bin(pos, _rand, 16) != 0)
+				return -1;
+			gsm_milenage(m->opc, m->ki, _rand, sres, kc);
+			*rpos++ = count == 0 ? ' ' : ':';
+			rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
+			*rpos++ = ':';
+			rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
+			pos += 16 * 2;
+			if (*pos != ':')
+				break;
+			pos++;
+		}
+		*rpos = '\0';
+		return 0;
+	}
+
+	printf("No GSM triplets found for %s\n", imsi);
+	ret = os_snprintf(rpos, rend - rpos, " FAILURE");
+	if (ret < 0 || ret >= rend - rpos)
+		return -1;
+	rpos += ret;
+
+	return 0;
 }
 
 
@@ -711,11 +767,10 @@
 }
 
 
-static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
-			 char *imsi)
+static int aka_req_auth(char *imsi, char *resp, size_t resp_len)
 {
 	/* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
-	char reply[1000], *pos, *end;
+	char *pos, *end;
 	u8 _rand[EAP_AKA_RAND_LEN];
 	u8 autn[EAP_AKA_AUTN_LEN];
 	u8 ik[EAP_AKA_IK_LEN];
@@ -729,16 +784,18 @@
 	m = get_milenage(imsi);
 	if (m) {
 		if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
-			return;
+			return -1;
 		res_len = EAP_AKA_RES_MAX_LEN;
 		inc_sqn(m->sqn);
 #ifdef CONFIG_SQLITE
 		db_update_milenage_sqn(m);
 #endif /* CONFIG_SQLITE */
 		sqn_changes = 1;
-		printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
-		       m->sqn[0], m->sqn[1], m->sqn[2],
-		       m->sqn[3], m->sqn[4], m->sqn[5]);
+		if (stdout_debug) {
+			printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
+			       m->sqn[0], m->sqn[1], m->sqn[2],
+			       m->sqn[3], m->sqn[4], m->sqn[5]);
+		}
 		milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
 				  autn, ik, ck, res, &res_len);
 	} else {
@@ -756,18 +813,18 @@
 #endif /* AKA_USE_FIXED_TEST_VALUES */
 	}
 
-	pos = reply;
-	end = &reply[sizeof(reply)];
+	pos = resp;
+	end = resp + resp_len;
 	ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
 	if (ret < 0 || ret >= end - pos)
-		return;
+		return -1;
 	pos += ret;
 	if (failed) {
 		ret = snprintf(pos, end - pos, "FAILURE");
 		if (ret < 0 || ret >= end - pos)
-			return;
+			return -1;
 		pos += ret;
-		goto done;
+		return 0;
 	}
 	pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
 	*pos++ = ' ';
@@ -779,65 +836,87 @@
 	*pos++ = ' ';
 	pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
 
-done:
-	printf("Send: %s\n", reply);
-
-	if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
-		   fromlen) < 0)
-		perror("send");
+	return 0;
 }
 
 
-static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
-		     char *imsi)
+static int aka_auts(char *imsi, char *resp, size_t resp_len)
 {
 	char *auts, *__rand;
 	u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
 	struct milenage_parameters *m;
 
+	resp[0] = '\0';
+
 	/* AKA-AUTS <IMSI> <AUTS> <RAND> */
 
 	auts = strchr(imsi, ' ');
 	if (auts == NULL)
-		return;
+		return -1;
 	*auts++ = '\0';
 
 	__rand = strchr(auts, ' ');
 	if (__rand == NULL)
-		return;
+		return -1;
 	*__rand++ = '\0';
 
-	printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand);
+	if (stdout_debug) {
+		printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n",
+		       imsi, auts, __rand);
+	}
 	if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
 	    hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) {
 		printf("Could not parse AUTS/RAND\n");
-		return;
+		return -1;
 	}
 
 	m = get_milenage(imsi);
 	if (m == NULL) {
 		printf("Unknown IMSI: %s\n", imsi);
-		return;
+		return -1;
 	}
 
 	if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
 		printf("AKA-AUTS: Incorrect MAC-S\n");
 	} else {
 		memcpy(m->sqn, sqn, 6);
-		printf("AKA-AUTS: Re-synchronized: "
-		       "SQN=%02x%02x%02x%02x%02x%02x\n",
-		       sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
+		if (stdout_debug) {
+			printf("AKA-AUTS: Re-synchronized: "
+			       "SQN=%02x%02x%02x%02x%02x%02x\n",
+			       sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
+		}
 #ifdef CONFIG_SQLITE
 		db_update_milenage_sqn(m);
 #endif /* CONFIG_SQLITE */
 		sqn_changes = 1;
 	}
+
+	return 0;
+}
+
+
+static int process_cmd(char *cmd, char *resp, size_t resp_len)
+{
+	if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0)
+		return sim_req_auth(cmd + 13, resp, resp_len);
+
+	if (os_strncmp(cmd, "GSM-AUTH-REQ ", 13) == 0)
+		return gsm_auth_req(cmd + 13, resp, resp_len);
+
+	if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0)
+		return aka_req_auth(cmd + 13, resp, resp_len);
+
+	if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0)
+		return aka_auts(cmd + 9, resp, resp_len);
+
+	printf("Unknown request: %s\n", cmd);
+	return -1;
 }
 
 
 static int process(int s)
 {
-	char buf[1000];
+	char buf[1000], resp[1000];
 	struct sockaddr_un from;
 	socklen_t fromlen;
 	ssize_t res;
@@ -859,14 +938,21 @@
 
 	printf("Received: %s\n", buf);
 
-	if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0)
-		sim_req_auth(s, &from, fromlen, buf + 13);
-	else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0)
-		aka_req_auth(s, &from, fromlen, buf + 13);
-	else if (strncmp(buf, "AKA-AUTS ", 9) == 0)
-		aka_auts(s, &from, fromlen, buf + 9);
-	else
-		printf("Unknown request: %s\n", buf);
+	if (process_cmd(buf, resp, sizeof(resp)) < 0) {
+		printf("Failed to process request\n");
+		return -1;
+	}
+
+	if (resp[0] == '\0') {
+		printf("No response\n");
+		return 0;
+	}
+
+	printf("Send: %s\n", resp);
+
+	if (sendto(s, resp, os_strlen(resp), 0, (struct sockaddr *) &from,
+		   fromlen) < 0)
+		perror("send");
 
 	return 0;
 }
@@ -894,8 +980,10 @@
 		os_free(prev);
 	}
 
-	close(serv_sock);
-	unlink(socket_path);
+	if (serv_sock >= 0)
+		close(serv_sock);
+	if (socket_path)
+		unlink(socket_path);
 
 #ifdef CONFIG_SQLITE
 	if (sqlite_db) {
@@ -917,12 +1005,12 @@
 {
 	printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
 	       "database/authenticator\n"
-	       "Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>\n"
+	       "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>\n"
 	       "\n"
 	       "usage:\n"
 	       "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
 	       "[-m<milenage file>] \\\n"
-	       "        [-D<DB file>] [-i<IND len in bits>]\n"
+	       "        [-D<DB file>] [-i<IND len in bits>] [command]\n"
 	       "\n"
 	       "options:\n"
 	       "  -h = show this usage help\n"
@@ -932,7 +1020,15 @@
 	       "  -g<triplet file> = path for GSM authentication triplets\n"
 	       "  -m<milenage file> = path for Milenage keys\n"
 	       "  -D<DB file> = path to SQLite database\n"
-	       "  -i<IND len in bits> = IND length for SQN (default: 5)\n",
+	       "  -i<IND len in bits> = IND length for SQN (default: 5)\n"
+	       "\n"
+	       "If the optional command argument, like "
+	       "\"AKA-REQ-AUTH <IMSI>\" is used, a single\n"
+	       "command is processed with response sent to stdout. Otherwise, "
+	       "hlr_auc_gw opens\n"
+	       "a control interface and processes commands sent through it "
+	       "(e.g., by EAP server\n"
+	       "in hostapd).\n",
 	       default_socket_path);
 }
 
@@ -942,6 +1038,7 @@
 	int c;
 	char *gsm_triplet_file = NULL;
 	char *sqlite_db_file = NULL;
+	int ret = 0;
 
 	if (os_program_init())
 		return -1;
@@ -1005,18 +1102,31 @@
 	if (milenage_file && read_milenage(milenage_file) < 0)
 		return -1;
 
-	serv_sock = open_socket(socket_path);
-	if (serv_sock < 0)
-		return -1;
+	if (optind == argc) {
+		serv_sock = open_socket(socket_path);
+		if (serv_sock < 0)
+			return -1;
 
-	printf("Listening for requests on %s\n", socket_path);
+		printf("Listening for requests on %s\n", socket_path);
 
-	atexit(cleanup);
-	signal(SIGTERM, handle_term);
-	signal(SIGINT, handle_term);
+		atexit(cleanup);
+		signal(SIGTERM, handle_term);
+		signal(SIGINT, handle_term);
 
-	for (;;)
-		process(serv_sock);
+		for (;;)
+			process(serv_sock);
+	} else {
+		char buf[1000];
+		socket_path = NULL;
+		stdout_debug = 0;
+		if (process_cmd(argv[optind], buf, sizeof(buf)) < 0) {
+			printf("FAIL\n");
+			ret = -1;
+		} else {
+			printf("%s\n", buf);
+		}
+		cleanup();
+	}
 
 #ifdef CONFIG_SQLITE
 	if (sqlite_db) {
@@ -1027,5 +1137,5 @@
 
 	os_program_deinit();
 
-	return 0;
+	return ret;
 }
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 45897ed..a7ab0f6 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -51,9 +51,6 @@
 logger_stdout=-1
 logger_stdout_level=2
 
-# Dump file for state information (on SIGUSR1)
-dump_file=/tmp/hostapd.dump
-
 # Interface for separate control program. If this is specified, hostapd
 # will create this directory and a UNIX domain socket for listening to requests
 # from external programs (CLI/GUI, etc.) for status information and
@@ -111,6 +108,20 @@
 # (default: 0 = disabled)
 #ieee80211h=1
 
+# Add Power Constraint element to Beacon and Probe Response frames
+# This config option adds Power Constraint element when applicable and Country
+# element is added. Power Constraint element is required by Transmit Power
+# Control. This can be used only with ieee80211d=1.
+# Valid values are 0..255.
+#local_pwr_constraint=3
+
+# Set Spectrum Management subfield in the Capability Information field.
+# This config option forces the Spectrum Management bit to be set. When this
+# option is not set, the value of the Spectrum Management bit depends on whether
+# DFS or TPC is required by regulatory authorities. This can be used only with
+# ieee80211d=1 and local_pwr_constraint configured.
+#spectrum_mgmt_required=1
+
 # Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g,
 # ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to
 # specify band)
@@ -143,6 +154,14 @@
 # Defaults:
 #acs_num_scans=5
 
+# Channel list restriction. This option allows hostapd to select one of the
+# provided channels when a channel should be automatically selected. This
+# is currently only used for DFS when the current channels becomes unavailable
+# due to radar interference, and is currently only useful when ieee80211h=1 is
+# set.
+# Default: not set (allow any enabled channel to be selected)
+#chanlist=100 104 108 112 116
+
 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
 beacon_int=100
 
@@ -416,6 +435,12 @@
 # associated stations in the BSS. By default, this bridging is allowed.
 #ap_isolate=1
 
+# Fixed BSS Load value for testing purposes
+# This field can be used to configure hostapd to add a fixed BSS Load element
+# into Beacon and Probe Response frames for testing purposes. The format is
+# <station count>:<channel utilization>:<available admission capacity>
+#bss_load_test=12:80:20000
+
 ##### IEEE 802.11n related configuration ######################################
 
 # ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -428,7 +453,7 @@
 # LDPC coding capability: [LDPC] = supported
 # Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
 #	channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz
-#	with secondary channel below the primary channel
+#	with secondary channel above the primary channel
 #	(20 MHz only if neither is set)
 #	Note: There are limits on which channels can be used with HT40- and
 #	HT40+. Following table shows the channels that may be available for
@@ -455,13 +480,20 @@
 # Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not
 #	set)
 # DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set)
-# PSMP support: [PSMP] (disabled if not set)
+# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
 # L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
 #ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40]
 
 # Require stations to support HT PHY (reject association if they do not)
 #require_ht=1
 
+# If set non-zero, require stations to perform scans of overlapping
+# channels to test for stations which would be affected by 40 MHz traffic.
+# This parameter sets the interval in seconds between these scans. This
+# is useful only for testing that stations properly set the OBSS interval,
+# since the other parameters in the OBSS scan parameters IE are set to 0.
+#obss_interval=0
+
 ##### IEEE 802.11ac related configuration #####################################
 
 # ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled
@@ -961,6 +993,11 @@
 # The UDP port number for the RADIUS authentication server
 #radius_server_auth_port=1812
 
+# The UDP port number for the RADIUS accounting server
+# Commenting this out or setting this to 0 can be used to disable RADIUS
+# accounting while still enabling RADIUS authentication.
+#radius_server_acct_port=1813
+
 # Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
 #radius_server_ipv6=1
 
@@ -1067,6 +1104,17 @@
 # 2 = required
 #ieee80211w=0
 
+# Group management cipher suite
+# Default: AES-128-CMAC (BIP)
+# Other options (depending on driver support):
+# BIP-GMAC-128
+# BIP-GMAC-256
+# BIP-CMAC-256
+# Note: All the stations connecting to the BSS will also need to support the
+# selected cipher. The default AES-128-CMAC is the only option that is commonly
+# available in deployed devices.
+#group_mgmt_cipher=AES-128-CMAC
+
 # Association SA Query maximum timeout (in TU = 1.024 ms; for MFP)
 # (maximum time to wait for a SA Query response)
 # dot11AssociationSAQueryMaximumTimeout, 1...4294967295
@@ -1457,6 +1505,9 @@
 # information to be complete.
 #venue_name=eng:Example venue
 #venue_name=fin:Esimerkkipaikka
+# Alternative format for language:value strings:
+# (double quoted string, printf-escaped string)
+#venue_name=P"eng:Example\nvenue"
 
 # Network Authentication Type
 # This parameter indicates what type of network authentication is used in the
@@ -1508,6 +1559,8 @@
 #	    accordance with IETF RFC 4282
 # NAI Realm(s): Semi-colon delimited NAI Realm(s)
 # EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...]
+# EAP Method types, see:
+# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4
 # AuthParam (Table 8-188 in IEEE Std 802.11-2012):
 # ID 2 = Non-EAP Inner Authentication Type
 #	1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2
@@ -1521,6 +1574,23 @@
 # username/password
 #nai_realm=0,example.org,13[5:6],21[2:4][5:7]
 
+# QoS Map Set configuration
+#
+# Comma delimited QoS Map Set in decimal values
+# (see IEEE Std 802.11-2012, 8.4.2.97)
+#
+# format:
+# [<DSCP Exceptions[DSCP,UP]>,]<UP 0 range[low,high]>,...<UP 7 range[low,high]>
+#
+# There can be up to 21 optional DSCP Exceptions which are pairs of DSCP Value
+# (0..63 or 255) and User Priority (0..7). This is followed by eight DSCP Range
+# descriptions with DSCP Low Value and DSCP High Value pairs (0..63 or 255) for
+# each UP starting from 0. If both low and high value are set to 255, the
+# corresponding UP is not used.
+#
+# default: not set
+#qos_map_set=53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255
+
 ##### Hotspot 2.0 #############################################################
 
 # Enable Hotspot 2.0 support
@@ -1533,6 +1603,21 @@
 # forging such frames to other stations in the BSS.
 #disable_dgaf=1
 
+# OSU Server-Only Authenticated L2 Encryption Network
+#osen=1
+
+# ANQP Domain ID (0..65535)
+# An identifier for a set of APs in an ESS that share the same common ANQP
+# information. 0 = Some of the ANQP information is unique to this AP (default).
+#anqp_domain_id=1234
+
+# Deauthentication request timeout
+# If the RADIUS server indicates that the station is not allowed to connect to
+# the BSS/ESS, the AP can allow the station some time to download a
+# notification page (URL included in the message). This parameter sets that
+# timeout in seconds.
+#hs20_deauth_req_timeout=60
+
 # Operator Friendly Name
 # This parameter can be used to configure one or more Operator Friendly Name
 # Duples. Each entry has a two or three character language code (ISO-639)
@@ -1576,6 +1661,32 @@
 # channels 36-48):
 #hs20_operating_class=5173
 
+# OSU icons
+# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
+#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
+#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
+
+# OSU SSID (see ssid2 for format description)
+# This is the SSID used for all OSU connections to all the listed OSU Providers.
+#osu_ssid="example"
+
+# OSU Providers
+# One or more sets of following parameter. Each OSU provider is started by the
+# mandatory osu_server_uri item. The other parameters add information for the
+# last added OSU provider.
+#
+#osu_server_uri=https://example.com/osu/
+#osu_friendly_name=eng:Example operator
+#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
+#osu_nai=anonymous@example.com
+#osu_method_list=1 0
+#osu_icon=icon32
+#osu_icon=icon64
+#osu_service_desc=eng:Example services
+#osu_service_desc=fin:Esimerkkipalveluja
+#
+#osu_server_uri=...
+
 ##### TESTING OPTIONS #########################################################
 #
 # The options in this section are only available when the build configuration
@@ -1619,6 +1730,11 @@
 # - is not the same as the MAC address of the radio
 # - is not the same as any other explicitly specified BSSID
 #
+# Not all drivers support multiple BSSes. The exact mechanism for determining
+# the driver capabilities is driver specific. With the current (i.e., a recent
+# kernel) drivers using nl80211, this information can be checked with "iw list"
+# (search for "valid interface combinations").
+#
 # Please note that hostapd uses some of the values configured for the first BSS
 # as the defaults for the following BSSes. However, it is recommended that all
 # BSSes include explicit configuration of all relevant configuration items.
diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user
index 12a2c61..00edc95 100644
--- a/hostapd/hostapd.eap_user
+++ b/hostapd/hostapd.eap_user
@@ -48,6 +48,12 @@
 # TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a
 # plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password
 # hash.
+#
+# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
+# to the way radius_auth_req_attr is used for Access-Request packet in
+# hostapd.conf. For EAP server, this is configured separately for each user
+# entry with radius_accept_attr=<value> line(s) following the main user entry
+# line.
 
 # Phase 1 users
 "user"		MD5	"password"
diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite
index f688327..826db34 100644
--- a/hostapd/hostapd.eap_user_sqlite
+++ b/hostapd/hostapd.eap_user_sqlite
@@ -2,6 +2,7 @@
 	identity TEXT PRIMARY KEY,
 	methods TEXT,
 	password TEXT,
+	remediation TEXT,
 	phase2 INTEGER
 );
 
@@ -15,3 +16,11 @@
 
 INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS');
 INSERT INTO wildcards(identity,methods) VALUES ('0','AKA');
+
+CREATE TABLE authlog(
+	timestamp TEXT,
+	session TEXT,
+	nas_ip TEXT,
+	username TEXT,
+	note TEXT
+);
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 7187abc..95f28d3 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,7 +18,7 @@
 
 static const char *hostapd_cli_version =
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *hostapd_cli_license =
@@ -216,8 +216,21 @@
 }
 
 
+static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
+		return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
+	return wpa_ctrl_command(ctrl, "STATUS");
+}
+
+
 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
+	if (argc > 0) {
+		char buf[100];
+		os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]);
+		return wpa_ctrl_command(ctrl, buf);
+	}
 	return wpa_ctrl_command(ctrl, "MIB");
 }
 
@@ -225,28 +238,19 @@
 static int hostapd_cli_exec(const char *program, const char *arg1,
 			    const char *arg2)
 {
-	char *cmd;
+	char *arg;
 	size_t len;
 	int res;
-	int ret = 0;
 
-	len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
-	cmd = os_malloc(len);
-	if (cmd == NULL)
+	len = os_strlen(arg1) + os_strlen(arg2) + 2;
+	arg = os_malloc(len);
+	if (arg == NULL)
 		return -1;
-	res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
-	if (res < 0 || (size_t) res >= len) {
-		os_free(cmd);
-		return -1;
-	}
-	cmd[len - 1] = '\0';
-#ifndef _WIN32_WCE
-	if (system(cmd) < 0)
-		ret = -1;
-#endif /* _WIN32_WCE */
-	os_free(cmd);
+	os_snprintf(arg, len, "%s %s", arg1, arg2);
+	res = os_exec(program, arg, 1);
+	os_free(arg);
 
-	return ret;
+	return res;
 }
 
 
@@ -270,12 +274,15 @@
 static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char buf[64];
-	if (argc != 1) {
-		printf("Invalid 'sta' command - exactly one argument, STA "
+	if (argc < 1) {
+		printf("Invalid 'sta' command - at least one argument, STA "
 		       "address, is required.\n");
 		return -1;
 	}
-	snprintf(buf, sizeof(buf), "STA %s", argv[0]);
+	if (argc > 1)
+		snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]);
+	else
+		snprintf(buf, sizeof(buf), "STA %s", argv[0]);
 	return wpa_ctrl_command(ctrl, buf);
 }
 
@@ -688,6 +695,90 @@
 }
 
 
+static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
+					   int argc, char *argv[])
+{
+	char buf[200];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid 'set_qos_map_set' command - "
+		       "one argument (comma delimited QoS map set) "
+		       "is needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
+	if (res < 0 || res >= (int) sizeof(buf))
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
+					     int argc, char *argv[])
+{
+	char buf[50];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid 'send_qos_map_conf' command - "
+		       "one argument (STA addr) is needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
+	if (res < 0 || res >= (int) sizeof(buf))
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	char buf[300];
+	int res;
+
+	if (argc < 2) {
+		printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
+		       "addr and URL) are needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
+			  argv[0], argv[1]);
+	if (res < 0 || res >= (int) sizeof(buf))
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
+					   char *argv[])
+{
+	char buf[300];
+	int res;
+
+	if (argc < 3) {
+		printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
+		return -1;
+	}
+
+	if (argc > 3)
+		res = os_snprintf(buf, sizeof(buf),
+				  "HS20_DEAUTH_REQ %s %s %s %s",
+				  argv[0], argv[1], argv[2], argv[3]);
+	else
+		res = os_snprintf(buf, sizeof(buf),
+				  "HS20_DEAUTH_REQ %s %s %s",
+				  argv[0], argv[1], argv[2]);
+	if (res < 0 || res >= (int) sizeof(buf))
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	hostapd_cli_quit = 1;
@@ -742,8 +833,8 @@
 	}
 
 	hostapd_cli_close_connection();
-	free(ctrl_ifname);
-	ctrl_ifname = strdup(argv[0]);
+	os_free(ctrl_ifname);
+	ctrl_ifname = os_strdup(argv[0]);
 
 	if (hostapd_cli_open_connection(ctrl_ifname)) {
 		printf("Connected to interface '%s.\n", ctrl_ifname);
@@ -801,6 +892,66 @@
 }
 
 
+static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
+				       int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+	int i;
+	char *tmp;
+	int total;
+
+	if (argc < 2) {
+		printf("Invalid chan_switch command: needs at least two "
+		       "arguments (count and freq)\n"
+		       "usage: <cs_count> <freq> [sec_channel_offset=] "
+		       "[center_freq1=] [center_freq2=] [bandwidth=] "
+		       "[blocktx] [ht|vht]\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
+			  argv[0], argv[1]);
+	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		printf("Too long CHAN_SWITCH command.\n");
+		return -1;
+	}
+
+	total = res;
+	for (i = 2; i < argc; i++) {
+		tmp = cmd + total;
+		res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
+		if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) {
+			printf("Too long CHAN_SWITCH command.\n");
+			return -1;
+		}
+		total += res;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc < 2 || argc > 3) {
+		printf("Invalid vendor command\n"
+		       "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
+			  argc == 3 ? argv[2] : "");
+	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		printf("Too long VENDOR command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -810,6 +961,7 @@
 	{ "ping", hostapd_cli_cmd_ping },
 	{ "mib", hostapd_cli_cmd_mib },
 	{ "relog", hostapd_cli_cmd_relog },
+	{ "status", hostapd_cli_cmd_status },
 	{ "sta", hostapd_cli_cmd_sta },
 	{ "all_sta", hostapd_cli_cmd_all_sta },
 	{ "new_sta", hostapd_cli_cmd_new_sta },
@@ -843,6 +995,12 @@
 	{ "quit", hostapd_cli_cmd_quit },
 	{ "set", hostapd_cli_cmd_set },
 	{ "get", hostapd_cli_cmd_get },
+	{ "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
+	{ "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
+	{ "chan_switch", hostapd_cli_cmd_chan_switch },
+	{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
+	{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
+	{ "vendor", hostapd_cli_cmd_vendor },
 	{ NULL, NULL }
 };
 
diff --git a/hostapd/main.c b/hostapd/main.c
index 90e5966..af4d85d 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -14,6 +14,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/uuid.h"
 #include "crypto/random.h"
 #include "crypto/tls.h"
 #include "common/version.h"
@@ -25,16 +26,10 @@
 #include "ap/ap_drv_ops.h"
 #include "config_file.h"
 #include "eap_register.h"
-#include "dump_state.h"
 #include "ctrl_iface.h"
 
-
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
-extern struct wpa_driver_ops *wpa_drivers[];
-
+struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
+						struct wpa_driver_capa *capa);
 
 struct hapd_global {
 	void **drv_priv;
@@ -99,7 +94,8 @@
 	if (hapd && hapd->conf && addr)
 		os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
 			    hapd->conf->iface, MAC2STR(addr),
-			    module_str ? " " : "", module_str, txt);
+			    module_str ? " " : "", module_str ? module_str : "",
+			    txt);
 	else if (hapd && hapd->conf)
 		os_snprintf(format, maxlen, "%s:%s%s %s",
 			    hapd->conf->iface, module_str ? " " : "",
@@ -114,7 +110,7 @@
 
 	if ((conf_stdout & module) && level >= conf_stdout_level) {
 		wpa_debug_print_timestamp();
-		printf("%s\n", format);
+		wpa_printf(MSG_INFO, "%s", format);
 	}
 
 #ifndef CONFIG_NATIVE_WINDOWS
@@ -148,65 +144,8 @@
 
 
 /**
- * hostapd_init - Allocate and initialize per-interface data
- * @config_file: Path to the configuration file
- * Returns: Pointer to the allocated interface data or %NULL on failure
- *
- * This function is used to allocate main data structures for per-interface
- * data. The allocated data buffer will be freed by calling
- * hostapd_cleanup_iface().
+ * hostapd_driver_init - Preparate driver interface
  */
-static struct hostapd_iface * hostapd_init(const char *config_file)
-{
-	struct hostapd_iface *hapd_iface = NULL;
-	struct hostapd_config *conf = NULL;
-	struct hostapd_data *hapd;
-	size_t i;
-
-	hapd_iface = os_zalloc(sizeof(*hapd_iface));
-	if (hapd_iface == NULL)
-		goto fail;
-
-	hapd_iface->config_fname = os_strdup(config_file);
-	if (hapd_iface->config_fname == NULL)
-		goto fail;
-
-	conf = hostapd_config_read(hapd_iface->config_fname);
-	if (conf == NULL)
-		goto fail;
-	hapd_iface->conf = conf;
-
-	hapd_iface->num_bss = conf->num_bss;
-	hapd_iface->bss = os_calloc(conf->num_bss,
-				    sizeof(struct hostapd_data *));
-	if (hapd_iface->bss == NULL)
-		goto fail;
-
-	for (i = 0; i < conf->num_bss; i++) {
-		hapd = hapd_iface->bss[i] =
-			hostapd_alloc_bss_data(hapd_iface, conf,
-					       &conf->bss[i]);
-		if (hapd == NULL)
-			goto fail;
-		hapd->msg_ctx = hapd;
-	}
-
-	return hapd_iface;
-
-fail:
-	wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
-		   config_file);
-	if (conf)
-		hostapd_config_free(conf);
-	if (hapd_iface) {
-		os_free(hapd_iface->config_fname);
-		os_free(hapd_iface->bss);
-		os_free(hapd_iface);
-	}
-	return NULL;
-}
-
-
 static int hostapd_driver_init(struct hostapd_iface *iface)
 {
 	struct wpa_init_params params;
@@ -274,18 +213,34 @@
 
 	if (hapd->driver->get_capa &&
 	    hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
+		struct wowlan_triggers *triggs;
+
 		iface->drv_flags = capa.flags;
 		iface->probe_resp_offloads = capa.probe_resp_offloads;
 		iface->extended_capa = capa.extended_capa;
 		iface->extended_capa_mask = capa.extended_capa_mask;
 		iface->extended_capa_len = capa.extended_capa_len;
 		iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
+
+		triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
+		if (triggs && hapd->driver->set_wowlan) {
+			if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
+				wpa_printf(MSG_ERROR, "set_wowlan failed");
+		}
+		os_free(triggs);
 	}
 
 	return 0;
 }
 
 
+/**
+ * hostapd_interface_init - Read configuration file and init BSS data
+ *
+ * This function is used to parse configuration file for a full interface (one
+ * or more BSSes sharing the same radio) and allocate memory for the BSS
+ * interfaces. No actiual driver operations are started.
+ */
 static struct hostapd_iface *
 hostapd_interface_init(struct hapd_interfaces *interfaces,
 		       const char *config_fname, int debug)
@@ -294,7 +249,7 @@
 	int k;
 
 	wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
-	iface = hostapd_init(config_fname);
+	iface = hostapd_init(interfaces, config_fname);
 	if (!iface)
 		return NULL;
 	iface->interfaces = interfaces;
@@ -304,7 +259,7 @@
 			iface->bss[0]->conf->logger_stdout_level--;
 	}
 
-	if (iface->conf->bss[0].iface[0] == '\0' &&
+	if (iface->conf->bss[0]->iface[0] == '\0' &&
 	    !hostapd_drv_none(iface->bss[0])) {
 		wpa_printf(MSG_ERROR, "Interface name not specified in %s",
 			   config_fname);
@@ -312,12 +267,6 @@
 		return NULL;
 	}
 
-	if (hostapd_driver_init(iface) ||
-	    hostapd_setup_interface(iface)) {
-		hostapd_interface_deinit_free(iface);
-		return NULL;
-	}
-
 	return iface;
 }
 
@@ -358,10 +307,7 @@
 
 static void handle_dump_state(int sig, void *signal_ctx)
 {
-#ifdef HOSTAPD_DUMP_STATE
-	struct hapd_interfaces *interfaces = signal_ctx;
-	hostapd_for_each_interface(interfaces, handle_dump_state_iface, NULL);
-#endif /* HOSTAPD_DUMP_STATE */
+	/* Not used anymore - ignore signal */
 }
 #endif /* CONFIG_NATIVE_WINDOWS */
 
@@ -480,7 +426,7 @@
 		"hostapd v" VERSION_STR "\n"
 		"User space daemon for IEEE 802.11 AP management,\n"
 		"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
-		"Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> "
+		"Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> "
 		"and contributors\n");
 }
 
@@ -507,6 +453,10 @@
 #ifdef CONFIG_DEBUG_FILE
 		"   -f   log output to debug file instead of stdout\n"
 #endif /* CONFIG_DEBUG_FILE */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+		"   -T = record to Linux tracing in addition to logging\n"
+		"        (records all messages regardless of debug verbosity)\n"
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 		"   -t   include timestamps in some debug messages\n"
 		"   -v   show hostapd version\n");
 
@@ -517,8 +467,9 @@
 static const char * hostapd_msg_ifname_cb(void *ctx)
 {
 	struct hostapd_data *hapd = ctx;
-	if (hapd && hapd->iconf && hapd->iconf->bss)
-		return hapd->iconf->bss->iface;
+	if (hapd && hapd->iconf && hapd->iconf->bss &&
+	    hapd->iconf->num_bss > 0 && hapd->iconf->bss[0])
+		return hapd->iconf->bss[0]->iface;
 	return NULL;
 }
 
@@ -563,15 +514,41 @@
 }
 
 
+#ifdef CONFIG_WPS
+static int gen_uuid(const char *txt_addr)
+{
+	u8 addr[ETH_ALEN];
+	u8 uuid[UUID_LEN];
+	char buf[100];
+
+	if (hwaddr_aton(txt_addr, addr) < 0)
+		return -1;
+
+	uuid_gen_mac_addr(addr, uuid);
+	if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0)
+		return -1;
+
+	printf("%s\n", buf);
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+
 int main(int argc, char *argv[])
 {
 	struct hapd_interfaces interfaces;
 	int ret = 1;
-	size_t i;
+	size_t i, j;
 	int c, debug = 0, daemonize = 0;
 	char *pid_file = NULL;
 	const char *log_file = NULL;
 	const char *entropy_file = NULL;
+	char **bss_config = NULL, **tmp_bss;
+	size_t num_bss_configs = 0;
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	int enable_trace_dbg = 0;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 
 	if (os_program_init())
 		return -1;
@@ -588,7 +565,7 @@
 	interfaces.global_ctrl_sock = -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "Bde:f:hKP:tvg:G:");
+		c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -619,6 +596,11 @@
 		case 't':
 			wpa_debug_timestamp++;
 			break;
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+		case 'T':
+			enable_trace_dbg = 1;
+			break;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 		case 'v':
 			show_version();
 			exit(1);
@@ -631,23 +613,46 @@
 			if (hostapd_get_ctrl_iface_group(&interfaces, optarg))
 				return -1;
 			break;
+		case 'b':
+			tmp_bss = os_realloc_array(bss_config,
+						   num_bss_configs + 1,
+						   sizeof(char *));
+			if (tmp_bss == NULL)
+				goto out;
+			bss_config = tmp_bss;
+			bss_config[num_bss_configs++] = optarg;
+			break;
+#ifdef CONFIG_WPS
+		case 'u':
+			return gen_uuid(optarg);
+#endif /* CONFIG_WPS */
 		default:
 			usage();
 			break;
 		}
 	}
 
-	if (optind == argc && interfaces.global_iface_path == NULL)
+	if (optind == argc && interfaces.global_iface_path == NULL &&
+	    num_bss_configs == 0)
 		usage();
 
 	wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
 
 	if (log_file)
 		wpa_debug_open_file(log_file);
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (enable_trace_dbg) {
+		int tret = wpa_debug_open_linux_tracing();
+		if (tret) {
+			wpa_printf(MSG_ERROR, "Failed to enable trace logging");
+			return -1;
+		}
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 
 	interfaces.count = argc - optind;
-	if (interfaces.count) {
-		interfaces.iface = os_calloc(interfaces.count,
+	if (interfaces.count || num_bss_configs) {
+		interfaces.iface = os_calloc(interfaces.count + num_bss_configs,
 					     sizeof(struct hostapd_iface *));
 		if (interfaces.iface == NULL) {
 			wpa_printf(MSG_ERROR, "malloc failed");
@@ -660,7 +665,7 @@
 		return -1;
 	}
 
-	/* Initialize interfaces */
+	/* Allocate and parse configuration for full interface files */
 	for (i = 0; i < interfaces.count; i++) {
 		interfaces.iface[i] = hostapd_interface_init(&interfaces,
 							     argv[optind + i],
@@ -671,6 +676,57 @@
 		}
 	}
 
+	/* Allocate and parse configuration for per-BSS files */
+	for (i = 0; i < num_bss_configs; i++) {
+		struct hostapd_iface *iface;
+		char *fname;
+
+		wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]);
+		fname = os_strchr(bss_config[i], ':');
+		if (fname == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid BSS config identifier '%s'",
+				   bss_config[i]);
+			goto out;
+		}
+		*fname++ = '\0';
+		iface = hostapd_interface_init_bss(&interfaces, bss_config[i],
+						   fname, debug);
+		if (iface == NULL)
+			goto out;
+		for (j = 0; j < interfaces.count; j++) {
+			if (interfaces.iface[j] == iface)
+				break;
+		}
+		if (j == interfaces.count) {
+			struct hostapd_iface **tmp;
+			tmp = os_realloc_array(interfaces.iface,
+					       interfaces.count + 1,
+					       sizeof(struct hostapd_iface *));
+			if (tmp == NULL) {
+				hostapd_interface_deinit_free(iface);
+				goto out;
+			}
+			interfaces.iface = tmp;
+			interfaces.iface[interfaces.count++] = iface;
+		}
+	}
+
+	/*
+	 * Enable configured interfaces. Depending on channel configuration,
+	 * this may complete full initialization before returning or use a
+	 * callback mechanism to complete setup in case of operations like HT
+	 * co-ex scans, ACS, or DFS are needed to determine channel parameters.
+	 * In such case, the interface will be enabled from eloop context within
+	 * hostapd_global_run().
+	 */
+	interfaces.terminate_on_error = interfaces.count;
+	for (i = 0; i < interfaces.count; i++) {
+		if (hostapd_driver_init(interfaces.iface[i]) ||
+		    hostapd_setup_interface(interfaces.iface[i]))
+			goto out;
+	}
+
 	hostapd_global_ctrl_iface_init(&interfaces);
 
 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
@@ -683,8 +739,14 @@
  out:
 	hostapd_global_ctrl_iface_deinit(&interfaces);
 	/* Deinitialize all interfaces */
-	for (i = 0; i < interfaces.count; i++)
+	for (i = 0; i < interfaces.count; i++) {
+		if (!interfaces.iface[i])
+			continue;
+		interfaces.iface[i]->driver_ap_teardown =
+			!!(interfaces.iface[i]->drv_flags &
+			   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
 		hostapd_interface_deinit_free(interfaces.iface[i]);
+	}
 	os_free(interfaces.iface);
 
 	hostapd_global_deinit(pid_file);
@@ -692,6 +754,9 @@
 
 	if (log_file)
 		wpa_debug_close_file();
+	wpa_debug_close_linux_tracing();
+
+	os_free(bss_config);
 
 	os_program_deinit();
 
diff --git a/hostapd/wps-ap-nfc.py b/hostapd/wps-ap-nfc.py
index 61b5519..2fc3012 100755
--- a/hostapd/wps-ap-nfc.py
+++ b/hostapd/wps-ap-nfc.py
@@ -9,6 +9,7 @@
 import os
 import sys
 import time
+import argparse
 
 import nfc
 import nfc.ndef
@@ -16,11 +17,25 @@
 import nfc.handover
 
 import logging
-logging.basicConfig()
 
 import wpaspy
 
 wpas_ctrl = '/var/run/hostapd'
+continue_loop = True
+summary_file = None
+success_file = None
+
+def summary(txt):
+    print txt
+    if summary_file:
+        with open(summary_file, 'a') as f:
+            f.write(txt + "\n")
+
+def success_report(txt):
+    summary(txt)
+    if success_file:
+        with open(success_file, 'a') as f:
+            f.write(txt + "\n")
 
 def wpas_connect():
     ifaces = []
@@ -47,29 +62,40 @@
 def wpas_tag_read(message):
     wpas = wpas_connect()
     if (wpas == None):
-        return
-    print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
+        return False
+    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
+        return False
+    return True
 
 
 def wpas_get_config_token():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex")
+    ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_password_token():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
+    ret = wpas.request("WPS_NFC_TOKEN NDEF")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_handover_sel():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
+    ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_report_handover(req, sel):
@@ -82,185 +108,228 @@
 
 
 class HandoverServer(nfc.handover.HandoverServer):
-    def __init__(self):
-        super(HandoverServer, self).__init__()
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.ho_server_processing = False
+        self.success = False
+
+    # override to avoid parser error in request/response.pretty() in nfcpy
+    # due to new WSC handover format
+    def _process_request(self, request):
+        summary("received handover request {}".format(request.type))
+        response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+        if not request.type == 'urn:nfc:wkt:Hr':
+            summary("not a handover request")
+        else:
+            try:
+                request = nfc.ndef.HandoverRequestMessage(request)
+            except nfc.ndef.DecodeError as e:
+                summary("error decoding 'Hr' message: {}".format(e))
+            else:
+                response = self.process_request(request)
+        summary("send handover response {}".format(response.type))
+        return response
 
     def process_request(self, request):
-        print "HandoverServer - request received"
-        print "Parsed handover request: " + request.pretty()
+        summary("HandoverServer - request received")
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
+        print str(request).encode("hex")
 
         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
 
         for carrier in request.carriers:
             print "Remote carrier type: " + carrier.type
             if carrier.type == "application/vnd.wfa.wsc":
-                print "WPS carrier type match - add WPS carrier record"
-                self.received_carrier = carrier.record
+                summary("WPS carrier type match - add WPS carrier record")
                 data = wpas_get_handover_sel()
                 if data is None:
-                    print "Could not get handover select carrier record from hostapd"
+                    summary("Could not get handover select carrier record from hostapd")
                     continue
                 print "Handover select carrier record from hostapd:"
                 print data.encode("hex")
-                self.sent_carrier = data
+                if "OK" in wpas_report_handover(carrier.record, data):
+                    success_report("Handover reported successfully")
+                else:
+                    summary("Handover report rejected")
 
                 message = nfc.ndef.Message(data);
                 sel.add_carrier(message[0], "active", message[1:])
 
         print "Handover select:"
-        print sel.pretty()
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
         print str(sel).encode("hex")
 
-        print "Sending handover select"
+        summary("Sending handover select")
+        self.success = True
         return sel
 
 
-def wps_handover_resp(peer):
-    print "Trying to handle WPS handover"
-
-    srv = HandoverServer()
-    srv.sent_carrier = None
-
-    nfc.llcp.activate(peer);
-
-    try:
-        print "Trying handover";
-        srv.start()
-        print "Wait for disconnect"
-        while nfc.llcp.connected():
-            time.sleep(0.1)
-        print "Disconnected after handover"
-    except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
-        return
-
-    if srv.sent_carrier:
-        wpas_report_handover(srv.received_carrier, srv.sent_carrier)
-
-    print "Remove peer"
-    nfc.llcp.shutdown()
-    print "Done with handover"
-
-
 def wps_tag_read(tag):
+    success = False
     if len(tag.ndef.message):
-        message = nfc.ndef.Message(tag.ndef.message)
-        print "message type " + message.type
-
-        for record in message:
+        for record in tag.ndef.message:
             print "record type " + record.type
             if record.type == "application/vnd.wfa.wsc":
-                print "WPS tag - send to hostapd"
-                wpas_tag_read(tag.ndef.message)
+                summary("WPS tag - send to hostapd")
+                success = wpas_tag_read(tag.ndef.message)
                 break
     else:
-        print "Empty tag"
+        summary("Empty tag")
 
-    print "Remove tag"
-    while tag.is_present:
+    if success:
+        success_report("Tag read succeeded")
+
+    return success
+
+
+def rdwr_connected_write(tag):
+    summary("Tag found - writing - " + str(tag))
+    global write_data
+    tag.ndef.message = str(write_data)
+    success_report("Tag write succeeded")
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global write_wait_remove
+    while write_wait_remove and tag.is_present:
         time.sleep(0.1)
 
-
-def wps_write_config_tag(clf):
-    print "Write WPS config token"
-    data = wpas_get_config_token()
-    if (data == None):
-        print "Could not get WPS config token from hostapd"
+def wps_write_config_tag(clf, wait_remove=True):
+    summary("Write WPS config token")
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_config_token()
+    if write_data == None:
+        summary("Could not get WPS config token from hostapd")
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def wps_write_password_tag(clf):
-    print "Write WPS password token"
-    data = wpas_get_password_token()
-    if (data == None):
-        print "Could not get WPS password token from hostapd"
+def wps_write_password_tag(clf, wait_remove=True):
+    summary("Write WPS password token")
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_password_token()
+    if write_data == None:
+        summary("Could not get WPS password token from hostapd")
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def find_peer(clf):
-    while True:
-        if nfc.llcp.connected():
-            print "LLCP connected"
-        general_bytes = nfc.llcp.startup({})
-        peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "listen -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+def rdwr_connected(tag):
+    global only_one, no_wait
+    summary("Tag connected: " + str(tag))
 
-        peer = clf.poll(general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "poll -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = wps_tag_read(tag)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        summary("Not an NDEF tag - remove tag")
+        return True
 
-        if peer:
-            print "Found tag"
-            return peer
+    return not no_wait
+
+
+def llcp_startup(clf, llc):
+    print "Start LLCP server"
+    global srv
+    srv = HandoverServer(llc)
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global srv
+    srv.start()
+    return True
 
 
 def main():
     clf = nfc.ContactlessFrontend()
 
+    parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--summary',
+                        help='summary file for writing status updates')
+    parser.add_argument('--success',
+                        help='success file for writing success update')
+    parser.add_argument('command', choices=['write-config',
+                                            'write-password'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    if args.summary:
+        global summary_file
+        summary_file = args.summary
+
+    if args.success:
+        global success_file
+        success_file = args.success
+
+    logging.basicConfig(level=args.loglevel)
+
     try:
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config":
-            wps_write_config_tag(clf)
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password":
-            wps_write_password_tag(clf)
+        if args.command == "write-config":
+            wps_write_config_tag(clf, wait_remove=not args.no_wait)
             raise SystemExit
 
-        while True:
+        if args.command == "write-password":
+            wps_write_password_tag(clf, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        global continue_loop
+        while continue_loop:
             print "Waiting for a tag or peer to be touched"
+            wait_connection = True
+            try:
+                if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                   llcp={'on-startup': llcp_startup,
+                                         'on-connect': llcp_connected}):
+                    break
+            except Exception, e:
+                print "clf.connect failed"
 
-            tag = find_peer(clf)
-            if isinstance(tag, nfc.DEP):
-                wps_handover_resp(tag)
-                continue
-
-            if tag.ndef:
-                wps_tag_read(tag)
-                continue
-
-            print "Not an NDEF tag - remove tag"
-            while tag.is_present:
-                time.sleep(0.1)
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
 
     except KeyboardInterrupt:
         raise SystemExit
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
new file mode 100644
index 0000000..63cbc6f
--- /dev/null
+++ b/hs20/client/Android.mk
@@ -0,0 +1,77 @@
+LOCAL_PATH := $(call my-dir)
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/../../src/utils
+INCLUDES += $(LOCAL_PATH)/../../src/common
+INCLUDES += $(LOCAL_PATH)/../../src
+INCLUDES += external/openssl/include
+INCLUDES += external/libxml2/include
+INCLUDES += external/curl/include
+INCLUDES += external/webkit/Source/WebKit/gtk
+ifneq ($(wildcard external/icu),)
+INCLUDES += external/icu/icu4c/source/common
+else
+INCLUDES += external/icu4c/common
+endif
+
+
+#GTKCFLAGS := $(shell pkg-config --cflags gtk+-2.0 webkit-1.0)
+#GTKLIBS := $(shell pkg-config --libs gtk+-2.0 webkit-1.0)
+#CFLAGS += $(GTKCFLAGS)
+#LIBS += $(GTKLIBS)
+
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DLIBXML_SCHEMAS_ENABLED
+L_CFLAGS += -DLIBXML_REGEXP_ENABLED
+
+OBJS = spp_client.c
+OBJS += oma_dm_client.c
+OBJS += osu_client.c
+OBJS += est.c
+OBJS += ../../src/common/wpa_ctrl.c
+OBJS += ../../src/common/wpa_helpers.c
+OBJS += ../../src/utils/xml-utils.c
+#OBJS += ../../src/utils/browser-android.c
+OBJS += ../../src/utils/browser-wpadebug.c
+OBJS += ../../src/utils/wpabuf.c
+OBJS += ../../src/utils/eloop.c
+OBJS += ../../src/wps/httpread.c
+OBJS += ../../src/wps/http_server.c
+OBJS += ../../src/utils/xml_libxml2.c
+OBJS += ../../src/utils/http_curl.c
+OBJS += ../../src/utils/base64.c
+OBJS += ../../src/utils/os_unix.c
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.c
+OBJS += ../../src/utils/common.c
+OBJS += ../../src/crypto/crypto_internal.c
+OBJS += ../../src/crypto/md5-internal.c
+OBJS += ../../src/crypto/sha1-internal.c
+OBJS += ../../src/crypto/sha256-internal.c
+
+L_CFLAGS += -DEAP_TLS_OPENSSL
+
+#CFLAGS += $(shell xml2-config --cflags)
+#LIBS += $(shell xml2-config --libs)
+
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hs20-osu-client
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
+#LOCAL_SHARED_LIBRARIES += libxml2
+LOCAL_STATIC_LIBRARIES += libxml2
+LOCAL_SHARED_LIBRARIES += libicuuc
+LOCAL_SHARED_LIBRARIES += libcurl
+
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
new file mode 100644
index 0000000..ca67b54
--- /dev/null
+++ b/hs20/client/Makefile
@@ -0,0 +1,94 @@
+all: hs20-osu-client
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/common
+CFLAGS += -I../../src
+
+ifndef CONFIG_NO_BROWSER
+ifndef CONFIG_BROWSER_SYSTEM
+GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkitgtk-3.0)
+GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkitgtk-3.0)
+CFLAGS += $(GTKCFLAGS)
+LIBS += $(GTKLIBS)
+endif
+endif
+
+OBJS=spp_client.o
+OBJS += oma_dm_client.o
+OBJS += osu_client.o
+OBJS += est.o
+OBJS += ../../src/utils/xml-utils.o
+CFLAGS += -DCONFIG_CTRL_IFACE
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+OBJS += ../../src/common/wpa_ctrl.o ../../src/common/wpa_helpers.o
+ifdef CONFIG_NO_BROWSER
+CFLAGS += -DCONFIG_NO_BROWSER
+else
+ifdef CONFIG_BROWSER_SYSTEM
+OBJS += ../../src/utils/eloop.o
+OBJS += ../../src/utils/wpabuf.o
+OBJS += ../../src/wps/httpread.o
+OBJS += ../../src/wps/http_server.o
+OBJS += ../../src/utils/browser-system.o
+else
+OBJS += ../../src/utils/browser.o
+endif
+endif
+OBJS += ../../src/utils/xml_libxml2.o
+OBJS += ../../src/utils/http_curl.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/os_unix.o
+CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/crypto/crypto_internal.o
+OBJS += ../../src/crypto/md5-internal.o
+OBJS += ../../src/crypto/sha1-internal.o
+OBJS += ../../src/crypto/sha256-internal.o
+
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+LIBS += -lcurl
+
+CFLAGS += -DEAP_TLS_OPENSSL
+LIBS += -lssl -lcrypto
+
+hs20-osu-client: $(OBJS)
+	$(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
+	@$(E) "  LD " $@
+
+%.o: %.c
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
+
+clean:
+	rm -f core *~ *.o *.d hs20-osu-client
+	rm -f ../../src/utils/*.o
+	rm -f ../../src/utils/*.d
+	rm -f ../../src/common/*.o
+	rm -f ../../src/common/*.d
+	rm -f ../../src/crypto/*.o
+	rm -f ../../src/crypto/*.d
+	rm -f ../../src/wps/*.o
+	rm -f ../../src/wps/*.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hs20/client/devdetail.xml b/hs20/client/devdetail.xml
new file mode 100644
index 0000000..6d0389e
--- /dev/null
+++ b/hs20/client/devdetail.xml
@@ -0,0 +1,47 @@
+<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
+	<Ext>
+		<org.wi-fi>
+			<Wi-Fi>
+				<EAPMethodList>
+					<EAPMethod1>
+						<EAPType>13</EAPType>
+					</EAPMethod1>
+					<EAPMethod2>
+						<EAPType>21</EAPType>
+						<InnerMethod>MS-CHAP-V2</InnerMethod>
+					</EAPMethod2>
+					<EAPMethod3>
+						<EAPType>18</EAPType>
+					</EAPMethod3>
+					<EAPMethod4>
+						<EAPType>23</EAPType>
+					</EAPMethod4>
+					<EAPMethod5>
+						<EAPType>50</EAPType>
+					</EAPMethod5>
+				</EAPMethodList>
+				<ManufacturingCertificate>false</ManufacturingCertificate>
+				<Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
+				<IMSI>310026000000000</IMSI>
+				<IMEI_MEID>imei:490123456789012</IMEI_MEID>
+				<ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
+				<Ops>
+					<launchBrowserToURI></launchBrowserToURI>
+					<negotiateClientCertTLS></negotiateClientCertTLS>
+					<getCertificate></getCertificate>
+				</Ops>
+			</Wi-Fi>
+		</org.wi-fi>
+	</Ext>
+	<URI>
+		<MaxDepth>0</MaxDepth>
+		<MaxTotLen>0</MaxTotLen>
+		<MaxSegLen>0</MaxSegLen>
+	</URI>
+	<DevType>MobilePhone</DevType>
+	<OEM>Manufacturer</OEM>
+	<FwV>1.0</FwV>
+	<SwV>1.0</SwV>
+	<HwV>1.0</HwV>
+	<LrgObj>false</LrgObj>
+</DevDetail>
diff --git a/hs20/client/devinfo.xml b/hs20/client/devinfo.xml
new file mode 100644
index 0000000..d48a520
--- /dev/null
+++ b/hs20/client/devinfo.xml
@@ -0,0 +1,7 @@
+<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
+	<DevId>urn:Example:HS20-station:123456</DevId>
+	<Man>Manufacturer</Man>
+	<Mod>HS20-station</Mod>
+	<DmV>1.2</DmV>
+	<Lang>en</Lang>
+</DevInfo>
diff --git a/hs20/client/est.c b/hs20/client/est.c
new file mode 100644
index 0000000..ec05bc4
--- /dev/null
+++ b/hs20/client/est.c
@@ -0,0 +1,715 @@
+/*
+ * Hotspot 2.0 OSU client - EST client
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rsa.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "common.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "osu_client.h"
+
+
+static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
+			 size_t len, char *pem_file, char *der_file)
+{
+	PKCS7 *p7 = NULL;
+	const unsigned char *p = pkcs7;
+	STACK_OF(X509) *certs;
+	int i, num, ret = -1;
+	BIO *out = NULL;
+
+	p7 = d2i_PKCS7(NULL, &p, len);
+	if (p7 == NULL) {
+		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		write_result(ctx, "Could not parse PKCS#7 object from EST");
+		goto fail;
+	}
+
+	switch (OBJ_obj2nid(p7->type)) {
+	case NID_pkcs7_signed:
+		certs = p7->d.sign->cert;
+		break;
+	case NID_pkcs7_signedAndEnveloped:
+		certs = p7->d.signed_and_enveloped->cert;
+		break;
+	default:
+		certs = NULL;
+		break;
+	}
+
+	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+		wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
+		write_result(ctx, "No certificates found in PKCS#7 object");
+		goto fail;
+	}
+
+	if (der_file) {
+		FILE *f = fopen(der_file, "wb");
+		if (f == NULL)
+			goto fail;
+		i2d_X509_fp(f, sk_X509_value(certs, 0));
+		fclose(f);
+	}
+
+	if (pem_file) {
+		out = BIO_new(BIO_s_file());
+		if (out == NULL ||
+		    BIO_write_filename(out, pem_file) <= 0)
+			goto fail;
+
+		for (i = 0; i < num; i++) {
+			X509 *cert = sk_X509_value(certs, i);
+			X509_print(out, cert);
+			PEM_write_bio_X509(out, cert);
+			BIO_puts(out, "\n");
+		}
+	}
+
+	ret = 0;
+
+fail:
+	PKCS7_free(p7);
+	if (out)
+		BIO_free_all(out);
+
+	return ret;
+}
+
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
+{
+	char *buf, *resp;
+	size_t buflen;
+	unsigned char *pkcs7;
+	size_t pkcs7_len, resp_len;
+	int res;
+
+	buflen = os_strlen(url) + 100;
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return -1;
+
+	os_snprintf(buf, buflen, "%s/cacerts", url);
+	wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
+	write_summary(ctx, "Download EST cacerts from %s", buf);
+	ctx->no_osu_cert_validation = 1;
+	http_ocsp_set(ctx->http, 1);
+	res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
+				 ctx->ca_fname);
+	http_ocsp_set(ctx->http,
+		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+	ctx->no_osu_cert_validation = 0;
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
+			   buf);
+		write_result(ctx, "Failed to download EST cacerts from %s",
+			     buf);
+		os_free(buf);
+		return -1;
+	}
+	os_free(buf);
+
+	resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
+	if (resp == NULL) {
+		wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
+		write_result(ctx, "Could not read EST cacerts");
+		return -1;
+	}
+
+	pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+	if (pkcs7 && pkcs7_len < resp_len / 2) {
+		wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
+			   (unsigned int) pkcs7_len, (unsigned int) resp_len);
+		os_free(pkcs7);
+		pkcs7 = NULL;
+	}
+	if (pkcs7 == NULL) {
+		wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+		pkcs7 = os_malloc(resp_len);
+		if (pkcs7) {
+			os_memcpy(pkcs7, resp, resp_len);
+			pkcs7_len = resp_len;
+		}
+	}
+	os_free(resp);
+
+	if (pkcs7 == NULL) {
+		wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
+		write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
+		return -1;
+	}
+
+	res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
+			    NULL);
+	os_free(pkcs7);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
+		write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
+		return -1;
+	}
+	unlink("Cert/est-cacerts.txt");
+
+	return 0;
+}
+
+
+/*
+ * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
+ *
+ * AttrOrOID ::= CHOICE {
+ *   oid OBJECT IDENTIFIER,
+ *   attribute Attribute }
+ *
+ * Attribute ::= SEQUENCE {
+ *   type OBJECT IDENTIFIER,
+ *   values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
+ */
+
+typedef struct {
+	ASN1_OBJECT *type;
+	STACK_OF(ASN1_OBJECT) *values;
+} Attribute;
+
+typedef struct {
+	int type;
+	union {
+		ASN1_OBJECT *oid;
+		Attribute *attribute;
+	} d;
+} AttrOrOID;
+
+typedef struct {
+	int type;
+	STACK_OF(AttrOrOID) *attrs;
+} CsrAttrs;
+
+ASN1_SEQUENCE(Attribute) = {
+	ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
+	ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
+} ASN1_SEQUENCE_END(Attribute);
+
+ASN1_CHOICE(AttrOrOID) = {
+	ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
+	ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
+} ASN1_CHOICE_END(AttrOrOID);
+
+ASN1_CHOICE(CsrAttrs) = {
+	ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
+} ASN1_CHOICE_END(CsrAttrs);
+
+IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
+
+
+static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
+			     STACK_OF(X509_EXTENSION) *exts)
+{
+	char txt[100];
+	int res;
+
+	if (!oid)
+		return;
+
+	res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+	if (res < 0 || res >= (int) sizeof(txt))
+		return;
+
+	if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
+		wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
+	} else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
+		wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
+	} else {
+		wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
+	}
+}
+
+
+static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
+				 STACK_OF(ASN1_OBJECT) *values,
+				 STACK_OF(X509_EXTENSION) *exts)
+{
+	char txt[100];
+	int i, num, res;
+
+	num = sk_ASN1_OBJECT_num(values);
+	for (i = 0; i < num; i++) {
+		ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
+
+		res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+		if (res < 0 || res >= (int) sizeof(txt))
+			continue;
+
+		if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
+			wpa_printf(MSG_INFO, "TODO: extReq macAddress");
+		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
+			wpa_printf(MSG_INFO, "TODO: extReq imei");
+		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
+			wpa_printf(MSG_INFO, "TODO: extReq meid");
+		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
+			wpa_printf(MSG_INFO, "TODO: extReq DevId");
+		} else {
+			wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
+				   txt);
+		}
+	}
+}
+
+
+static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
+			      STACK_OF(X509_EXTENSION) *exts)
+{
+	char txt[100], txt2[100];
+	int i, num, res;
+
+	if (!attr || !attr->type || !attr->values)
+		return;
+
+	res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
+	if (res < 0 || res >= (int) sizeof(txt))
+		return;
+
+	if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
+		add_csrattrs_ext_req(ctx, attr->values, exts);
+		return;
+	}
+
+	num = sk_ASN1_OBJECT_num(attr->values);
+	for (i = 0; i < num; i++) {
+		ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
+
+		res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
+		if (res < 0 || res >= (int) sizeof(txt2))
+			continue;
+
+		wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
+			   txt, txt2);
+	}
+}
+
+
+static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
+			 STACK_OF(X509_EXTENSION) *exts)
+{
+	int i, num;
+
+	if (!csrattrs || ! csrattrs->attrs)
+		return;
+
+	num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
+	for (i = 0; i < num; i++) {
+		AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
+		switch (ao->type) {
+		case 0:
+			add_csrattrs_oid(ctx, ao->d.oid, exts);
+			break;
+		case 1:
+			add_csrattrs_attr(ctx, ao->d.attribute, exts);
+			break;
+		}
+	}
+}
+
+
+static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
+			char *csr_pem, char *est_req, char *old_cert,
+			CsrAttrs *csrattrs)
+{
+	EVP_PKEY_CTX *pctx = NULL;
+	EVP_PKEY *pkey = NULL;
+	RSA *rsa;
+	X509_REQ *req = NULL;
+	int ret = -1;
+	unsigned int val;
+	X509_NAME *subj = NULL;
+	char name[100];
+	STACK_OF(X509_EXTENSION) *exts = NULL;
+	X509_EXTENSION *ex;
+	BIO *out;
+
+	wpa_printf(MSG_INFO, "Generate RSA private key");
+	write_summary(ctx, "Generate RSA private key");
+	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+	if (!pctx)
+		return -1;
+
+	if (EVP_PKEY_keygen_init(pctx) <= 0)
+		goto fail;
+
+	if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
+		goto fail;
+
+	if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
+		goto fail;
+	EVP_PKEY_CTX_free(pctx);
+	pctx = NULL;
+
+	rsa = EVP_PKEY_get1_RSA(pkey);
+	if (rsa == NULL)
+		goto fail;
+
+	if (key_pem) {
+		FILE *f = fopen(key_pem, "wb");
+		if (f == NULL)
+			goto fail;
+		if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL,
+					     NULL)) {
+			wpa_printf(MSG_INFO, "Could not write private key: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			fclose(f);
+			goto fail;
+		}
+		fclose(f);
+	}
+
+	wpa_printf(MSG_INFO, "Generate CSR");
+	write_summary(ctx, "Generate CSR");
+	req = X509_REQ_new();
+	if (req == NULL)
+		goto fail;
+
+	if (old_cert) {
+		FILE *f;
+		X509 *cert;
+		int res;
+
+		f = fopen(old_cert, "r");
+		if (f == NULL)
+			goto fail;
+		cert = PEM_read_X509(f, NULL, NULL, NULL);
+		fclose(f);
+
+		if (cert == NULL)
+			goto fail;
+		res = X509_REQ_set_subject_name(req,
+						X509_get_subject_name(cert));
+		X509_free(cert);
+		if (!res)
+			goto fail;
+	} else {
+		os_get_random((u8 *) &val, sizeof(val));
+		os_snprintf(name, sizeof(name), "cert-user-%u", val);
+		subj = X509_NAME_new();
+		if (subj == NULL ||
+		    !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
+						(unsigned char *) name,
+						-1, -1, 0) ||
+		    !X509_REQ_set_subject_name(req, subj))
+			goto fail;
+		X509_NAME_free(subj);
+		subj = NULL;
+	}
+
+	if (!X509_REQ_set_pubkey(req, pkey))
+		goto fail;
+
+	exts = sk_X509_EXTENSION_new_null();
+	if (!exts)
+		goto fail;
+
+	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
+				 "CA:FALSE");
+	if (ex == NULL ||
+	    !sk_X509_EXTENSION_push(exts, ex))
+		goto fail;
+
+	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
+				 "nonRepudiation,digitalSignature,keyEncipherment");
+	if (ex == NULL ||
+	    !sk_X509_EXTENSION_push(exts, ex))
+		goto fail;
+
+	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
+				 "1.3.6.1.4.1.40808.1.1.2");
+	if (ex == NULL ||
+	    !sk_X509_EXTENSION_push(exts, ex))
+		goto fail;
+
+	add_csrattrs(ctx, csrattrs, exts);
+
+	if (!X509_REQ_add_extensions(req, exts))
+		goto fail;
+	sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+	exts = NULL;
+
+	if (!X509_REQ_sign(req, pkey, EVP_sha256()))
+		goto fail;
+
+	out = BIO_new(BIO_s_mem());
+	if (out) {
+		char *txt;
+		size_t rlen;
+
+		X509_REQ_print(out, req);
+		rlen = BIO_ctrl_pending(out);
+		txt = os_malloc(rlen + 1);
+		if (txt) {
+			int res = BIO_read(out, txt, rlen);
+			if (res > 0) {
+				txt[res] = '\0';
+				wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
+					   txt);
+			}
+			os_free(txt);
+		}
+		BIO_free(out);
+	}
+
+	if (csr_pem) {
+		FILE *f = fopen(csr_pem, "w");
+		if (f == NULL)
+			goto fail;
+		X509_REQ_print_fp(f, req);
+		if (!PEM_write_X509_REQ(f, req)) {
+			fclose(f);
+			goto fail;
+		}
+		fclose(f);
+	}
+
+	if (est_req) {
+		BIO *mem = BIO_new(BIO_s_mem());
+		BUF_MEM *ptr;
+		char *pos, *end, *buf_end;
+		FILE *f;
+
+		if (mem == NULL)
+			goto fail;
+		if (!PEM_write_bio_X509_REQ(mem, req)) {
+			BIO_free(mem);
+			goto fail;
+		}
+
+		BIO_get_mem_ptr(mem, &ptr);
+		pos = ptr->data;
+		buf_end = pos + ptr->length;
+
+		/* Remove START/END lines */
+		while (pos < buf_end && *pos != '\n')
+			pos++;
+		if (pos == buf_end) {
+			BIO_free(mem);
+			goto fail;
+		}
+		pos++;
+
+		end = pos;
+		while (end < buf_end && *end != '-')
+			end++;
+
+		f = fopen(est_req, "w");
+		if (f == NULL) {
+			BIO_free(mem);
+			goto fail;
+		}
+		fwrite(pos, end - pos, 1, f);
+		fclose(f);
+
+		BIO_free(mem);
+	}
+
+	ret = 0;
+fail:
+	if (exts)
+		sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+	if (subj)
+		X509_NAME_free(subj);
+	if (req)
+		X509_REQ_free(req);
+	if (pkey)
+		EVP_PKEY_free(pkey);
+	if (pctx)
+		EVP_PKEY_CTX_free(pctx);
+	return ret;
+}
+
+
+int est_build_csr(struct hs20_osu_client *ctx, const char *url)
+{
+	char *buf;
+	size_t buflen;
+	int res;
+	char old_cert_buf[200];
+	char *old_cert = NULL;
+	CsrAttrs *csrattrs = NULL;
+
+	buflen = os_strlen(url) + 100;
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return -1;
+
+	os_snprintf(buf, buflen, "%s/csrattrs", url);
+	wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
+	write_summary(ctx, "Download EST csrattrs from %s", buf);
+	ctx->no_osu_cert_validation = 1;
+	http_ocsp_set(ctx->http, 1);
+	res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
+				 ctx->ca_fname);
+	http_ocsp_set(ctx->http,
+		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+	ctx->no_osu_cert_validation = 0;
+	os_free(buf);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
+	} else {
+		size_t resp_len;
+		char *resp;
+		unsigned char *attrs;
+		const unsigned char *pos;
+		size_t attrs_len;
+
+		resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
+		if (resp == NULL) {
+			wpa_printf(MSG_INFO, "Could not read csrattrs");
+			return -1;
+		}
+
+		attrs = base64_decode((unsigned char *) resp, resp_len,
+				      &attrs_len);
+		os_free(resp);
+
+		if (attrs == NULL) {
+			wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
+			return -1;
+		}
+		unlink("Cert/est-csrattrs.txt");
+
+		pos = attrs;
+		csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
+		os_free(attrs);
+		if (csrattrs == NULL) {
+			wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
+			/* Continue assuming no additional requirements */
+		}
+	}
+
+	if (ctx->client_cert_present) {
+		os_snprintf(old_cert_buf, sizeof(old_cert_buf),
+			    "SP/%s/client-cert.pem", ctx->fqdn);
+		old_cert = old_cert_buf;
+	}
+
+	res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
+			   "Cert/est-req.b64", old_cert, csrattrs);
+	if (csrattrs)
+		CsrAttrs_free(csrattrs);
+
+	return res;
+}
+
+
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+		      const char *user, const char *pw)
+{
+	char *buf, *resp, *req, *req2;
+	size_t buflen, resp_len, len, pkcs7_len;
+	unsigned char *pkcs7;
+	FILE *f;
+	char client_cert_buf[200];
+	char client_key_buf[200];
+	const char *client_cert = NULL, *client_key = NULL;
+	int res;
+
+	req = os_readfile("Cert/est-req.b64", &len);
+	if (req == NULL) {
+		wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
+		return -1;
+	}
+	req2 = os_realloc(req, len + 1);
+	if (req2 == NULL) {
+		os_free(req);
+		return -1;
+	}
+	req2[len] = '\0';
+	req = req2;
+	wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
+
+	buflen = os_strlen(url) + 100;
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		os_free(req);
+		return -1;
+	}
+
+	if (ctx->client_cert_present) {
+		os_snprintf(buf, buflen, "%s/simplereenroll", url);
+		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+			    "SP/%s/client-cert.pem", ctx->fqdn);
+		client_cert = client_cert_buf;
+		os_snprintf(client_key_buf, sizeof(client_key_buf),
+			    "SP/%s/client-key.pem", ctx->fqdn);
+		client_key = client_key_buf;
+	} else
+		os_snprintf(buf, buflen, "%s/simpleenroll", url);
+	wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
+	write_summary(ctx, "EST simpleenroll URL: %s", buf);
+	ctx->no_osu_cert_validation = 1;
+	http_ocsp_set(ctx->http, 1);
+	resp = http_post(ctx->http, buf, req, "application/pkcs10",
+			 "Content-Transfer-Encoding: base64",
+			 ctx->ca_fname, user, pw, client_cert, client_key,
+			 &resp_len);
+	http_ocsp_set(ctx->http,
+		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+	ctx->no_osu_cert_validation = 0;
+	os_free(buf);
+	if (resp == NULL) {
+		wpa_printf(MSG_INFO, "EST certificate enrollment failed");
+		write_result(ctx, "EST certificate enrollment failed");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
+	f = fopen("Cert/est-resp.raw", "w");
+	if (f) {
+		fwrite(resp, resp_len, 1, f);
+		fclose(f);
+	}
+
+	pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+	if (pkcs7 == NULL) {
+		wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+		pkcs7 = os_malloc(resp_len);
+		if (pkcs7) {
+			os_memcpy(pkcs7, resp, resp_len);
+			pkcs7_len = resp_len;
+		}
+	}
+	os_free(resp);
+
+	if (pkcs7 == NULL) {
+		wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
+		write_result(ctx, "Failed to parse EST simpleenroll base64 response");
+		return -1;
+	}
+
+	res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
+			    "Cert/est_cert.der");
+	os_free(pkcs7);
+
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
+		write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
+		   ctx->client_cert_present ? "re" : "");
+	write_summary(ctx, "EST simple%senroll completed successfully",
+		      ctx->client_cert_present ? "re" : "");
+
+	return 0;
+}
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
new file mode 100644
index 0000000..82e9106
--- /dev/null
+++ b/hs20/client/oma_dm_client.c
@@ -0,0 +1,1370 @@
+/*
+ * Hotspot 2.0 - OMA DM client
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/browser.h"
+#include "osu_client.h"
+
+
+#define DM_SERVER_INITIATED_MGMT 1200
+#define DM_CLIENT_INITIATED_MGMT 1201
+#define DM_GENERIC_ALERT 1226
+
+/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
+#define DM_RESP_OK 200
+#define DM_RESP_AUTH_ACCEPTED 212
+#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
+#define DM_RESP_NOT_EXECUTED 215
+#define DM_RESP_ATOMIC_ROLL_BACK_OK 216
+#define DM_RESP_NOT_MODIFIED 304
+#define DM_RESP_BAD_REQUEST 400
+#define DM_RESP_UNAUTHORIZED 401
+#define DM_RESP_FORBIDDEN 403
+#define DM_RESP_NOT_FOUND 404
+#define DM_RESP_COMMAND_NOT_ALLOWED 405
+#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
+#define DM_RESP_MISSING_CREDENTIALS 407
+#define DM_RESP_CONFLICT 409
+#define DM_RESP_GONE 410
+#define DM_RESP_INCOMPLETE_COMMAND 412
+#define DM_RESP_REQ_ENTITY_TOO_LARGE 413
+#define DM_RESP_URI_TOO_LONG 414
+#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
+#define DM_RESP_REQ_TOO_BIG 416
+#define DM_RESP_ALREADY_EXISTS 418
+#define DM_RESP_DEVICE_FULL 420
+#define DM_RESP_SIZE_MISMATCH 424
+#define DM_RESP_PERMISSION_DENIED 425
+#define DM_RESP_COMMAND_FAILED 500
+#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
+#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
+
+#define DM_HS20_SUBSCRIPTION_CREATION \
+	"org.wi-fi.hotspot2dot0.SubscriptionCreation"
+#define DM_HS20_SUBSCRIPTION_PROVISIONING \
+	"org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
+#define DM_HS20_SUBSCRIPTION_REMEDIATION \
+	"org.wi-fi.hotspot2dot0.SubscriptionRemediation"
+#define DM_HS20_POLICY_UPDATE \
+	"org.wi-fi.hotspot2dot0.PolicyUpdate"
+
+#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
+#define DM_URI_LAUNCH_BROWSER \
+	"./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+		     const char *locuri, const char *data);
+
+
+static const char * int2str(int val)
+{
+	static char buf[20];
+	snprintf(buf, sizeof(buf), "%d", val);
+	return buf;
+}
+
+
+static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
+				       xml_node_t *node)
+{
+	xml_node_t *locuri;
+	char *uri, *ret = NULL;
+
+	locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
+	if (locuri == NULL)
+		return NULL;
+
+	uri = xml_node_get_text(ctx->xml, locuri);
+	if (uri)
+		ret = os_strdup(uri);
+	xml_node_get_text_free(ctx->xml, uri);
+	return ret;
+}
+
+
+static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
+			      const char *element, const char *uri)
+{
+	xml_node_t *node;
+
+	node = xml_node_create(ctx->xml, parent, NULL, element);
+	if (node == NULL)
+		return;
+	xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
+}
+
+
+static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
+				     const char *url, int msgid)
+{
+	xml_node_t *syncml, *synchdr;
+	xml_namespace_t *ns;
+
+	syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
+				      "SyncML");
+
+	synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
+	xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
+	xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
+	xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
+	xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
+
+	oma_dm_add_locuri(ctx, synchdr, "Target", url);
+	oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
+
+	return syncml;
+}
+
+
+static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
+			     int cmdid)
+{
+	xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
+}
+
+
+static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
+			      int cmdid, int data)
+{
+	xml_node_t *node;
+
+	node = xml_node_create(ctx->xml, parent, NULL, "Alert");
+	if (node == NULL)
+		return NULL;
+	oma_dm_add_cmdid(ctx, node, cmdid);
+	xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+
+	return node;
+}
+
+
+static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
+			       int msgref, int cmdref, int cmdid,
+			       const char *cmd, int data, const char *targetref)
+{
+	xml_node_t *node;
+
+	node = xml_node_create(ctx->xml, parent, NULL, "Status");
+	if (node == NULL)
+		return NULL;
+	oma_dm_add_cmdid(ctx, node, cmdid);
+	xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+	if (cmdref)
+		xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
+				     int2str(cmdref));
+	xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
+	xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+	if (targetref) {
+		xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
+				     targetref);
+	}
+
+	return node;
+}
+
+
+static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
+				int msgref, int cmdref, int cmdid,
+				const char *locuri, const char *data)
+{
+	xml_node_t *node;
+
+	node = xml_node_create(ctx->xml, parent, NULL, "Results");
+	if (node == NULL)
+		return NULL;
+
+	oma_dm_add_cmdid(ctx, node, cmdid);
+	xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+	xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
+	add_item(ctx, node, locuri, data);
+
+	return node;
+}
+
+
+static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
+		     const char *fname)
+{
+	xml_node_t *fnode, *tnds;
+	char *str;
+
+	fnode = node_from_file(ctx->xml, fname);
+	if (!fnode)
+		return NULL;
+	tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
+	xml_node_free(ctx->xml, fnode);
+	if (!tnds)
+		return NULL;
+
+	str = xml_node_to_str(ctx->xml, tnds);
+	xml_node_free(ctx->xml, tnds);
+	if (str == NULL)
+		return NULL;
+	wpa_printf(MSG_INFO, "MgmtTree: %s", str);
+
+	return str;
+}
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+		     const char *locuri, const char *data)
+{
+	xml_node_t *item, *node;
+
+	item = xml_node_create(ctx->xml, parent, NULL, "Item");
+	oma_dm_add_locuri(ctx, item, "Source", locuri);
+	node = xml_node_create(ctx->xml, item, NULL, "Meta");
+	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+				"Chr");
+	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
+				"text/plain");
+	xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
+				int cmdid)
+{
+	xml_node_t *info, *child, *replace;
+	const char *name;
+	char locuri[200], *txt;
+
+	info = node_from_file(ctx->xml, "devinfo.xml");
+	if (info == NULL) {
+		wpa_printf(MSG_INFO, "Could not read devinfo.xml");
+		return;
+	}
+
+	replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
+	if (replace == NULL) {
+		xml_node_free(ctx->xml, info);
+		return;
+	}
+	oma_dm_add_cmdid(ctx, replace, cmdid);
+
+	xml_node_for_each_child(ctx->xml, child, info) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
+		txt = xml_node_get_text(ctx->xml, child);
+		if (txt) {
+			add_item(ctx, replace, locuri, txt);
+			xml_node_get_text_free(ctx->xml, txt);
+		}
+	}
+
+	xml_node_free(ctx->xml, info);
+}
+
+
+static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
+					  xml_node_t *syncbody,
+					  int cmdid, const char *oper,
+					  const char *data)
+{
+	xml_node_t *node, *item;
+	char buf[200];
+
+	node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
+
+	item = xml_node_create(ctx->xml, node, NULL, "Item");
+	oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
+	node = xml_node_create(ctx->xml, item, NULL, "Meta");
+	snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
+	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
+	xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+				"xml");
+	xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
+				   const char *url, int msgid, const char *oper)
+{
+	xml_node_t *syncml, *syncbody;
+	char *str;
+	int cmdid = 0;
+
+	syncml = oma_dm_build_hdr(ctx, url, msgid);
+	if (syncml == NULL)
+		return NULL;
+
+	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+	if (syncbody == NULL) {
+		xml_node_free(ctx->xml, syncml);
+		return NULL;
+	}
+
+	cmdid++;
+	add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
+
+	str = mo_str(ctx, NULL, "devdetail.xml");
+	if (str == NULL) {
+		xml_node_free(ctx->xml, syncml);
+		return NULL;
+	}
+	cmdid++;
+	oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
+	os_free(str);
+
+	cmdid++;
+	add_replace_devinfo(ctx, syncbody, cmdid);
+
+	xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+	return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
+					   const char *url, int msgid)
+{
+	xml_node_t *syncml;
+
+	syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
+	if (syncml)
+		debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
+
+	return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
+					    const char *url, int msgid)
+{
+	xml_node_t *syncml;
+
+	syncml = build_oma_dm_1(ctx, url, msgid,
+				DM_HS20_SUBSCRIPTION_PROVISIONING);
+	if (syncml)
+		debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
+
+	return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
+					   const char *url, int msgid)
+{
+	xml_node_t *syncml;
+
+	syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
+	if (syncml)
+		debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
+
+	return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
+					   const char *url, int msgid)
+{
+	xml_node_t *syncml;
+
+	syncml = build_oma_dm_1(ctx, url, msgid,
+				DM_HS20_SUBSCRIPTION_REMEDIATION);
+	if (syncml)
+		debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
+
+	return syncml;
+}
+
+
+static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+	xml_node_t *node;
+	char *data;
+	int res;
+
+	node = get_node(ctx->xml, exec, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Data node found");
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	data = xml_node_get_text(ctx->xml, node);
+	wpa_printf(MSG_INFO, "Data: %s", data);
+	wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
+	write_summary(ctx, "Launch browser to URI '%s'", data);
+	res = hs20_web_browser(data);
+	xml_node_get_text_free(ctx->xml, data);
+	if (res > 0) {
+		wpa_printf(MSG_INFO, "User response in browser completed successfully");
+		write_summary(ctx, "User response in browser completed successfully");
+		return DM_RESP_OK;
+	} else {
+		wpa_printf(MSG_INFO, "Failed to receive user response");
+		write_summary(ctx, "Failed to receive user response");
+		return DM_RESP_COMMAND_FAILED;
+	}
+}
+
+
+static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+	xml_node_t *node, *getcert;
+	char *data;
+	const char *name;
+	int res;
+
+	wpa_printf(MSG_INFO, "Client certificate enrollment");
+	write_summary(ctx, "Client certificate enrollment");
+
+	node = get_node(ctx->xml, exec, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Data node found");
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	data = xml_node_get_text(ctx->xml, node);
+	wpa_printf(MSG_INFO, "Data: %s", data);
+	getcert = xml_node_from_buf(ctx->xml, data);
+	xml_node_get_text_free(ctx->xml, data);
+
+	if (getcert == NULL) {
+		wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
+
+	name = xml_node_get_localname(ctx->xml, getcert);
+	if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
+		wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
+			   name);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	res = osu_get_certificate(ctx, getcert);
+
+	xml_node_free(ctx->xml, getcert);
+
+	return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
+}
+
+
+static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+	char *locuri;
+	int ret;
+
+	locuri = oma_dm_get_target_locuri(ctx, exec);
+	if (locuri == NULL) {
+		wpa_printf(MSG_INFO, "No Target LocURI node found");
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+
+	if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+			  "launchBrowserToURI") == 0) {
+		ret = oma_dm_exec_browser(ctx, exec);
+	} else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+			  "getCertificate") == 0) {
+		ret = oma_dm_exec_get_cert(ctx, exec);
+	} else {
+		wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
+		ret = DM_RESP_NOT_FOUND;
+	}
+	os_free(locuri);
+
+	return ret;
+}
+
+
+static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
+			  xml_node_t *add, xml_node_t *pps,
+			  const char *pps_fname)
+{
+	const char *pos;
+	size_t fqdn_len;
+	xml_node_t *node, *tnds, *unode, *pps_node;
+	char *data, *uri, *upos, *end;
+	int use_tnds = 0;
+	size_t uri_len;
+
+	wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
+
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos = locuri + 8;
+
+	if (ctx->fqdn == NULL)
+		return DM_RESP_COMMAND_FAILED;
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO,
+			   "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
+
+	pps_node = get_node(ctx->xml, pps, pos);
+	if (pps_node) {
+		wpa_printf(MSG_INFO, "Specified PPS node exists already");
+		return DM_RESP_ALREADY_EXISTS;
+	}
+
+	uri = os_strdup(pos);
+	if (uri == NULL)
+		return DM_RESP_COMMAND_FAILED;
+	while (!pps_node) {
+		upos = os_strrchr(uri, '/');
+		if (!upos)
+			break;
+		upos[0] = '\0';
+		pps_node = get_node(ctx->xml, pps, uri);
+		wpa_printf(MSG_INFO, "Node %s %s", uri,
+			   pps_node ? "exists" : "does not exist");
+	}
+
+	wpa_printf(MSG_INFO, "Parent URI: %s", uri);
+
+	if (!pps_node) {
+		/* Add at root of PPS MO */
+		pps_node = pps;
+	}
+
+	uri_len = os_strlen(uri);
+	os_strlcpy(uri, pos + uri_len, os_strlen(pos));
+	upos = uri;
+	while (*upos == '/')
+		upos++;
+	wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
+
+	for (;;) {
+		end = os_strchr(upos, '/');
+		if (!end)
+			break;
+		*end = '\0';
+		wpa_printf(MSG_INFO, "Adding interim node %s", upos);
+		pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
+		if (pps_node == NULL) {
+			os_free(uri);
+			return DM_RESP_COMMAND_FAILED;
+		}
+		upos = end + 1;
+	}
+
+	wpa_printf(MSG_INFO, "Adding node %s", upos);
+
+	node = get_node(ctx->xml, add, "Item/Meta/Type");
+	if (node) {
+		char *type;
+		type = xml_node_get_text(ctx->xml, node);
+		use_tnds = node &&
+			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+	}
+
+	node = get_node(ctx->xml, add, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Add/Item/Data found");
+		os_free(uri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	data = xml_node_get_text(ctx->xml, node);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
+		os_free(uri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
+
+	if (use_tnds) {
+		tnds = xml_node_from_buf(ctx->xml, data);
+		xml_node_get_text_free(ctx->xml, data);
+		if (tnds == NULL) {
+			wpa_printf(MSG_INFO,
+				   "Could not parse Add/Item/Data text");
+			os_free(uri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		unode = tnds_to_mo(ctx->xml, tnds);
+		xml_node_free(ctx->xml, tnds);
+		if (unode == NULL) {
+			wpa_printf(MSG_INFO, "Could not parse TNDS text");
+			os_free(uri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		debug_dump_node(ctx, "Parsed TNDS", unode);
+
+		xml_node_add_child(ctx->xml, pps_node, unode);
+	} else {
+		/* TODO: What to do here? */
+		os_free(uri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	os_free(uri);
+
+	if (update_pps_file(ctx, pps_fname, pps) < 0)
+		return DM_RESP_COMMAND_FAILED;
+
+	ctx->pps_updated = 1;
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
+		      xml_node_t *pps, const char *pps_fname)
+{
+	xml_node_t *node;
+	char *locuri;
+	char fname[300];
+	int ret;
+
+	node = get_node(ctx->xml, add, "Item/Target/LocURI");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Target LocURI node found");
+		return DM_RESP_BAD_REQUEST;
+	}
+	locuri = xml_node_get_text(ctx->xml, node);
+	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
+		xml_node_get_text_free(ctx->xml, locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+
+	node = get_node(ctx->xml, add, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Data node found");
+		xml_node_get_text_free(ctx->xml, locuri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	if (pps_fname && os_file_exists(pps_fname)) {
+		ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
+		if (ret != DM_RESP_OK) {
+			xml_node_get_text_free(ctx->xml, locuri);
+			return ret;
+		}
+		ret = 0;
+		os_strlcpy(fname, pps_fname, sizeof(fname));
+	} else
+		ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
+	xml_node_get_text_free(ctx->xml, locuri);
+	if (ret < 0)
+		return ret == -2 ? DM_RESP_ALREADY_EXISTS :
+			DM_RESP_COMMAND_FAILED;
+
+	if (ctx->no_reconnect == 2) {
+		os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
+			    fname);
+		ctx->pps_cred_set = 1;
+		return DM_RESP_OK;
+	}
+
+	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+	cmd_set_pps(ctx, fname);
+
+	if (ctx->no_reconnect)
+		return DM_RESP_OK;
+
+	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
+			  xml_node_t *pps, const char *pps_fname)
+{
+	char *locuri, *pos;
+	size_t fqdn_len;
+	xml_node_t *node, *tnds, *unode, *pps_node, *parent;
+	char *data;
+	int use_tnds = 0;
+
+	locuri = oma_dm_get_target_locuri(ctx, replace);
+	if (locuri == NULL)
+		return DM_RESP_BAD_REQUEST;
+
+	wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos = locuri + 8;
+
+	if (ctx->fqdn == NULL) {
+		os_free(locuri);
+		return DM_RESP_COMMAND_FAILED;
+	}
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO,
+			   "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
+
+	pps_node = get_node(ctx->xml, pps, pos);
+	if (pps_node == NULL) {
+		wpa_printf(MSG_INFO, "Specified PPS node not found");
+		os_free(locuri);
+		return DM_RESP_NOT_FOUND;
+	}
+
+	node = get_node(ctx->xml, replace, "Item/Meta/Type");
+	if (node) {
+		char *type;
+		type = xml_node_get_text(ctx->xml, node);
+		use_tnds = node &&
+			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+	}
+
+	node = get_node(ctx->xml, replace, "Item/Data");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No Replace/Item/Data found");
+		os_free(locuri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	data = xml_node_get_text(ctx->xml, node);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
+		os_free(locuri);
+		return DM_RESP_BAD_REQUEST;
+	}
+
+	wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
+
+	if (use_tnds) {
+		tnds = xml_node_from_buf(ctx->xml, data);
+		xml_node_get_text_free(ctx->xml, data);
+		if (tnds == NULL) {
+			wpa_printf(MSG_INFO,
+				   "Could not parse Replace/Item/Data text");
+			os_free(locuri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		unode = tnds_to_mo(ctx->xml, tnds);
+		xml_node_free(ctx->xml, tnds);
+		if (unode == NULL) {
+			wpa_printf(MSG_INFO, "Could not parse TNDS text");
+			os_free(locuri);
+			return DM_RESP_BAD_REQUEST;
+		}
+
+		debug_dump_node(ctx, "Parsed TNDS", unode);
+
+		parent = xml_node_get_parent(ctx->xml, pps_node);
+		xml_node_detach(ctx->xml, pps_node);
+		xml_node_add_child(ctx->xml, parent, unode);
+	} else {
+		xml_node_set_text(ctx->xml, pps_node, data);
+		xml_node_get_text_free(ctx->xml, data);
+	}
+
+	os_free(locuri);
+
+	if (update_pps_file(ctx, pps_fname, pps) < 0)
+		return DM_RESP_COMMAND_FAILED;
+
+	ctx->pps_updated = 1;
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
+		      xml_node_t *pps, const char *pps_fname, char **value)
+{
+	char *locuri, *pos;
+	size_t fqdn_len;
+	xml_node_t *pps_node;
+	const char *name;
+
+	*value = NULL;
+
+	locuri = oma_dm_get_target_locuri(ctx, get);
+	if (locuri == NULL)
+		return DM_RESP_BAD_REQUEST;
+
+	wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
+	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos = locuri + 8;
+
+	if (ctx->fqdn == NULL)
+		return DM_RESP_COMMAND_FAILED;
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO,
+			   "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
+
+	pps_node = get_node(ctx->xml, pps, pos);
+	if (pps_node == NULL) {
+		wpa_printf(MSG_INFO, "Specified PPS node not found");
+		os_free(locuri);
+		return DM_RESP_NOT_FOUND;
+	}
+
+	name = xml_node_get_localname(ctx->xml, pps_node);
+	wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
+	if (os_strcasecmp(name, "Password") == 0) {
+		wpa_printf(MSG_INFO, "Do not allow Get for Password node");
+		os_free(locuri);
+		return DM_RESP_PERMISSION_DENIED;
+	}
+
+	/*
+	 * TODO: No support for DMTNDS, so if interior node, reply with a
+	 * list of children node names in Results element. The child list type is
+	 * defined in [DMTND].
+	 */
+
+	*value = xml_node_get_text(ctx->xml, pps_node);
+	if (*value == NULL)
+		return DM_RESP_COMMAND_FAILED;
+
+	return DM_RESP_OK;
+}
+
+
+static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
+{
+	xml_node_t *cnode;
+	char *str;
+	int ret;
+
+	cnode = get_node(ctx->xml, node, "CmdID");
+	if (cnode == NULL)
+		return 0;
+
+	str = xml_node_get_text(ctx->xml, cnode);
+	if (str == NULL)
+		return 0;
+	ret = atoi(str);
+	xml_node_get_text_free(ctx->xml, str);
+	return ret;
+}
+
+
+static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
+				     const char *url, xml_node_t *syncml,
+				     const char *ext_hdr,
+				     const char *username, const char *password,
+				     const char *client_cert,
+				     const char *client_key)
+{
+	xml_node_t *resp;
+	char *str, *res;
+	char *resp_uri = NULL;
+
+	str = xml_node_to_str(ctx->xml, syncml);
+	xml_node_free(ctx->xml, syncml);
+	if (str == NULL)
+		return NULL;
+
+	wpa_printf(MSG_INFO, "Send OMA DM Package");
+	write_summary(ctx, "Send OMA DM Package");
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(url);
+	res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
+			ext_hdr, ctx->ca_fname, username, password,
+			client_cert, client_key, NULL);
+	os_free(str);
+	os_free(resp_uri);
+	resp_uri = NULL;
+
+	if (res == NULL) {
+		const char *err = http_get_err(ctx->http);
+		if (err) {
+			wpa_printf(MSG_INFO, "HTTP error: %s", err);
+			write_result(ctx, "HTTP error: %s", err);
+		} else {
+			write_summary(ctx, "Failed to send OMA DM Package");
+		}
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "Server response: %s", res);
+
+	wpa_printf(MSG_INFO, "Process OMA DM Package");
+	write_summary(ctx, "Process received OMA DM Package");
+	resp = xml_node_from_buf(ctx->xml, res);
+	os_free(res);
+	if (resp == NULL) {
+		wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
+		return NULL;
+	}
+
+	debug_dump_node(ctx, "OMA DM Package", resp);
+
+	return resp;
+}
+
+
+static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
+				   xml_node_t *resp, int msgid,
+				   char **ret_resp_uri,
+				   xml_node_t *pps, const char *pps_fname)
+{
+	xml_node_t *syncml, *syncbody, *hdr, *body, *child;
+	const char *name;
+	char *resp_uri = NULL;
+	int server_msgid = 0;
+	int cmdid = 0;
+	int server_cmdid;
+	int resp_needed = 0;
+	char *tmp;
+	int final = 0;
+	char *locuri;
+
+	*ret_resp_uri = NULL;
+
+	name = xml_node_get_localname(ctx->xml, resp);
+	if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
+		wpa_printf(MSG_INFO, "SyncML node not found");
+		return NULL;
+	}
+
+	hdr = get_node(ctx->xml, resp, "SyncHdr");
+	body = get_node(ctx->xml, resp, "SyncBody");
+	if (hdr == NULL || body == NULL) {
+		wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
+		return NULL;
+	}
+
+	xml_node_for_each_child(ctx->xml, child, hdr) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		wpa_printf(MSG_INFO, "SyncHdr %s", name);
+		if (os_strcasecmp(name, "RespURI") == 0) {
+			tmp = xml_node_get_text(ctx->xml, child);
+			if (tmp)
+				resp_uri = os_strdup(tmp);
+			xml_node_get_text_free(ctx->xml, tmp);
+		} else if (os_strcasecmp(name, "MsgID") == 0) {
+			tmp = xml_node_get_text(ctx->xml, child);
+			if (tmp)
+				server_msgid = atoi(tmp);
+			xml_node_get_text_free(ctx->xml, tmp);
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
+	if (resp_uri)
+		wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
+
+	syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
+	if (syncml == NULL) {
+		os_free(resp_uri);
+		return NULL;
+	}
+
+	syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+	cmdid++;
+	add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
+		   DM_RESP_AUTH_ACCEPTED, NULL);
+
+	xml_node_for_each_child(ctx->xml, child, body) {
+		xml_node_for_each_check(ctx->xml, child);
+		server_cmdid = oma_dm_get_cmdid(ctx, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
+			   server_cmdid, name);
+		if (os_strcasecmp(name, "Exec") == 0) {
+			int res = oma_dm_exec(ctx, child);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		} else if (os_strcasecmp(name, "Add") == 0) {
+			int res = oma_dm_add(ctx, child, pps, pps_fname);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		} else if (os_strcasecmp(name, "Replace") == 0) {
+			int res;
+			res = oma_dm_replace(ctx, child, pps, pps_fname);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		} else if (os_strcasecmp(name, "Status") == 0) {
+			/* TODO: Verify success */
+		} else if (os_strcasecmp(name, "Get") == 0) {
+			int res;
+			char *value;
+			res = oma_dm_get(ctx, child, pps, pps_fname, &value);
+			cmdid++;
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			if (locuri == NULL)
+				res = DM_RESP_BAD_REQUEST;
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, res, locuri);
+			if (res == DM_RESP_OK && value) {
+				cmdid++;
+				add_results(ctx, syncbody, server_msgid,
+					    server_cmdid, cmdid, locuri, value);
+			}
+			os_free(locuri);
+			xml_node_get_text_free(ctx->xml, value);
+			resp_needed = 1;
+#if 0 /* TODO: MUST support */
+		} else if (os_strcasecmp(name, "Delete") == 0) {
+#endif
+#if 0 /* TODO: MUST support */
+		} else if (os_strcasecmp(name, "Sequence") == 0) {
+#endif
+		} else if (os_strcasecmp(name, "Final") == 0) {
+			final = 1;
+			break;
+		} else {
+			locuri = oma_dm_get_target_locuri(ctx, child);
+			add_status(ctx, syncbody, server_msgid, server_cmdid,
+				   cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
+				   locuri);
+			os_free(locuri);
+			resp_needed = 1;
+		}
+	}
+
+	if (!final) {
+		wpa_printf(MSG_INFO, "Final node not found");
+		xml_node_free(ctx->xml, syncml);
+		os_free(resp_uri);
+		return NULL;
+	}
+
+	if (!resp_needed) {
+		wpa_printf(MSG_INFO, "Exchange completed - no response needed");
+		xml_node_free(ctx->xml, syncml);
+		os_free(resp_uri);
+		return NULL;
+	}
+
+	xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+	debug_dump_node(ctx, "OMA-DM Package 3", syncml);
+
+	*ret_resp_uri = resp_uri;
+	return syncml;
+}
+
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
+	write_summary(ctx, "OMA-DM credential provisioning");
+
+	msgid++;
+	syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
+	if (syncml == NULL)
+		return -1;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+					syncml, NULL, NULL, NULL, NULL, NULL);
+		if (resp == NULL)
+			return -1;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+					NULL, NULL);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
+	ctx->no_reconnect = 2;
+
+	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+	write_summary(ctx, "Wait for IP address before starting SIM provisioning");
+
+	if (wait_ip_addr(ctx->ifname, 15) < 0) {
+		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+	}
+	write_summary(ctx, "OMA-DM SIM provisioning");
+
+	msgid++;
+	syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
+	if (syncml == NULL)
+		return -1;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+					syncml, NULL, NULL, NULL, NULL, NULL);
+		if (resp == NULL)
+			return -1;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+					NULL, NULL);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	if (ctx->pps_cred_set) {
+		wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+		cmd_set_pps(ctx, ctx->pps_fname);
+
+		wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+		write_summary(ctx, "Requesting reconnection with updated configuration");
+		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+			wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+			write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+			return -1;
+		}
+	}
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+		    const char *pps_fname,
+		    const char *client_cert, const char *client_key,
+		    const char *cred_username, const char *cred_password,
+		    xml_node_t *pps)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	wpa_printf(MSG_INFO, "OMA-DM policy update");
+	write_summary(ctx, "OMA-DM policy update");
+
+	msgid++;
+	syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
+	if (syncml == NULL)
+		return;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+					syncml, NULL, cred_username,
+					cred_password, client_cert, client_key);
+		if (resp == NULL)
+			return;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+					pps, pps_fname);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	if (ctx->pps_updated) {
+		wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
+		write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
+		cmd_set_pps(ctx, pps_fname);
+		if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+			wpa_printf(MSG_INFO,
+				   "Failed to request wpa_supplicant to reconnect");
+			write_summary(ctx,
+				      "Failed to request wpa_supplicant to reconnect");
+		}
+	}
+}
+
+
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+		    const char *pps_fname,
+		    const char *client_cert, const char *client_key,
+		    const char *cred_username, const char *cred_password,
+		    xml_node_t *pps)
+{
+	xml_node_t *syncml, *resp;
+	char *resp_uri = NULL;
+	int msgid = 0;
+
+	wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
+	write_summary(ctx, "OMA-DM subscription remediation");
+
+	msgid++;
+	syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
+	if (syncml == NULL)
+		return;
+
+	while (syncml) {
+		resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+					syncml, NULL, cred_username,
+					cred_password, client_cert, client_key);
+		if (resp == NULL)
+			return;
+
+		msgid++;
+		syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+					pps, pps_fname);
+		xml_node_free(ctx->xml, resp);
+	}
+
+	os_free(resp_uri);
+
+	wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+	write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+	cmd_set_pps(ctx, pps_fname);
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+	}
+}
+
+
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+		    const char *add_fname)
+{
+	xml_node_t *pps, *add;
+	int res;
+
+	ctx->fqdn = os_strdup("wi-fi.org");
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+			   pps_fname);
+		return;
+	}
+
+	add = node_from_file(ctx->xml, add_fname);
+	if (add == NULL) {
+		wpa_printf(MSG_INFO, "Add file %s could not be parsed",
+			   add_fname);
+		xml_node_free(ctx->xml, pps);
+		return;
+	}
+
+	res = oma_dm_add(ctx, add, pps, pps_fname);
+	wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
+
+	xml_node_free(ctx->xml, pps);
+	xml_node_free(ctx->xml, add);
+}
+
+
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+			const char *replace_fname)
+{
+	xml_node_t *pps, *replace;
+	int res;
+
+	ctx->fqdn = os_strdup("wi-fi.org");
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+			   pps_fname);
+		return;
+	}
+
+	replace = node_from_file(ctx->xml, replace_fname);
+	if (replace == NULL) {
+		wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
+			   replace_fname);
+		xml_node_free(ctx->xml, pps);
+		return;
+	}
+
+	res = oma_dm_replace(ctx, replace, pps, pps_fname);
+	wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
+
+	xml_node_free(ctx->xml, pps);
+	xml_node_free(ctx->xml, replace);
+}
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
new file mode 100644
index 0000000..a439bde
--- /dev/null
+++ b/hs20/client/osu_client.c
@@ -0,0 +1,3203 @@
+/*
+ * Hotspot 2.0 OSU client
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sys/stat.h>
+
+#include "common.h"
+#include "utils/browser.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "common/wpa_ctrl.h"
+#include "common/wpa_helpers.h"
+#include "eap_common/eap_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+	va_list ap;
+	FILE *f;
+	char buf[500];
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+	write_summary(ctx, "%s", buf);
+
+	if (!ctx->result_file)
+		return;
+
+	f = fopen(ctx->result_file, "w");
+	if (f == NULL)
+		return;
+
+	va_start(ap, fmt);
+	vfprintf(f, fmt, ap);
+	va_end(ap);
+	fprintf(f, "\n");
+	fclose(f);
+}
+
+
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+	va_list ap;
+	FILE *f;
+
+	if (!ctx->summary_file)
+		return;
+
+	f = fopen(ctx->summary_file, "a");
+	if (f == NULL)
+		return;
+
+	va_start(ap, fmt);
+	vfprintf(f, fmt, ap);
+	va_end(ap);
+	fprintf(f, "\n");
+	fclose(f);
+}
+
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+		     xml_node_t *node)
+{
+	char *str = xml_node_to_str(ctx->xml, node);
+	wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
+	free(str);
+}
+
+
+static int valid_fqdn(const char *fqdn)
+{
+	const char *pos;
+
+	/* TODO: could make this more complete.. */
+	if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
+		return 0;
+	for (pos = fqdn; *pos; pos++) {
+		if (*pos >= 'a' && *pos <= 'z')
+			continue;
+		if (*pos >= 'A' && *pos <= 'Z')
+			continue;
+		if (*pos >= '0' && *pos <= '9')
+			continue;
+		if (*pos == '-' || *pos == '.' || *pos == '_')
+			continue;
+		return 0;
+	}
+	return 1;
+}
+
+
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
+{
+	xml_node_t *node;
+	char *url, *user = NULL, *pw = NULL;
+	char *proto;
+	int ret = -1;
+
+	proto = xml_node_get_attr_value(ctx->xml, getcert,
+					"enrollmentProtocol");
+	if (!proto)
+		return -1;
+	wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
+	write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
+	if (os_strcasecmp(proto, "EST") != 0) {
+		wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
+		xml_node_get_attr_value_free(ctx->xml, proto);
+		return -1;
+	}
+	xml_node_get_attr_value_free(ctx->xml, proto);
+
+	node = get_node(ctx->xml, getcert, "enrollmentServerURI");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
+		xml_node_get_attr_value_free(ctx->xml, proto);
+		return -1;
+	}
+	url = xml_node_get_text(ctx->xml, node);
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Could not get URL text");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
+	write_summary(ctx, "enrollmentServerURI: %s", url);
+
+	node = get_node(ctx->xml, getcert, "estUserID");
+	if (node == NULL && !ctx->client_cert_present) {
+		wpa_printf(MSG_INFO, "Could not find estUserID node");
+		goto fail;
+	}
+	if (node) {
+		user = xml_node_get_text(ctx->xml, node);
+		if (user == NULL) {
+			wpa_printf(MSG_INFO, "Could not get estUserID text");
+			goto fail;
+		}
+		wpa_printf(MSG_INFO, "estUserID: %s", user);
+		write_summary(ctx, "estUserID: %s", user);
+	}
+
+	node = get_node(ctx->xml, getcert, "estPassword");
+	if (node == NULL && !ctx->client_cert_present) {
+		wpa_printf(MSG_INFO, "Could not find estPassword node");
+		goto fail;
+	}
+	if (node) {
+		pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+		if (pw == NULL) {
+			wpa_printf(MSG_INFO, "Could not get estPassword text");
+			goto fail;
+		}
+		wpa_printf(MSG_INFO, "estPassword: %s", pw);
+	}
+
+	mkdir("Cert", S_IRWXU);
+	if (est_load_cacerts(ctx, url) < 0 ||
+	    est_build_csr(ctx, url) < 0 ||
+	    est_simple_enroll(ctx, url, user, pw) < 0)
+		goto fail;
+
+	ret = 0;
+fail:
+	xml_node_get_text_free(ctx->xml, url);
+	xml_node_get_text_free(ctx->xml, user);
+	xml_node_get_text_free(ctx->xml, pw);
+
+	return ret;
+}
+
+
+static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
+			    const char *fqdn)
+{
+	u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+	char *der, *pem;
+	size_t der_len, pem_len;
+	char *fingerprint;
+	char buf[200];
+
+	wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
+
+	fingerprint = xml_node_get_text(ctx->xml, cert);
+	if (fingerprint == NULL)
+		return -1;
+	if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
+		wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+		write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
+		xml_node_get_text_free(ctx->xml, fingerprint);
+		return -1;
+	}
+	xml_node_get_text_free(ctx->xml, fingerprint);
+
+	der = os_readfile("Cert/est_cert.der", &der_len);
+	if (der == NULL) {
+		wpa_printf(MSG_INFO, "Could not find client certificate from EST");
+		write_result(ctx, "Could not find client certificate from EST");
+		return -1;
+	}
+
+	if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
+		os_free(der);
+		return -1;
+	}
+	os_free(der);
+
+	if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+		wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
+		write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
+	unlink("Cert/est_cert.der");
+
+	os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
+	if (rename("Cert/est-cacerts.pem", buf) < 0) {
+		wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
+			   strerror(errno));
+		return -1;
+	}
+	pem = os_readfile(buf, &pem_len);
+
+	os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
+	if (rename("Cert/est_cert.pem", buf) < 0) {
+		wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
+			   strerror(errno));
+		os_free(pem);
+		return -1;
+	}
+
+	if (pem) {
+		FILE *f = fopen(buf, "a");
+		if (f) {
+			fwrite(pem, pem_len, 1, f);
+			fclose(f);
+		}
+		os_free(pem);
+	}
+
+	os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
+	if (rename("Cert/privkey-plain.pem", buf) < 0) {
+		wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	unlink("Cert/est-req.b64");
+	unlink("Cert/est-req.pem");
+	unlink("Cert/est-resp.raw");
+	rmdir("Cert");
+
+	return 0;
+}
+
+
+#define TMP_CERT_DL_FILE "tmp-cert-download"
+
+static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
+			 const char *fname)
+{
+	xml_node_t *url_node, *hash_node;
+	char *url, *hash;
+	char *cert;
+	size_t len;
+	u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+	int res;
+	unsigned char *b64;
+	FILE *f;
+
+	url_node = get_node(ctx->xml, params, "CertURL");
+	hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint");
+	if (url_node == NULL || hash_node == NULL)
+		return -1;
+	url = xml_node_get_text(ctx->xml, url_node);
+	hash = xml_node_get_text(ctx->xml, hash_node);
+	if (url == NULL || hash == NULL) {
+		xml_node_get_text_free(ctx->xml, url);
+		xml_node_get_text_free(ctx->xml, hash);
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "CertURL: %s", url);
+	wpa_printf(MSG_INFO, "SHA256 hash: %s", hash);
+
+	if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) {
+		wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+		write_result(ctx, "Invalid SHA256 hash value for downloaded certificate");
+		xml_node_get_text_free(ctx->xml, hash);
+		return -1;
+	}
+	xml_node_get_text_free(ctx->xml, hash);
+
+	write_summary(ctx, "Download certificate from %s", url);
+	ctx->no_osu_cert_validation = 1;
+	http_ocsp_set(ctx->http, 1);
+	res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
+	http_ocsp_set(ctx->http,
+		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+	ctx->no_osu_cert_validation = 0;
+	xml_node_get_text_free(ctx->xml, url);
+	if (res < 0)
+		return -1;
+
+	cert = os_readfile(TMP_CERT_DL_FILE, &len);
+	remove(TMP_CERT_DL_FILE);
+	if (cert == NULL)
+		return -1;
+
+	if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) {
+		os_free(cert);
+		return -1;
+	}
+
+	if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+		wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match");
+		write_result(ctx, "Downloaded certificate fingerprint did not match");
+		os_free(cert);
+		return -1;
+	}
+
+	b64 = base64_encode((unsigned char *) cert, len, NULL);
+	os_free(cert);
+	if (b64 == NULL)
+		return -1;
+
+	f = fopen(fname, "wb");
+	if (f == NULL) {
+		os_free(b64);
+		return -1;
+	}
+
+	fprintf(f, "-----BEGIN CERTIFICATE-----\n"
+		"%s"
+		"-----END CERTIFICATE-----\n",
+		b64);
+
+	os_free(b64);
+	fclose(f);
+
+	wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint",
+		   fname);
+	write_summary(ctx, "Downloaded certificate into %s and validated fingerprint",
+		      fname);
+
+	return 0;
+}
+
+
+static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+			 const char *ca_fname)
+{
+	xml_node_t *pps, *node;
+	int ret;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+		return -1;
+	}
+
+	node = get_child_node(ctx->xml, pps,
+			      "SubscriptionUpdate/TrustRoot");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
+		xml_node_free(ctx->xml, pps);
+		return -1;
+	}
+
+	ret = download_cert(ctx, node, ca_fname);
+	xml_node_free(ctx->xml, pps);
+
+	return ret;
+}
+
+
+static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+			    const char *ca_fname)
+{
+	xml_node_t *pps, *node;
+	int ret;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+		return -1;
+	}
+
+	node = get_child_node(ctx->xml, pps,
+			      "PolicyUpdate/TrustRoot");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No PolicyUpdate/TrustRoot/CertURL found from PPS");
+		xml_node_free(ctx->xml, pps);
+		return -1;
+	}
+
+	ret = download_cert(ctx, node, ca_fname);
+	xml_node_free(ctx->xml, pps);
+
+	return ret;
+}
+
+
+static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+			 const char *ca_fname)
+{
+	xml_node_t *pps, *node, *aaa;
+	int ret;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+		return -1;
+	}
+
+	node = get_child_node(ctx->xml, pps,
+			      "AAAServerTrustRoot");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+		xml_node_free(ctx->xml, pps);
+		return -1;
+	}
+
+	aaa = xml_node_first_child(ctx->xml, node);
+	if (aaa == NULL) {
+		wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+		xml_node_free(ctx->xml, pps);
+		return -1;
+	}
+
+	ret = download_cert(ctx, aaa, ca_fname);
+	xml_node_free(ctx->xml, pps);
+
+	return ret;
+}
+
+
+static int download_trust_roots(struct hs20_osu_client *ctx,
+				const char *pps_fname)
+{
+	char *dir, *pos;
+	char fname[300];
+	int ret;
+
+	dir = os_strdup(pps_fname);
+	if (dir == NULL)
+		return -1;
+	pos = os_strrchr(dir, '/');
+	if (pos == NULL) {
+		os_free(dir);
+		return -1;
+	}
+	*pos = '\0';
+
+	snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
+	ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
+	snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
+	cmd_dl_polupd_ca(ctx, pps_fname, fname);
+	snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
+	cmd_dl_aaa_ca(ctx, pps_fname, fname);
+
+	os_free(dir);
+
+	return ret;
+}
+
+
+static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
+				       const char *fqdn)
+{
+	size_t match_len, len, i;
+	const char *val;
+
+	match_len = os_strlen(fqdn);
+
+	for (i = 0; i < ctx->server_dnsname_count; i++) {
+		wpa_printf(MSG_INFO,
+			   "Checking suffix match against server dNSName %s",
+			   ctx->server_dnsname[i]);
+		val = ctx->server_dnsname[i];
+		len = os_strlen(val);
+
+		if (match_len > len)
+			continue;
+
+		if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
+			continue; /* no match */
+
+		if (match_len == len)
+			return 1; /* exact match */
+
+		if (val[len - match_len - 1] == '.')
+			return 1; /* full label match completes suffix match */
+
+		/* Reject due to incomplete label match */
+	}
+
+	/* None of the dNSName(s) matched */
+	return 0;
+}
+
+
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+		    xml_node_t *add_mo, char *fname, size_t fname_len)
+{
+	char *str;
+	char *fqdn, *pos;
+	xml_node_t *tnds, *mo, *cert;
+	const char *name;
+	int ret;
+
+	if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
+			   uri);
+		write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
+			     uri);
+		return -1;
+	}
+
+	fqdn = strdup(uri + 8);
+	if (fqdn == NULL)
+		return -1;
+	pos = strchr(fqdn, '/');
+	if (pos) {
+		if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
+			wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
+				   uri);
+			write_result(ctx, "Unsupported location for addMO to "
+				     "add PPS MO (extra directory): '%s'", uri);
+			return -1;
+		}
+		*pos = '\0'; /* remove trailing slash and PPS node name */
+	}
+	wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
+
+	if (!server_dnsname_suffix_match(ctx, fqdn)) {
+		wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+			   fqdn);
+		write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+			     fqdn);
+		free(fqdn);
+		return -1;
+	}
+
+	if (!valid_fqdn(fqdn)) {
+		wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
+		write_result(ctx, "Invalid FQDN '%s'", fqdn);
+		free(fqdn);
+		return -1;
+	}
+
+	mkdir("SP", S_IRWXU);
+	snprintf(fname, fname_len, "SP/%s", fqdn);
+	if (mkdir(fname, S_IRWXU) < 0) {
+		if (errno != EEXIST) {
+			int err = errno;
+			wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+				   fname, strerror(err));
+			free(fqdn);
+			return -1;
+		}
+	}
+
+	snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
+
+	if (os_file_exists(fname)) {
+		wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
+			   fname);
+		write_result(ctx, "PPS file '%s' exists - reject addMO",
+			     fname);
+		free(fqdn);
+		return -2;
+	}
+	wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
+
+	str = xml_node_get_text(ctx->xml, add_mo);
+	if (str == NULL) {
+		wpa_printf(MSG_INFO, "Could not extract MO text");
+		free(fqdn);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
+
+	tnds = xml_node_from_buf(ctx->xml, str);
+	xml_node_get_text_free(ctx->xml, str);
+	if (tnds == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
+		free(fqdn);
+		return -1;
+	}
+
+	mo = tnds_to_mo(ctx->xml, tnds);
+	if (mo == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
+		free(fqdn);
+		return -1;
+	}
+
+	debug_dump_node(ctx, "Parsed TNDS", mo);
+
+	name = xml_node_get_localname(ctx->xml, mo);
+	if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
+		wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
+			   name);
+		free(fqdn);
+		return -1;
+	}
+
+	cert = get_child_node(ctx->xml, mo,
+			      "Credential/DigitalCertificate/"
+			      "CertSHA256Fingerprint");
+	if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
+		xml_node_free(ctx->xml, mo);
+		free(fqdn);
+		return -1;
+	}
+	free(fqdn);
+
+	if (node_to_file(ctx->xml, fname, mo) < 0) {
+		wpa_printf(MSG_INFO, "Could not write MO to file");
+		xml_node_free(ctx->xml, mo);
+		return -1;
+	}
+	xml_node_free(ctx->xml, mo);
+
+	wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
+	write_summary(ctx, "A new PPS MO added as '%s'", fname);
+
+	ret = download_trust_roots(ctx, fname);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
+		write_summary(ctx, "Remove invalid PPS MO file");
+		unlink(fname);
+	}
+
+	return ret;
+}
+
+
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+		    xml_node_t *pps)
+{
+	char *str;
+	FILE *f;
+	char backup[300];
+
+	if (ctx->client_cert_present) {
+		xml_node_t *cert;
+		cert = get_child_node(ctx->xml, pps,
+				      "Credential/DigitalCertificate/"
+				      "CertSHA256Fingerprint");
+		if (cert && os_file_exists("Cert/est_cert.der") &&
+		    process_est_cert(ctx, cert, ctx->fqdn) < 0) {
+			wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
+			return -1;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
+
+	str = xml_node_to_str(ctx->xml, pps);
+	wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
+
+	snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
+	rename(pps_fname, backup);
+	f = fopen(pps_fname, "w");
+	if (f == NULL) {
+		wpa_printf(MSG_INFO, "Could not write PPS");
+		rename(backup, pps_fname);
+		free(str);
+		return -1;
+	}
+	fprintf(f, "%s\n", str);
+	fclose(f);
+
+	free(str);
+
+	return 0;
+}
+
+
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+		 const char *alt_loc, char **user, char **pw)
+{
+	xml_node_t *node;
+
+	node = get_child_node(ctx->xml, pps,
+			      "Credential/UsernamePassword/Username");
+	if (node)
+		*user = xml_node_get_text(ctx->xml, node);
+
+	node = get_child_node(ctx->xml, pps,
+			      "Credential/UsernamePassword/Password");
+	if (node)
+		*pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+
+	node = get_child_node(ctx->xml, pps, alt_loc);
+	if (node) {
+		xml_node_t *a;
+		a = get_node(ctx->xml, node, "Username");
+		if (a) {
+			xml_node_get_text_free(ctx->xml, *user);
+			*user = xml_node_get_text(ctx->xml, a);
+			wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
+		}
+
+		a = get_node(ctx->xml, node, "Password");
+		if (a) {
+			free(*pw);
+			*pw = xml_node_get_base64_text(ctx->xml, a, NULL);
+			wpa_printf(MSG_INFO, "Use OSU password");
+		}
+	}
+}
+
+
+/* Remove old credentials based on HomeSP/FQDN */
+static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
+{
+	char cmd[300];
+	os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
+	if (wpa_command(ctx->ifname, cmd) < 0)
+		wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
+}
+
+
+static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
+				    xml_node_t *spe)
+{
+	xml_node_t *ssid;
+	char *txt;
+
+	ssid = get_node(ctx->xml, spe, "SSID");
+	if (ssid == NULL)
+		return;
+	txt = xml_node_get_text(ctx->xml, ssid);
+	if (txt == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
+	if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
+	xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
+				     xml_node_t *spel)
+{
+	xml_node_t *child;
+
+	xml_node_for_each_child(ctx->xml, child, spel) {
+		xml_node_for_each_check(ctx->xml, child);
+		set_pps_cred_policy_spe(ctx, id, child);
+	}
+}
+
+
+static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
+				    xml_node_t *prp)
+{
+	xml_node_t *node;
+	char *txt = NULL, *pos;
+	char *prio, *country_buf = NULL;
+	const char *country;
+	char val[200];
+	int priority;
+
+	node = get_node(ctx->xml, prp, "Priority");
+	if (node == NULL)
+		return;
+	prio = xml_node_get_text(ctx->xml, node);
+	if (prio == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
+		   prio);
+	priority = atoi(prio);
+	xml_node_get_text_free(ctx->xml, prio);
+
+	node = get_node(ctx->xml, prp, "Country");
+	if (node) {
+		country_buf = xml_node_get_text(ctx->xml, node);
+		if (country_buf == NULL)
+			return;
+		country = country_buf;
+		wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
+			   country);
+	} else {
+		country = "*";
+	}
+
+	node = get_node(ctx->xml, prp, "FQDN_Match");
+	if (node == NULL)
+		goto out;
+	txt = xml_node_get_text(ctx->xml, node);
+	if (txt == NULL)
+		goto out;
+	wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
+		   txt);
+	pos = strrchr(txt, ',');
+	if (pos == NULL)
+		goto out;
+	*pos++ = '\0';
+
+	snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
+		 strcmp(pos, "includeSubdomains") != 0, priority, country);
+	if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
+out:
+	xml_node_get_text_free(ctx->xml, country_buf);
+	xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
+				     xml_node_t *prpl)
+{
+	xml_node_t *child;
+
+	xml_node_for_each_child(ctx->xml, child, prpl) {
+		xml_node_for_each_check(ctx->xml, child);
+		set_pps_cred_policy_prp(ctx, id, child);
+	}
+}
+
+
+static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
+					     xml_node_t *min_backhaul)
+{
+	xml_node_t *node;
+	char *type, *dl = NULL, *ul = NULL;
+	int home;
+
+	node = get_node(ctx->xml, min_backhaul, "NetworkType");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
+		return;
+	}
+
+	type = xml_node_get_text(ctx->xml, node);
+	if (type == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s",
+		   type);
+	if (os_strcasecmp(type, "home") == 0)
+		home = 1;
+	else if (os_strcasecmp(type, "roaming") == 0)
+		home = 0;
+	else {
+		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType");
+		xml_node_get_text_free(ctx->xml, type);
+		return;
+	}
+	xml_node_get_text_free(ctx->xml, type);
+
+	node = get_node(ctx->xml, min_backhaul, "DLBandwidth");
+	if (node)
+		dl = xml_node_get_text(ctx->xml, node);
+
+	node = get_node(ctx->xml, min_backhaul, "ULBandwidth");
+	if (node)
+		ul = xml_node_get_text(ctx->xml, node);
+
+	if (dl == NULL && ul == NULL) {
+		wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes");
+		return;
+	}
+
+	if (dl)
+		wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s",
+			   dl);
+	if (ul)
+		wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s",
+			   ul);
+
+	if (home) {
+		if (dl &&
+		    set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0)
+			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+		if (ul &&
+		    set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0)
+			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+	} else {
+		if (dl &&
+		    set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) <
+		    0)
+			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+		if (ul &&
+		    set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) <
+		    0)
+			wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+	}
+
+	xml_node_get_text_free(ctx->xml, dl);
+	xml_node_get_text_free(ctx->xml, ul);
+}
+
+
+static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx,
+						  int id, xml_node_t *node)
+{
+	xml_node_t *child;
+
+	wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		set_pps_cred_policy_min_backhaul(ctx, id, child);
+	}
+}
+
+
+static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id,
+				       xml_node_t *node)
+{
+	wpa_printf(MSG_INFO, "- Policy/PolicyUpdate");
+	/* Not used in wpa_supplicant */
+}
+
+
+static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx,
+						    int id, xml_node_t *tuple)
+{
+	xml_node_t *node;
+	char *proto, *port;
+	char *buf;
+	size_t buflen;
+
+	node = get_node(ctx->xml, tuple, "IPProtocol");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node");
+		return;
+	}
+
+	proto = xml_node_get_text(ctx->xml, node);
+	if (proto == NULL)
+		return;
+
+	wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s",
+		   proto);
+
+	node = get_node(ctx->xml, tuple, "PortNumber");
+	port = node ? xml_node_get_text(ctx->xml, node) : NULL;
+	if (port) {
+		wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s",
+			   port);
+		buflen = os_strlen(proto) + os_strlen(port) + 10;
+		buf = os_malloc(buflen);
+		if (buf)
+			os_snprintf(buf, buflen, "%s:%s", proto, port);
+		xml_node_get_text_free(ctx->xml, port);
+	} else {
+		buflen = os_strlen(proto) + 10;
+		buf = os_malloc(buflen);
+		if (buf)
+			os_snprintf(buf, buflen, "%s", proto);
+	}
+
+	xml_node_get_text_free(ctx->xml, proto);
+
+	if (buf == NULL)
+		return;
+
+	if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0)
+		wpa_printf(MSG_INFO, "Could not set req_conn_capab");
+
+	os_free(buf);
+}
+
+
+static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx,
+						     int id, xml_node_t *node)
+{
+	xml_node_t *child;
+
+	wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		set_pps_cred_policy_required_proto_port(ctx, id, child);
+	}
+}
+
+
+static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id,
+					     xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str);
+	if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit");
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id,
+				xml_node_t *node)
+{
+	xml_node_t *child;
+	const char *name;
+
+	wpa_printf(MSG_INFO, "- Policy");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0)
+			set_pps_cred_policy_prpl(ctx, id, child);
+		else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0)
+			set_pps_cred_policy_min_backhaul_list(ctx, id, child);
+		else if (os_strcasecmp(name, "PolicyUpdate") == 0)
+			set_pps_cred_policy_update(ctx, id, child);
+		else if (os_strcasecmp(name, "SPExclusionList") == 0)
+			set_pps_cred_policy_spel(ctx, id, child);
+		else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0)
+			set_pps_cred_policy_required_proto_ports(ctx, id, child);
+		else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0)
+			set_pps_cred_policy_max_bss_load(ctx, id, child);
+		else
+			wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name);
+	}
+}
+
+
+static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id,
+				  xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- CredentialPriority = %s", str);
+	if (set_cred(ctx->ifname, id, "sp_priority", str) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred sp_priority");
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx,
+					       int id, xml_node_t *node)
+{
+	wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO");
+}
+
+
+static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id,
+				    xml_node_t *node)
+{
+	wpa_printf(MSG_INFO, "- SubscriptionUpdate");
+	/* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx,
+					    int id, xml_node_t *node)
+{
+	xml_node_t *ssid_node, *hessid_node;
+	char *ssid, *hessid;
+
+	ssid_node = get_node(ctx->xml, node, "SSID");
+	if (ssid_node == NULL) {
+		wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node");
+		return;
+	}
+
+	hessid_node = get_node(ctx->xml, node, "HESSID");
+
+	ssid = xml_node_get_text(ctx->xml, ssid_node);
+	if (ssid == NULL)
+		return;
+	hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL;
+
+	wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid);
+	if (hessid)
+		wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s",
+			   hessid);
+
+	/* TODO: Configure to wpa_supplicant */
+
+	xml_node_get_text_free(ctx->xml, ssid);
+	xml_node_get_text_free(ctx->xml, hessid);
+}
+
+
+static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx,
+					     int id, xml_node_t *node)
+{
+	xml_node_t *child;
+
+	wpa_printf(MSG_INFO, "- HomeSP/NetworkID");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		set_pps_cred_home_sp_network_id(ctx, id, child);
+	}
+}
+
+
+static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx,
+					       int id, xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str);
+	/* not used within wpa_supplicant(?) */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx,
+					  int id, xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str);
+	/* not used within wpa_supplicant */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id,
+				      xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str);
+	if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred domain");
+	if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match");
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id,
+				    xml_node_t *node)
+{
+	xml_node_t *child;
+	const char *name;
+	char *homeoi = NULL;
+	int required = 0;
+	char *str;
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (strcasecmp(name, "HomeOI") == 0 && !homeoi) {
+			homeoi = xml_node_get_text(ctx->xml, child);
+			wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s",
+				   homeoi);
+		} else if (strcasecmp(name, "HomeOIRequired") == 0) {
+			str = xml_node_get_text(ctx->xml, child);
+			wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'",
+				   str);
+			if (str == NULL)
+				continue;
+			required = strcasecmp(str, "true") == 0;
+			xml_node_get_text_free(ctx->xml, str);
+		} else
+			wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'",
+				   name);
+	}
+
+	if (homeoi == NULL) {
+		wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored");
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d",
+		   homeoi, required);
+
+	if (required) {
+		if (set_cred(ctx->ifname, id, "required_roaming_consortium",
+			     homeoi) < 0)
+			wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+	} else {
+		if (set_cred_quoted(ctx->ifname, id, "roaming_consortium",
+				    homeoi) < 0)
+			wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+	}
+
+	xml_node_get_text_free(ctx->xml, homeoi);
+}
+
+
+static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id,
+					 xml_node_t *node)
+{
+	xml_node_t *child;
+
+	wpa_printf(MSG_INFO, "- HomeSP/HomeOIList");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		set_pps_cred_home_sp_oi(ctx, id, child);
+	}
+}
+
+
+static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx,
+					       int id, xml_node_t *node)
+{
+	xml_node_t *child;
+	const char *name;
+	char *fqdn = NULL;
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) {
+			fqdn = xml_node_get_text(ctx->xml, child);
+			wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s",
+				   fqdn);
+		} else
+			wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'",
+				   name);
+	}
+
+	if (fqdn == NULL) {
+		wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored");
+		return;
+	}
+
+	if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node");
+
+	xml_node_get_text_free(ctx->xml, fqdn);
+}
+
+
+static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx,
+						int id,
+						xml_node_t *node)
+{
+	xml_node_t *child;
+
+	wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		set_pps_cred_home_sp_other_partner(ctx, id, child);
+	}
+}
+
+
+static void set_pps_cred_home_sp_roaming_consortium_oi(
+	struct hs20_osu_client *ctx, int id, xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
+	/* TODO: Set to wpa_supplicant */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id,
+				 xml_node_t *node)
+{
+	xml_node_t *child;
+	const char *name;
+
+	wpa_printf(MSG_INFO, "- HomeSP");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "NetworkID") == 0)
+			set_pps_cred_home_sp_network_ids(ctx, id, child);
+		else if (os_strcasecmp(name, "FriendlyName") == 0)
+			set_pps_cred_home_sp_friendly_name(ctx, id, child);
+		else if (os_strcasecmp(name, "IconURL") == 0)
+			set_pps_cred_home_sp_icon_url(ctx, id, child);
+		else if (os_strcasecmp(name, "FQDN") == 0)
+			set_pps_cred_home_sp_fqdn(ctx, id, child);
+		else if (os_strcasecmp(name, "HomeOIList") == 0)
+			set_pps_cred_home_sp_oi_list(ctx, id, child);
+		else if (os_strcasecmp(name, "OtherHomePartners") == 0)
+			set_pps_cred_home_sp_other_partners(ctx, id, child);
+		else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0)
+			set_pps_cred_home_sp_roaming_consortium_oi(ctx, id,
+								   child);
+		else
+			wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name);
+	}
+}
+
+
+static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id,
+				    xml_node_t *node)
+{
+	wpa_printf(MSG_INFO, "- SubscriptionParameters");
+	/* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id,
+				       xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str);
+	/* not used within wpa_supplicant */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id,
+					 xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str);
+	/* not used within wpa_supplicant */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_username(struct hs20_osu_client *ctx, int id,
+				  xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s",
+		   str);
+	if (set_cred_quoted(ctx->ifname, id, "username", str) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred username");
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_password(struct hs20_osu_client *ctx, int id,
+				  xml_node_t *node)
+{
+	int len, i;
+	char *pw, *hex, *pos, *end;
+
+	pw = xml_node_get_base64_text(ctx->xml, node, &len);
+	if (pw == NULL)
+		return;
+
+	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw);
+
+	hex = malloc(len * 2 + 1);
+	if (hex == NULL) {
+		free(pw);
+		return;
+	}
+	end = hex + len * 2 + 1;
+	pos = hex;
+	for (i = 0; i < len; i++) {
+		snprintf(pos, end - pos, "%02x", pw[i]);
+		pos += 2;
+	}
+	free(pw);
+
+	if (set_cred(ctx->ifname, id, "password", hex) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred password");
+	free(hex);
+}
+
+
+static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id,
+					 xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s",
+		   str);
+	/* not used within wpa_supplicant */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id,
+					xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s",
+		   str);
+	/* not used within wpa_supplicant */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id,
+				       xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	if (str == NULL)
+		return;
+	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s",
+		   str);
+	/* not used within wpa_supplicant */
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
+				    xml_node_t *node)
+{
+	wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO");
+}
+
+
+static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id,
+					   xml_node_t *node)
+{
+	xml_node_t *child;
+	const char *name;
+
+	wpa_printf(MSG_INFO, "- Credential/UsernamePassword");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "Username") == 0)
+			set_pps_cred_username(ctx, id, child);
+		else if (os_strcasecmp(name, "Password") == 0)
+			set_pps_cred_password(ctx, id, child);
+		else if (os_strcasecmp(name, "MachineManaged") == 0)
+			set_pps_cred_machine_managed(ctx, id, child);
+		else if (os_strcasecmp(name, "SoftTokenApp") == 0)
+			set_pps_cred_soft_token_app(ctx, id, child);
+		else if (os_strcasecmp(name, "AbleToShare") == 0)
+			set_pps_cred_able_to_share(ctx, id, child);
+		else if (os_strcasecmp(name, "EAPMethod") == 0)
+			set_pps_cred_eap_method(ctx, id, child);
+		else
+			wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'",
+				   name);
+	}
+}
+
+
+static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id,
+				      xml_node_t *node, const char *fqdn)
+{
+	char buf[200], dir[200];
+
+	wpa_printf(MSG_INFO, "- Credential/DigitalCertificate");
+
+	if (getcwd(dir, sizeof(dir)) == NULL)
+		return;
+
+	/* TODO: could build username from Subject of Subject AltName */
+	if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) {
+		wpa_printf(MSG_INFO, "Failed to set username");
+	}
+
+	snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, fqdn);
+	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);
+	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");
+		}
+	}
+}
+
+
+static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id,
+			       xml_node_t *node, const char *fqdn, int sim)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+	char buf[200], dir[200];
+
+	if (str == NULL)
+		return;
+
+	wpa_printf(MSG_INFO, "- Credential/Realm = %s", str);
+	if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred realm");
+	xml_node_get_text_free(ctx->xml, str);
+
+	if (sim)
+		return;
+
+	if (getcwd(dir, sizeof(dir)) == NULL)
+		return;
+	snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
+	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");
+		}
+	}
+}
+
+
+static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx,
+					       int id, xml_node_t *node)
+{
+	char *str = xml_node_get_text(ctx->xml, node);
+
+	if (str == NULL)
+		return;
+
+	wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str);
+	if (os_strcasecmp(str, "true") == 0 &&
+	    set_cred(ctx->ifname, id, "ocsp", "2") < 0)
+		wpa_printf(MSG_INFO, "Failed to set cred ocsp");
+	xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id,
+			     xml_node_t *sim, xml_node_t *realm)
+{
+	xml_node_t *node;
+	char *imsi, *eaptype, *str, buf[20];
+	int type;
+	int mnc_len = 3;
+	size_t imsi_len;
+
+	node = get_node(ctx->xml, sim, "EAPType");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No SIM/EAPType node in credential");
+		return;
+	}
+	eaptype = xml_node_get_text(ctx->xml, node);
+	if (eaptype == NULL) {
+		wpa_printf(MSG_INFO, "Could not extract SIM/EAPType");
+		return;
+	}
+	wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype);
+	type = atoi(eaptype);
+	xml_node_get_text_free(ctx->xml, eaptype);
+
+	switch (type) {
+	case EAP_TYPE_SIM:
+		if (set_cred(ctx->ifname, id, "eap", "SIM") < 0)
+			wpa_printf(MSG_INFO, "Could not set eap=SIM");
+		break;
+	case EAP_TYPE_AKA:
+		if (set_cred(ctx->ifname, id, "eap", "AKA") < 0)
+			wpa_printf(MSG_INFO, "Could not set eap=SIM");
+		break;
+	case EAP_TYPE_AKA_PRIME:
+		if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0)
+			wpa_printf(MSG_INFO, "Could not set eap=SIM");
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type);
+		return;
+	}
+
+	node = get_node(ctx->xml, sim, "IMSI");
+	if (node == NULL) {
+		wpa_printf(MSG_INFO, "No SIM/IMSI node in credential");
+		return;
+	}
+	imsi = xml_node_get_text(ctx->xml, node);
+	if (imsi == NULL) {
+		wpa_printf(MSG_INFO, "Could not extract SIM/IMSI");
+		return;
+	}
+	wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi);
+	imsi_len = os_strlen(imsi);
+	if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) {
+		wpa_printf(MSG_INFO, "Invalid IMSI length");
+		xml_node_get_text_free(ctx->xml, imsi);
+		return;
+	}
+
+	str = xml_node_get_text(ctx->xml, node);
+	if (str) {
+		char *pos;
+		pos = os_strstr(str, "mnc");
+		if (pos && os_strlen(pos) >= 6) {
+			if (os_strncmp(imsi + 3, pos + 3, 3) == 0)
+				mnc_len = 3;
+			else if (os_strncmp(imsi + 3, pos + 4, 2) == 0)
+				mnc_len = 2;
+		}
+		xml_node_get_text_free(ctx->xml, str);
+	}
+
+	os_memcpy(buf, imsi, 3 + mnc_len);
+	buf[3 + mnc_len] = '-';
+	os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len,
+		   sizeof(buf) - 3 - mnc_len - 1);
+
+	xml_node_get_text_free(ctx->xml, imsi);
+
+	if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0)
+		wpa_printf(MSG_INFO, "Could not set IMSI");
+
+	if (set_cred_quoted(ctx->ifname, id, "milenage",
+			    "90dca4eda45b53cf0f12d7c9c3bc6a89:"
+			    "cb9cccc4b9258e6dca4760379fb82581:000000000123") <
+	    0)
+		wpa_printf(MSG_INFO, "Could not set Milenage parameters");
+}
+
+
+static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id,
+				    xml_node_t *node, const char *fqdn)
+{
+	xml_node_t *child, *sim, *realm;
+	const char *name;
+
+	wpa_printf(MSG_INFO, "- Credential");
+
+	sim = get_node(ctx->xml, node, "SIM");
+	realm = get_node(ctx->xml, node, "Realm");
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "CreationDate") == 0)
+			set_pps_cred_creation_date(ctx, id, child);
+		else if (os_strcasecmp(name, "ExpirationDate") == 0)
+			set_pps_cred_expiration_date(ctx, id, child);
+		else if (os_strcasecmp(name, "UsernamePassword") == 0)
+			set_pps_cred_username_password(ctx, id, child);
+		else if (os_strcasecmp(name, "DigitalCertificate") == 0)
+			set_pps_cred_digital_cert(ctx, id, child, fqdn);
+		else if (os_strcasecmp(name, "Realm") == 0)
+			set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL);
+		else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0)
+			set_pps_cred_check_aaa_cert_status(ctx, id, child);
+		else if (os_strcasecmp(name, "SIM") == 0)
+			set_pps_cred_sim(ctx, id, child, realm);
+		else
+			wpa_printf(MSG_INFO, "Unknown Credential node '%s'",
+				   name);
+	}
+}
+
+
+static void set_pps_credential(struct hs20_osu_client *ctx, int id,
+			       xml_node_t *cred, const char *fqdn)
+{
+	xml_node_t *child;
+	const char *name;
+
+	xml_node_for_each_child(ctx->xml, child, cred) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "Policy") == 0)
+			set_pps_cred_policy(ctx, id, child);
+		else if (os_strcasecmp(name, "CredentialPriority") == 0)
+			set_pps_cred_priority(ctx, id, child);
+		else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0)
+			set_pps_cred_aaa_server_trust_root(ctx, id, child);
+		else if (os_strcasecmp(name, "SubscriptionUpdate") == 0)
+			set_pps_cred_sub_update(ctx, id, child);
+		else if (os_strcasecmp(name, "HomeSP") == 0)
+			set_pps_cred_home_sp(ctx, id, child);
+		else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
+			set_pps_cred_sub_params(ctx, id, child);
+		else if (os_strcasecmp(name, "Credential") == 0)
+			set_pps_cred_credential(ctx, id, child, fqdn);
+		else
+			wpa_printf(MSG_INFO, "Unknown credential node '%s'",
+				   name);
+	}
+}
+
+
+static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
+		    const char *fqdn)
+{
+	xml_node_t *child;
+	const char *name;
+	int id;
+	char *update_identifier = NULL;
+
+	/*
+	 * TODO: Could consider more complex mechanism that would remove
+	 * credentials only if there are changes in the information sent to
+	 * wpa_supplicant.
+	 */
+	remove_sp_creds(ctx, fqdn);
+
+	xml_node_for_each_child(ctx->xml, child, pps) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
+			update_identifier = xml_node_get_text(ctx->xml, child);
+			if (update_identifier) {
+				wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
+					   update_identifier);
+				break;
+			}
+		}
+	}
+
+	xml_node_for_each_child(ctx->xml, child, pps) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (os_strcasecmp(name, "UpdateIdentifier") == 0)
+			continue;
+		id = add_cred(ctx->ifname);
+		if (id < 0) {
+			wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
+			write_summary(ctx, "Failed to add credential to wpa_supplicant");
+			break;
+		}
+		write_summary(ctx, "Add a credential to wpa_supplicant");
+		if (update_identifier &&
+		    set_cred(ctx->ifname, id, "update_identifier",
+			     update_identifier) < 0)
+			wpa_printf(MSG_INFO, "Failed to set update_identifier");
+		if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
+		    0)
+			wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
+		wpa_printf(MSG_INFO, "credential localname: '%s'", name);
+		set_pps_credential(ctx, id, child, fqdn);
+		ctx->pps_cred_set = 1;
+	}
+
+	xml_node_get_text_free(ctx->xml, update_identifier);
+}
+
+
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+	xml_node_t *pps;
+	const char *fqdn;
+	char *fqdn_buf = NULL, *pos;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+		return;
+	}
+
+	fqdn = os_strstr(pps_fname, "SP/");
+	if (fqdn) {
+		fqdn_buf = os_strdup(fqdn + 3);
+		if (fqdn_buf == NULL)
+			return;
+		pos = os_strchr(fqdn_buf, '/');
+		if (pos)
+			*pos = '\0';
+		fqdn = fqdn_buf;
+	} else
+		fqdn = "wi-fi.org";
+
+	wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
+		   fqdn);
+	set_pps(ctx, pps, fqdn);
+
+	os_free(fqdn_buf);
+	xml_node_free(ctx->xml, pps);
+}
+
+
+static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+	xml_node_t *pps, *node;
+	char *fqdn = NULL;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+		return -1;
+	}
+
+	node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+	if (node)
+		fqdn = xml_node_get_text(ctx->xml, node);
+
+	xml_node_free(ctx->xml, pps);
+
+	if (fqdn) {
+		FILE *f = fopen("pps-fqdn", "w");
+		if (f) {
+			fprintf(f, "%s", fqdn);
+			fclose(f);
+		}
+		xml_node_get_text_free(ctx->xml, fqdn);
+		return 0;
+	}
+
+	xml_node_get_text_free(ctx->xml, fqdn);
+	return -1;
+}
+
+
+static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+			const char *out_fname, const char *urn, int use_path)
+{
+	xml_node_t *mo, *node;
+
+	mo = node_from_file(ctx->xml, in_fname);
+	if (mo == NULL) {
+		wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+		return;
+	}
+
+	node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
+	if (node) {
+		node_to_file(ctx->xml, out_fname, node);
+		xml_node_free(ctx->xml, node);
+	}
+
+	xml_node_free(ctx->xml, mo);
+}
+
+
+static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+			  const char *out_fname)
+{
+	xml_node_t *tnds, *mo;
+
+	tnds = node_from_file(ctx->xml, in_fname);
+	if (tnds == NULL) {
+		wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+		return;
+	}
+
+	mo = tnds_to_mo(ctx->xml, tnds);
+	if (mo) {
+		node_to_file(ctx->xml, out_fname, mo);
+		xml_node_free(ctx->xml, mo);
+	}
+
+	xml_node_free(ctx->xml, tnds);
+}
+
+
+struct osu_icon {
+	int id;
+	char lang[4];
+	char mime_type[256];
+	char filename[256];
+};
+
+struct osu_data {
+	char bssid[20];
+	char url[256];
+	unsigned int methods;
+	char osu_ssid[33];
+	char osu_nai[256];
+	struct osu_lang_text friendly_name[MAX_OSU_VALS];
+	size_t friendly_name_count;
+	struct osu_lang_text serv_desc[MAX_OSU_VALS];
+	size_t serv_desc_count;
+	struct osu_icon icon[MAX_OSU_VALS];
+	size_t icon_count;
+};
+
+
+static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
+{
+	FILE *f;
+	char buf[1000];
+	struct osu_data *osu = NULL, *last = NULL;
+	size_t osu_count = 0;
+	char *pos, *end;
+
+	f = fopen(fname, "r");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Could not open %s", fname);
+		return NULL;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = strchr(buf, '\n');
+		if (pos)
+			*pos = '\0';
+
+		if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
+			last = realloc(osu, (osu_count + 1) * sizeof(*osu));
+			if (last == NULL)
+				break;
+			osu = last;
+			last = &osu[osu_count++];
+			memset(last, 0, sizeof(*last));
+			snprintf(last->bssid, sizeof(last->bssid), "%s",
+				 buf + 13);
+			continue;
+		}
+		if (!last)
+			continue;
+
+		if (strncmp(buf, "uri=", 4) == 0) {
+			snprintf(last->url, sizeof(last->url), "%s", buf + 4);
+			continue;
+		}
+
+		if (strncmp(buf, "methods=", 8) == 0) {
+			last->methods = strtol(buf + 8, NULL, 16);
+			continue;
+		}
+
+		if (strncmp(buf, "osu_ssid=", 9) == 0) {
+			snprintf(last->osu_ssid, sizeof(last->osu_ssid),
+				 "%s", buf + 9);
+			continue;
+		}
+
+		if (os_strncmp(buf, "osu_nai=", 8) == 0) {
+			os_snprintf(last->osu_nai, sizeof(last->osu_nai),
+				    "%s", buf + 8);
+			continue;
+		}
+
+		if (strncmp(buf, "friendly_name=", 14) == 0) {
+			struct osu_lang_text *txt;
+			if (last->friendly_name_count == MAX_OSU_VALS)
+				continue;
+			pos = strchr(buf + 14, ':');
+			if (pos == NULL)
+				continue;
+			*pos++ = '\0';
+			txt = &last->friendly_name[last->friendly_name_count++];
+			snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 14);
+			snprintf(txt->text, sizeof(txt->text), "%s", pos);
+		}
+
+		if (strncmp(buf, "desc=", 5) == 0) {
+			struct osu_lang_text *txt;
+			if (last->serv_desc_count == MAX_OSU_VALS)
+				continue;
+			pos = strchr(buf + 5, ':');
+			if (pos == NULL)
+				continue;
+			*pos++ = '\0';
+			txt = &last->serv_desc[last->serv_desc_count++];
+			snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 5);
+			snprintf(txt->text, sizeof(txt->text), "%s", pos);
+		}
+
+		if (strncmp(buf, "icon=", 5) == 0) {
+			struct osu_icon *icon;
+			if (last->icon_count == MAX_OSU_VALS)
+				continue;
+			icon = &last->icon[last->icon_count++];
+			icon->id = atoi(buf + 5);
+			pos = strchr(buf, ':');
+			if (pos == NULL)
+				continue;
+			pos = strchr(pos + 1, ':');
+			if (pos == NULL)
+				continue;
+			pos = strchr(pos + 1, ':');
+			if (pos == NULL)
+				continue;
+			pos++;
+			end = strchr(pos, ':');
+			if (!end)
+				continue;
+			*end = '\0';
+			snprintf(icon->lang, sizeof(icon->lang), "%s", pos);
+			pos = end + 1;
+
+			end = strchr(pos, ':');
+			if (end)
+				*end = '\0';
+			snprintf(icon->mime_type, sizeof(icon->mime_type),
+				 "%s", pos);
+			if (!pos)
+				continue;
+			pos = end + 1;
+
+			end = strchr(pos, ':');
+			if (end)
+				*end = '\0';
+			snprintf(icon->filename, sizeof(icon->filename),
+				 "%s", pos);
+			continue;
+		}
+	}
+
+	fclose(f);
+
+	*count = osu_count;
+	return osu;
+}
+
+
+static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
+		       const char *ssid, const char *url,
+		       unsigned int methods, int no_prod_assoc,
+		       const char *osu_nai)
+{
+	int id;
+	const char *ifname = ctx->ifname;
+	char buf[200];
+	struct wpa_ctrl *mon;
+	int res;
+
+	id = add_network(ifname);
+	if (id < 0)
+		return -1;
+	if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
+		return -1;
+	if (osu_nai && os_strlen(osu_nai) > 0) {
+		char dir[255], fname[300];
+		if (getcwd(dir, sizeof(dir)) == NULL)
+			return -1;
+		os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
+
+		if (set_network(ifname, id, "proto", "OSEN") < 0 ||
+		    set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
+		    set_network(ifname, id, "pairwise", "CCMP") < 0 ||
+		    set_network(ifname, id, "group", "GTK_NOT_USED") < 0 ||
+		    set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
+		    set_network(ifname, id, "ocsp", "2") < 0 ||
+		    set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
+		    set_network_quoted(ifname, id, "ca_cert", fname) < 0)
+			return -1;
+	} else {
+		if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
+			return -1;
+	}
+
+	mon = open_wpa_mon(ifname);
+	if (mon == NULL)
+		return -1;
+
+	wpa_printf(MSG_INFO, "Associate with OSU SSID");
+	write_summary(ctx, "Associate with OSU SSID");
+	snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
+	if (wpa_command(ifname, buf) < 0)
+		return -1;
+
+	res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
+				buf, sizeof(buf));
+
+	wpa_ctrl_detach(mon);
+	wpa_ctrl_close(mon);
+
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "Could not connect");
+		write_summary(ctx, "Could not connect to OSU network");
+		wpa_printf(MSG_INFO, "Remove OSU network connection");
+		snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+		wpa_command(ifname, buf);
+		return -1;
+	}
+
+	write_summary(ctx, "Waiting for IP address for subscription registration");
+	if (wait_ip_addr(ifname, 15) < 0) {
+		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+	}
+
+	if (no_prod_assoc) {
+		if (res < 0)
+			return -1;
+		wpa_printf(MSG_INFO, "No production connection used for testing purposes");
+		write_summary(ctx, "No production connection used for testing purposes");
+		return 0;
+	}
+
+	ctx->no_reconnect = 1;
+	if (methods & 0x02)
+		res = cmd_prov(ctx, url);
+	else if (methods & 0x01)
+		res = cmd_oma_dm_prov(ctx, url);
+
+	wpa_printf(MSG_INFO, "Remove OSU network connection");
+	write_summary(ctx, "Remove OSU network connection");
+	snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+	wpa_command(ifname, buf);
+
+	if (res < 0)
+		return -1;
+
+	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+	write_summary(ctx, "Requesting reconnection with updated configuration");
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+		wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+		write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
+			  int connect, int no_prod_assoc,
+			  const char *friendly_name)
+{
+	char fname[255];
+	FILE *f;
+	struct osu_data *osu = NULL, *last = NULL;
+	size_t osu_count, i, j;
+	int ret;
+
+	write_summary(ctx, "OSU provider selection");
+
+	if (dir == NULL) {
+		wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
+		return -1;
+	}
+
+	snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
+	osu = parse_osu_providers(fname, &osu_count);
+	if (osu == NULL) {
+		wpa_printf(MSG_INFO, "Could not any OSU providers from %s",
+			   fname);
+		write_result(ctx, "No OSU providers available");
+		return -1;
+	}
+
+	if (friendly_name) {
+		for (i = 0; i < osu_count; i++) {
+			last = &osu[i];
+			for (j = 0; j < last->friendly_name_count; j++) {
+				if (os_strcmp(last->friendly_name[j].text,
+					      friendly_name) == 0)
+					break;
+			}
+			if (j < last->friendly_name_count)
+				break;
+		}
+		if (i == osu_count) {
+			wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
+				   friendly_name);
+			write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
+				      friendly_name);
+			free(osu);
+			return -1;
+		}
+
+		wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
+			   friendly_name);
+		write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
+			      friendly_name);
+		ret = i + 1;
+		goto selected;
+	}
+
+	snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
+	f = fopen(fname, "w");
+	if (f == NULL) {
+		wpa_printf(MSG_INFO, "Could not open %s", fname);
+		free(osu);
+		return -1;
+	}
+
+	fprintf(f, "<html><head>"
+		"<meta http-equiv=\"Content-type\" content=\"text/html; "
+		"charset=utf-8\"<title>Select service operator</title>"
+		"</head><body><h1>Select service operator</h1>\n");
+
+	if (osu_count == 0)
+		fprintf(f, "No online signup available\n");
+
+	for (i = 0; i < osu_count; i++) {
+		last = &osu[i];
+#ifdef ANDROID
+		fprintf(f, "<p>\n"
+			"<a href=\"http://localhost:12345/osu/%d\">"
+			"<table><tr><td>", (int) i + 1);
+#else /* ANDROID */
+		fprintf(f, "<p>\n"
+			"<a href=\"osu://%d\">"
+			"<table><tr><td>", (int) i + 1);
+#endif /* ANDROID */
+		for (j = 0; j < last->icon_count; j++) {
+			fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
+				last->icon[j].id,
+				strcasecmp(last->icon[j].mime_type,
+					   "image/png") == 0 ? "png" : "icon");
+		}
+		fprintf(f, "<td>");
+		for (j = 0; j < last->friendly_name_count; j++) {
+			fprintf(f, "<small>[%s]</small> %s<br>\n",
+				last->friendly_name[j].lang,
+				last->friendly_name[j].text);
+		}
+		fprintf(f, "<tr><td colspan=2>");
+		for (j = 0; j < last->serv_desc_count; j++) {
+			fprintf(f, "<small>[%s]</small> %s<br>\n",
+				last->serv_desc[j].lang,
+				last->serv_desc[j].text);
+		}
+		fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
+			"SSID: %s<br>\n",
+			last->bssid, last->osu_ssid);
+		if (last->osu_nai)
+			fprintf(f, "NAI: %s<br>\n", last->osu_nai);
+		fprintf(f, "URL: %s<br>\n"
+			"methods:%s%s<br>\n"
+			"</small></p>\n",
+			last->url,
+			last->methods & 0x01 ? " OMA-DM" : "",
+			last->methods & 0x02 ? " SOAP-XML-SPP" : "");
+	}
+
+	fprintf(f, "</body></html>\n");
+
+	fclose(f);
+
+	snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
+	write_summary(ctx, "Start web browser with OSU provider selection page");
+	ret = hs20_web_browser(fname);
+
+selected:
+	if (ret > 0 && (size_t) ret <= osu_count) {
+		char *data;
+		size_t data_len;
+
+		wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
+		last = &osu[ret - 1];
+		ret = 0;
+		wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
+		wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
+		wpa_printf(MSG_INFO, "URL: %s", last->url);
+		write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
+			      ret, last->bssid, last->osu_ssid, last->url);
+
+		ctx->friendly_name_count = last->friendly_name_count;
+		for (j = 0; j < last->friendly_name_count; j++) {
+			wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
+				   last->friendly_name[j].lang,
+				   last->friendly_name[j].text);
+			os_strlcpy(ctx->friendly_name[j].lang,
+				   last->friendly_name[j].lang,
+				   sizeof(ctx->friendly_name[j].lang));
+			os_strlcpy(ctx->friendly_name[j].text,
+				   last->friendly_name[j].text,
+				   sizeof(ctx->friendly_name[j].text));
+		}
+
+		ctx->icon_count = last->icon_count;
+		for (j = 0; j < last->icon_count; j++) {
+			char fname[256];
+
+			os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
+				    dir, last->icon[j].id,
+				    strcasecmp(last->icon[j].mime_type,
+					       "image/png") == 0 ?
+				    "png" : "icon");
+			wpa_printf(MSG_INFO, "ICON: %s (%s)",
+				   fname, last->icon[j].filename);
+			os_strlcpy(ctx->icon_filename[j],
+				   last->icon[j].filename,
+				   sizeof(ctx->icon_filename[j]));
+
+			data = os_readfile(fname, &data_len);
+			if (data) {
+				sha256_vector(1, (const u8 **) &data, &data_len,
+					      ctx->icon_hash[j]);
+				os_free(data);
+			}
+		}
+
+		if (connect == 2) {
+			if (last->methods & 0x02)
+				ret = cmd_prov(ctx, last->url);
+			else if (last->methods & 0x01)
+				ret = cmd_oma_dm_prov(ctx, last->url);
+			else
+				ret = -1;
+		} else if (connect)
+			ret = osu_connect(ctx, last->bssid, last->osu_ssid,
+					  last->url, last->methods,
+					  no_prod_assoc, last->osu_nai);
+	} else
+		ret = -1;
+
+	free(osu);
+
+	return ret;
+}
+
+
+static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
+		      const char *friendly_name)
+{
+	char dir[255];
+	char fname[300], buf[400];
+	struct wpa_ctrl *mon;
+	const char *ifname;
+	int res;
+
+	ifname = ctx->ifname;
+
+	if (getcwd(dir, sizeof(dir)) == NULL)
+		return -1;
+
+	snprintf(fname, sizeof(fname), "%s/osu-info", dir);
+	if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
+		wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+			   fname, strerror(errno));
+		return -1;
+	}
+
+	snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
+	if (wpa_command(ifname, buf) < 0) {
+		wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
+		return -1;
+	}
+
+	mon = open_wpa_mon(ifname);
+	if (mon == NULL)
+		return -1;
+
+	wpa_printf(MSG_INFO, "Starting OSU fetch");
+	write_summary(ctx, "Starting OSU provider information fetch");
+	if (wpa_command(ifname, "FETCH_OSU") < 0) {
+		wpa_printf(MSG_INFO, "Could not start OSU fetch");
+		wpa_ctrl_detach(mon);
+		wpa_ctrl_close(mon);
+		return -1;
+	}
+	res = get_wpa_cli_event(mon, "OSU provider fetch completed",
+				buf, sizeof(buf));
+
+	wpa_ctrl_detach(mon);
+	wpa_ctrl_close(mon);
+
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "OSU fetch did not complete");
+		write_summary(ctx, "OSU fetch did not complete");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "OSU provider fetch completed");
+
+	return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name);
+}
+
+
+static void cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
+			const char *pps_fname, const char *ca_fname)
+{
+	xml_node_t *pps, *node;
+	char pps_fname_buf[300];
+	char ca_fname_buf[200];
+	char *cred_username = NULL;
+	char *cred_password = NULL;
+	char *sub_rem_uri = NULL;
+	char client_cert_buf[200];
+	char *client_cert = NULL;
+	char client_key_buf[200];
+	char *client_key = NULL;
+	int spp;
+
+	wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
+		   address);
+
+	if (!pps_fname) {
+		char buf[256];
+		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+		if (os_strncmp(address, "fqdn=", 5) == 0) {
+			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+			os_snprintf(buf, sizeof(buf), "%s", address + 5);
+			address = NULL;
+		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+					  sizeof(buf)) < 0) {
+			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+			return;
+		}
+		os_free(ctx->fqdn);
+		ctx->fqdn = os_strdup(buf);
+		if (ctx->fqdn == NULL)
+			return;
+		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+			   buf);
+		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+			    "SP/%s/pps.xml", ctx->fqdn);
+		pps_fname = pps_fname_buf;
+
+		os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+			    ctx->fqdn);
+		ca_fname = ca_fname_buf;
+	}
+
+	if (!os_file_exists(pps_fname)) {
+		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+			   pps_fname);
+		return;
+	}
+	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+	if (ca_fname && !os_file_exists(ca_fname)) {
+		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+			   ca_fname);
+		return;
+	}
+	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+	ctx->ca_fname = ca_fname;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read PPS MO");
+		return;
+	}
+
+	if (!ctx->fqdn) {
+		char *tmp;
+		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+		if (node == NULL) {
+			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+			return;
+		}
+		tmp = xml_node_get_text(ctx->xml, node);
+		if (tmp == NULL) {
+			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+			return;
+		}
+		ctx->fqdn = os_strdup(tmp);
+		xml_node_get_text_free(ctx->xml, tmp);
+		if (!ctx->fqdn) {
+			wpa_printf(MSG_INFO, "No FQDN known");
+			return;
+		}
+	}
+
+	node = get_child_node(ctx->xml, pps,
+			      "SubscriptionUpdate/UpdateMethod");
+	if (node) {
+		char *tmp;
+		tmp = xml_node_get_text(ctx->xml, node);
+		if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+			spp = 0;
+		else
+			spp = 1;
+	} else {
+		wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+		spp = 1;
+	}
+
+	get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
+		    &cred_username, &cred_password);
+	if (cred_username)
+		wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+	if (cred_password)
+		wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+	if (cred_username == NULL && cred_password == NULL &&
+	    get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+		wpa_printf(MSG_INFO, "Using client certificate");
+		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+			    "SP/%s/client-cert.pem", ctx->fqdn);
+		client_cert = client_cert_buf;
+		os_snprintf(client_key_buf, sizeof(client_key_buf),
+			    "SP/%s/client-key.pem", ctx->fqdn);
+		client_key = client_key_buf;
+		ctx->client_cert_present = 1;
+	}
+
+	node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
+	if (node) {
+		sub_rem_uri = xml_node_get_text(ctx->xml, node);
+		if (sub_rem_uri &&
+		    (!address || os_strcmp(address, sub_rem_uri) != 0)) {
+			wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
+				   sub_rem_uri);
+			address = sub_rem_uri;
+		}
+	}
+	if (!address) {
+		wpa_printf(MSG_INFO, "Server URL not known");
+		return;
+	}
+
+	write_summary(ctx, "Wait for IP address for subscriptiom remediation");
+	wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
+
+	if (wait_ip_addr(ctx->ifname, 15) < 0) {
+		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+	}
+
+	if (spp)
+		spp_sub_rem(ctx, address, pps_fname,
+			    client_cert, client_key,
+			    cred_username, cred_password, pps);
+	else
+		oma_dm_sub_rem(ctx, address, pps_fname,
+			       client_cert, client_key,
+			       cred_username, cred_password, pps);
+
+	xml_node_get_text_free(ctx->xml, sub_rem_uri);
+	xml_node_get_text_free(ctx->xml, cred_username);
+	str_clear_free(cred_password);
+	xml_node_free(ctx->xml, pps);
+}
+
+
+static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
+		       const char *pps_fname, const char *ca_fname)
+{
+	xml_node_t *pps;
+	xml_node_t *node;
+	char pps_fname_buf[300];
+	char ca_fname_buf[200];
+	char *uri = NULL;
+	char *cred_username = NULL;
+	char *cred_password = NULL;
+	char client_cert_buf[200];
+	char *client_cert = NULL;
+	char client_key_buf[200];
+	char *client_key = NULL;
+	int spp;
+
+	wpa_printf(MSG_INFO, "Policy update requested");
+
+	if (!pps_fname) {
+		char buf[256];
+		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+		if (os_strncmp(address, "fqdn=", 5) == 0) {
+			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+			os_snprintf(buf, sizeof(buf), "%s", address + 5);
+			address = NULL;
+		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+					  sizeof(buf)) < 0) {
+			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+			return -1;
+		}
+		os_free(ctx->fqdn);
+		ctx->fqdn = os_strdup(buf);
+		if (ctx->fqdn == NULL)
+			return -1;
+		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+			   buf);
+		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+			    "SP/%s/pps.xml", ctx->fqdn);
+		pps_fname = pps_fname_buf;
+
+		os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+			    buf);
+		ca_fname = ca_fname_buf;
+	}
+
+	if (!os_file_exists(pps_fname)) {
+		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+			   pps_fname);
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+	if (ca_fname && !os_file_exists(ca_fname)) {
+		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+			   ca_fname);
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+	ctx->ca_fname = ca_fname;
+
+	pps = node_from_file(ctx->xml, pps_fname);
+	if (pps == NULL) {
+		wpa_printf(MSG_INFO, "Could not read PPS MO");
+		return -1;
+	}
+
+	if (!ctx->fqdn) {
+		char *tmp;
+		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+		if (node == NULL) {
+			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+			return -1;
+		}
+		tmp = xml_node_get_text(ctx->xml, node);
+		if (tmp == NULL) {
+			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+			return -1;
+		}
+		ctx->fqdn = os_strdup(tmp);
+		xml_node_get_text_free(ctx->xml, tmp);
+		if (!ctx->fqdn) {
+			wpa_printf(MSG_INFO, "No FQDN known");
+			return -1;
+		}
+	}
+
+	node = get_child_node(ctx->xml, pps,
+			      "Policy/PolicyUpdate/UpdateMethod");
+	if (node) {
+		char *tmp;
+		tmp = xml_node_get_text(ctx->xml, node);
+		if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+			spp = 0;
+		else
+			spp = 1;
+	} else {
+		wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+		spp = 1;
+	}
+
+	get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
+		    &cred_username, &cred_password);
+	if (cred_username)
+		wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+	if (cred_password)
+		wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+	if (cred_username == NULL && cred_password == NULL &&
+	    get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+		wpa_printf(MSG_INFO, "Using client certificate");
+		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+			    "SP/%s/client-cert.pem", ctx->fqdn);
+		client_cert = client_cert_buf;
+		os_snprintf(client_key_buf, sizeof(client_key_buf),
+			    "SP/%s/client-key.pem", ctx->fqdn);
+		client_key = client_key_buf;
+	}
+
+	if (!address) {
+		node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
+		if (node) {
+			uri = xml_node_get_text(ctx->xml, node);
+			wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
+			address = uri;
+		}
+	}
+	if (!address) {
+		wpa_printf(MSG_INFO, "Server URL not known");
+		return -1;
+	}
+
+	if (spp)
+		spp_pol_upd(ctx, address, pps_fname,
+			    client_cert, client_key,
+			    cred_username, cred_password, pps);
+	else
+		oma_dm_pol_upd(ctx, address, pps_fname,
+			       client_cert, client_key,
+			       cred_username, cred_password, pps);
+
+	xml_node_get_text_free(ctx->xml, uri);
+	xml_node_get_text_free(ctx->xml, cred_username);
+	str_clear_free(cred_password);
+	xml_node_free(ctx->xml, pps);
+
+	return 0;
+}
+
+
+static char * get_hostname(const char *url)
+{
+	const char *pos, *end, *end2;
+	char *ret;
+
+	if (url == NULL)
+		return NULL;
+
+	pos = os_strchr(url, '/');
+	if (pos == NULL)
+		return NULL;
+	pos++;
+	if (*pos != '/')
+		return NULL;
+	pos++;
+
+	end = os_strchr(pos, '/');
+	end2 = os_strchr(pos, ':');
+	if (end && end2 && end2 < end)
+		end = end2;
+	if (end)
+		end--;
+	else {
+		end = pos;
+		while (*end)
+			end++;
+		if (end > pos)
+			end--;
+	}
+
+	ret = os_malloc(end - pos + 2);
+	if (ret == NULL)
+		return NULL;
+
+	os_memcpy(ret, pos, end - pos + 1);
+	ret[end - pos + 1] = '\0';
+
+	return ret;
+}
+
+
+static int osu_cert_cb(void *_ctx, struct http_cert *cert)
+{
+	struct hs20_osu_client *ctx = _ctx;
+	unsigned int i, j;
+	int found;
+	char *host = NULL;
+
+	wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)",
+		   !ctx->no_osu_cert_validation);
+
+	host = get_hostname(ctx->server_url);
+
+	for (i = 0; i < ctx->server_dnsname_count; i++)
+		os_free(ctx->server_dnsname[i]);
+	os_free(ctx->server_dnsname);
+	ctx->server_dnsname = os_calloc(cert->num_dnsname, sizeof(char *));
+	ctx->server_dnsname_count = 0;
+
+	found = 0;
+	for (i = 0; i < cert->num_dnsname; i++) {
+		if (ctx->server_dnsname) {
+			ctx->server_dnsname[ctx->server_dnsname_count] =
+				os_strdup(cert->dnsname[i]);
+			if (ctx->server_dnsname[ctx->server_dnsname_count])
+				ctx->server_dnsname_count++;
+		}
+		if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
+			found = 1;
+		wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
+	}
+
+	if (host && !found) {
+		wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
+			   host);
+		write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
+			     host);
+		os_free(host);
+		return -1;
+	}
+
+	os_free(host);
+
+	for (i = 0; i < cert->num_othername; i++) {
+		if (os_strcmp(cert->othername[i].oid,
+			      "1.3.6.1.4.1.40808.1.1.1") == 0) {
+			wpa_hexdump_ascii(MSG_INFO,
+					  "id-wfa-hotspot-friendlyName",
+					  cert->othername[i].data,
+					  cert->othername[i].len);
+		}
+	}
+
+	for (j = 0; !ctx->no_osu_cert_validation &&
+		     j < ctx->friendly_name_count; j++) {
+		int found = 0;
+		for (i = 0; i < cert->num_othername; i++) {
+			if (os_strcmp(cert->othername[i].oid,
+				      "1.3.6.1.4.1.40808.1.1.1") != 0)
+				continue;
+			if (cert->othername[i].len < 3)
+				continue;
+			if (os_strncasecmp((char *) cert->othername[i].data,
+					   ctx->friendly_name[j].lang, 3) != 0)
+				continue;
+			if (os_strncmp((char *) cert->othername[i].data + 3,
+				       ctx->friendly_name[j].text,
+				       cert->othername[i].len - 3) == 0) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found) {
+			wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
+				   ctx->friendly_name[j].lang,
+				   ctx->friendly_name[j].text);
+			write_result(ctx, "No friendly name match found for '[%s]%s'",
+				     ctx->friendly_name[j].lang,
+				     ctx->friendly_name[j].text);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < cert->num_logo; i++) {
+		struct http_logo *logo = &cert->logo[i];
+
+		wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
+			   logo->alg_oid, logo->uri);
+		wpa_hexdump_ascii(MSG_INFO, "hashValue",
+				  logo->hash, logo->hash_len);
+	}
+
+	for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
+		int found = 0;
+		char *name = ctx->icon_filename[j];
+		size_t name_len = os_strlen(name);
+
+		wpa_printf(MSG_INFO, "Looking for icon file name '%s' match",
+			   name);
+		for (i = 0; i < cert->num_logo; i++) {
+			struct http_logo *logo = &cert->logo[i];
+			size_t uri_len = os_strlen(logo->uri);
+			char *pos;
+
+			wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d",
+				   logo->uri, (int) uri_len, (int) name_len);
+			if (uri_len < 1 + name_len)
+				continue;
+			pos = &logo->uri[uri_len - name_len - 1];
+			if (*pos != '/')
+				continue;
+			pos++;
+			if (os_strcmp(pos, name) == 0) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found) {
+			wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
+				   name);
+			write_result(ctx,
+				     "No icon filename match found for '%s'",
+				     name);
+			return -1;
+		}
+	}
+
+	for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
+		int found = 0;
+
+		for (i = 0; i < cert->num_logo; i++) {
+			struct http_logo *logo = &cert->logo[i];
+
+			if (logo->hash_len != 32)
+				continue;
+			if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found) {
+			wpa_printf(MSG_INFO, "No icon hash match found");
+			write_result(ctx, "No icon hash match found");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int init_ctx(struct hs20_osu_client *ctx)
+{
+	xml_node_t *devinfo, *devid;
+
+	os_memset(ctx, 0, sizeof(*ctx));
+	ctx->ifname = "wlan0";
+	ctx->xml = xml_node_init_ctx(ctx, NULL);
+	if (ctx->xml == NULL)
+		return -1;
+
+	devinfo = node_from_file(ctx->xml, "devinfo.xml");
+	if (!devinfo) {
+		wpa_printf(MSG_ERROR, "devinfo.xml not found");
+		return -1;
+	}
+
+	devid = get_node(ctx->xml, devinfo, "DevId");
+	if (devid) {
+		char *tmp = xml_node_get_text(ctx->xml, devid);
+		if (tmp) {
+			ctx->devid = os_strdup(tmp);
+			xml_node_get_text_free(ctx->xml, tmp);
+		}
+	}
+	xml_node_free(ctx->xml, devinfo);
+
+	if (ctx->devid == NULL) {
+		wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml");
+		return -1;
+	}
+
+	ctx->http = http_init_ctx(ctx, ctx->xml);
+	if (ctx->http == NULL) {
+		xml_node_deinit_ctx(ctx->xml);
+		return -1;
+	}
+	http_ocsp_set(ctx->http, 2);
+	http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
+
+	return 0;
+}
+
+
+static void deinit_ctx(struct hs20_osu_client *ctx)
+{
+	size_t i;
+
+	http_deinit_ctx(ctx->http);
+	xml_node_deinit_ctx(ctx->xml);
+	os_free(ctx->fqdn);
+	os_free(ctx->server_url);
+	os_free(ctx->devid);
+
+	for (i = 0; i < ctx->server_dnsname_count; i++)
+		os_free(ctx->server_dnsname[i]);
+	os_free(ctx->server_dnsname);
+}
+
+
+static void check_workarounds(struct hs20_osu_client *ctx)
+{
+	FILE *f;
+	char buf[100];
+	unsigned long int val = 0;
+
+	f = fopen("hs20-osu-client.workarounds", "r");
+	if (f == NULL)
+		return;
+
+	if (fgets(buf, sizeof(buf), f))
+		val = strtoul(buf, NULL, 16);
+
+	fclose(f);
+
+	if (val) {
+		wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val);
+		ctx->workarounds = val;
+		if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL)
+			http_ocsp_set(ctx->http, 1);
+	}
+}
+
+
+static void usage(void)
+{
+	printf("usage: hs20-osu-client [-dddqqKt] [-S<station ifname>] \\\n"
+	       "    [-w<wpa_supplicant ctrl_iface dir>] "
+	       "[-r<result file>] [-f<debug file>] \\\n"
+	       "    [-s<summary file>] \\\n"
+	       "    <command> [arguments..]\n"
+	       "commands:\n"
+	       "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
+	       "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) "
+	       "[URN]>\n"
+	       "- from_tnds <XML MO in TNDS format> <XML MO>\n"
+	       "- set_pps <PerProviderSubscription XML file name>\n"
+	       "- get_fqdn <PerProviderSubscription XML file name>\n"
+	       "- pol_upd [Server URL] [PPS] [CA cert]\n"
+	       "- sub_rem <Server URL> [PPS] [CA cert]\n"
+	       "- prov <Server URL> [CA cert]\n"
+	       "- oma_dm_prov <Server URL> [CA cert]\n"
+	       "- sim_prov <Server URL> [CA cert]\n"
+	       "- oma_dm_sim_prov <Server URL> [CA cert]\n"
+	       "- signup [CA cert]\n"
+	       "- dl_osu_ca <PPS> <CA file>\n"
+	       "- dl_polupd_ca <PPS> <CA file>\n"
+	       "- dl_aaa_ca <PPS> <CA file>\n"
+	       "- browser <URL>\n"
+	       "- parse_cert <X.509 certificate (DER)>\n"
+	       "- osu_select <OSU info directory> [CA cert]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct hs20_osu_client ctx;
+	int c;
+	int ret = 0;
+	int no_prod_assoc = 0;
+	const char *friendly_name = NULL;
+	const char *wpa_debug_file_path = NULL;
+	extern char *wpas_ctrl_path;
+	extern int wpa_debug_level;
+	extern int wpa_debug_show_keys;
+	extern int wpa_debug_timestamp;
+
+	if (init_ctx(&ctx) < 0)
+		return -1;
+
+	for (;;) {
+		c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'd':
+			if (wpa_debug_level > 0)
+				wpa_debug_level--;
+			break;
+		case 'f':
+			wpa_debug_file_path = optarg;
+			break;
+		case 'K':
+			wpa_debug_show_keys++;
+			break;
+		case 'N':
+			no_prod_assoc = 1;
+			break;
+		case 'O':
+			friendly_name = optarg;
+			break;
+		case 'q':
+			wpa_debug_level++;
+			break;
+		case 'r':
+			ctx.result_file = optarg;
+			break;
+		case 's':
+			ctx.summary_file = optarg;
+			break;
+		case 'S':
+			ctx.ifname = optarg;
+			break;
+		case 't':
+			wpa_debug_timestamp++;
+			break;
+		case 'w':
+			wpas_ctrl_path = optarg;
+			break;
+		case 'h':
+		default:
+			usage();
+			exit(0);
+			break;
+		}
+	}
+
+	if (argc - optind < 1) {
+		usage();
+		exit(0);
+	}
+
+	wpa_debug_open_file(wpa_debug_file_path);
+
+#ifdef __linux__
+	setlinebuf(stdout);
+#endif /* __linux__ */
+
+	if (ctx.result_file)
+		unlink(ctx.result_file);
+	wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======"
+		   "================", argv[optind]);
+	check_workarounds(&ctx);
+
+	if (strcmp(argv[optind], "to_tnds") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+			    argc > optind + 3 ? argv[optind + 3] : NULL,
+			    0);
+	} else if (strcmp(argv[optind], "to_tnds2") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+			    argc > optind + 3 ? argv[optind + 3] : NULL,
+			    1);
+	} else if (strcmp(argv[optind], "from_tnds") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "sub_rem") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		if (argc - optind < 2)
+			wpa_printf(MSG_ERROR, "Server URL missing from command line");
+		else
+			cmd_sub_rem(&ctx, argv[optind + 1],
+				    argc > optind + 2 ? argv[optind + 2] : NULL,
+				    argc > optind + 3 ? argv[optind + 3] :
+				    NULL);
+	} else if (strcmp(argv[optind], "pol_upd") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL,
+				  argc > optind + 2 ? argv[optind + 2] : NULL,
+				  argc > optind + 3 ? argv[optind + 3] : NULL);
+	} else if (strcmp(argv[optind], "prov") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ctx.ca_fname = argv[optind + 2];
+		cmd_prov(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "sim_prov") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ctx.ca_fname = argv[optind + 2];
+		cmd_sim_prov(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "osu_select") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
+		cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
+	} else if (strcmp(argv[optind], "signup") == 0) {
+		ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
+		ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
+	} else if (strcmp(argv[optind], "set_pps") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_set_pps(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "get_fqdn") == 0) {
+		if (argc - optind < 1) {
+			usage();
+			exit(0);
+		}
+		ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ctx.ca_fname = argv[optind + 2];
+		cmd_oma_dm_prov(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		ctx.ca_fname = argv[optind + 2];
+		if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) {
+			write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
+			return -1;
+		}
+	} else if (strcmp(argv[optind], "oma_dm_add") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
+	} else if (strcmp(argv[optind], "est_csr") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+		mkdir("Cert", S_IRWXU);
+		est_build_csr(&ctx, argv[optind + 1]);
+	} else if (strcmp(argv[optind], "browser") == 0) {
+		int ret;
+
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+
+		wpa_printf(MSG_INFO, "Launch web browser to URL %s",
+			   argv[optind + 1]);
+		ret = hs20_web_browser(argv[optind + 1]);
+		wpa_printf(MSG_INFO, "Web browser result: %d", ret);
+	} else if (strcmp(argv[optind], "parse_cert") == 0) {
+		if (argc - optind < 2) {
+			usage();
+			exit(0);
+		}
+
+		wpa_debug_level = MSG_MSGDUMP;
+		http_parse_x509_certificate(ctx.http, argv[optind + 1]);
+		wpa_debug_level = MSG_INFO;
+	} else {
+		wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
+	}
+
+	deinit_ctx(&ctx);
+	wpa_printf(MSG_DEBUG,
+		   "===[hs20-osu-client END ]======================");
+
+	wpa_debug_close_file();
+
+	return ret;
+}
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
new file mode 100644
index 0000000..9a7059e
--- /dev/null
+++ b/hs20/client/osu_client.h
@@ -0,0 +1,118 @@
+/*
+ * Hotspot 2.0 - OSU client
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OSU_CLIENT_H
+#define OSU_CLIENT_H
+
+#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
+
+#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
+#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
+#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+#define MAX_OSU_VALS 10
+
+struct osu_lang_text {
+	char lang[4];
+	char text[253];
+};
+
+struct hs20_osu_client {
+	struct xml_node_ctx *xml;
+	struct http_ctx *http;
+	int no_reconnect;
+	char pps_fname[300];
+	char *devid;
+	const char *result_file;
+	const char *summary_file;
+	const char *ifname;
+	const char *ca_fname;
+	int no_osu_cert_validation; /* for EST operations */
+	char *fqdn;
+	char *server_url;
+	struct osu_lang_text friendly_name[MAX_OSU_VALS];
+	size_t friendly_name_count;
+	size_t icon_count;
+	char icon_filename[MAX_OSU_VALS][256];
+	u8 icon_hash[MAX_OSU_VALS][32];
+	int pps_cred_set;
+	int pps_updated;
+	int client_cert_present;
+	char **server_dnsname;
+	size_t server_dnsname_count;
+#define WORKAROUND_OCSP_OPTIONAL 0x00000001
+	unsigned long int workarounds;
+};
+
+
+/* osu_client.c */
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+	__attribute__ ((format (printf, 2, 3)));
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+	__attribute__ ((format (printf, 2, 3)));
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+		     xml_node_t *node);
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+		    xml_node_t *add_mo, char *fname, size_t fname_len);
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+		 const char *alt_loc, char **user, char **pw);
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+		    xml_node_t *pps);
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+/* spp_client.c */
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+		 const char *pps_fname,
+		 const char *client_cert, const char *client_key,
+		 const char *cred_username, const char *cred_password,
+		 xml_node_t *pps);
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+		 const char *pps_fname,
+		 const char *client_cert, const char *client_key,
+		 const char *cred_username, const char *cred_password,
+		 xml_node_t *pps);
+int cmd_prov(struct hs20_osu_client *ctx, const char *url);
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
+
+
+/* oma_dm_client.c */
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+		    const char *pps_fname,
+		    const char *client_cert, const char *client_key,
+		    const char *cred_username, const char *cred_password,
+		    xml_node_t *pps);
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+		    const char *pps_fname,
+		    const char *client_cert, const char *client_key,
+		    const char *cred_username, const char *cred_password,
+		    xml_node_t *pps);
+void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+			const char *pps_fname);
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+		    const char *add_fname);
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+			const char *replace_fname);
+
+/* est.c */
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
+int est_build_csr(struct hs20_osu_client *ctx, const char *url);
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+		      const char *user, const char *pw);
+
+#endif /* OSU_CLIENT_H */
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
new file mode 100644
index 0000000..302a050
--- /dev/null
+++ b/hs20/client/spp_client.c
@@ -0,0 +1,995 @@
+/*
+ * Hotspot 2.0 SPP client
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/stat.h>
+
+#include "common.h"
+#include "browser.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+				    const char *session_id,
+				    const char *spp_status,
+				    const char *error_code);
+static void hs20_policy_update_complete(
+	struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+				 char *attr_name)
+{
+	return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
+}
+
+
+static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
+			     const char *expected_name)
+{
+	struct xml_node_ctx *xctx = ctx->xml;
+	const char *name;
+	char *err;
+	int ret;
+
+	if (!xml_node_is_element(xctx, node))
+		return -1;
+
+	name = xml_node_get_localname(xctx, node);
+	if (name == NULL)
+		return -1;
+
+	if (strcmp(expected_name, name) != 0) {
+		wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
+			   name, expected_name);
+		write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
+			      name, expected_name);
+		return -1;
+	}
+
+	ret = xml_validate(xctx, node, "spp.xsd", &err);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
+		write_summary(ctx, "SPP XML schema validation failed");
+		os_free(err);
+	}
+	return ret;
+}
+
+
+static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
+			     xml_node_t *parent, const char *urn,
+			     const char *fname)
+{
+	xml_node_t *node;
+	xml_node_t *fnode, *tnds;
+	char *str;
+
+	fnode = node_from_file(ctx, fname);
+	if (!fnode)
+		return;
+	tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
+	xml_node_free(ctx, fnode);
+	if (!tnds)
+		return;
+
+	str = xml_node_to_str(ctx, tnds);
+	xml_node_free(ctx, tnds);
+	if (str == NULL)
+		return;
+
+	node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
+	if (node)
+		xml_node_add_attr(ctx, node, ns, "moURN", urn);
+	os_free(str);
+}
+
+
+static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
+					    xml_namespace_t **ret_ns,
+					    const char *session_id,
+					    const char *reason)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node;
+
+	write_summary(ctx, "Building sppPostDevData requestReason='%s'",
+		      reason);
+	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+					"sppPostDevData");
+	if (spp_node == NULL)
+		return NULL;
+	if (ret_ns)
+		*ret_ns = ns;
+
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+	xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
+	if (session_id)
+		xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
+				  session_id);
+	xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
+			  "http://localhost:12345/");
+
+	xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
+			     "1.0");
+	xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
+			     URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
+			     URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
+
+	add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
+			 "devinfo.xml");
+	add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
+			 "devdetail.xml");
+
+	return spp_node;
+}
+
+
+static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
+			       xml_node_t *update)
+{
+	xml_node_t *node, *parent, *tnds, *unode;
+	char *str;
+	const char *name;
+	char *uri, *pos;
+	char *cdata, *cdata_end;
+	size_t fqdn_len;
+
+	wpa_printf(MSG_INFO, "Processing updateNode");
+	debug_dump_node(ctx, "updateNode", update);
+
+	uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
+	if (uri == NULL) {
+		wpa_printf(MSG_INFO, "No managementTreeURI present");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
+
+	name = os_strrchr(uri, '/');
+	if (name == NULL) {
+		wpa_printf(MSG_INFO, "Unexpected URI");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	name++;
+	wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
+
+	str = xml_node_get_text(ctx->xml, update);
+	if (str == NULL) {
+		wpa_printf(MSG_INFO, "Could not extract MO text");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
+	cdata = strstr(str, "<![CDATA[");
+	cdata_end = strstr(str, "]]>");
+	if (cdata && cdata_end && cdata_end > cdata &&
+	    cdata < strstr(str, "MgmtTree") &&
+	    cdata_end > strstr(str, "/MgmtTree")) {
+		char *tmp;
+		wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
+		tmp = strdup(cdata + 9);
+		if (tmp) {
+			cdata_end = strstr(tmp, "]]>");
+			if (cdata_end)
+				*cdata_end = '\0';
+			wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
+				   tmp);
+			tnds = xml_node_from_buf(ctx->xml, tmp);
+			free(tmp);
+		} else
+			tnds = NULL;
+	} else
+		tnds = xml_node_from_buf(ctx->xml, str);
+	xml_node_get_text_free(ctx->xml, str);
+	if (tnds == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+
+	unode = tnds_to_mo(ctx->xml, tnds);
+	xml_node_free(ctx->xml, tnds);
+	if (unode == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+
+	debug_dump_node(ctx, "Parsed TNDS", unode);
+
+	if (get_node_uri(ctx->xml, unode, name) == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+
+	if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	pos = uri + 8;
+
+	if (ctx->fqdn == NULL) {
+		wpa_printf(MSG_INFO, "FQDN not known");
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	fqdn_len = os_strlen(ctx->fqdn);
+	if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+	    pos[fqdn_len] != '/') {
+		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
+			   ctx->fqdn);
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	pos += fqdn_len + 1;
+
+	if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+		wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
+			   ctx->fqdn);
+		xml_node_free(ctx->xml, unode);
+		xml_node_get_attr_value_free(ctx->xml, uri);
+		return -1;
+	}
+	pos += 24;
+
+	wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
+
+	node = get_node(ctx->xml, pps, pos);
+	if (node) {
+		parent = xml_node_get_parent(ctx->xml, node);
+		xml_node_detach(ctx->xml, node);
+		wpa_printf(MSG_INFO, "Replace '%s' node", name);
+	} else {
+		char *pos2;
+		pos2 = os_strrchr(pos, '/');
+		if (pos2 == NULL) {
+			parent = pps;
+		} else {
+			*pos2 = '\0';
+			parent = get_node(ctx->xml, pps, pos);
+		}
+		if (parent == NULL) {
+			wpa_printf(MSG_INFO, "Could not find parent %s", pos);
+			xml_node_free(ctx->xml, unode);
+			xml_node_get_attr_value_free(ctx->xml, uri);
+			return -1;
+		}
+		wpa_printf(MSG_INFO, "Add '%s' node", name);
+	}
+	xml_node_add_child(ctx->xml, parent, unode);
+
+	xml_node_get_attr_value_free(ctx->xml, uri);
+
+	return 0;
+}
+
+
+static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
+		      const char *pps_fname, xml_node_t *pps)
+{
+	wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
+	xml_node_for_each_sibling(ctx->xml, update) {
+		xml_node_for_each_check(ctx->xml, update);
+		if (process_update_node(ctx, pps, update) < 0)
+			return -1;
+	}
+
+	return update_pps_file(ctx, pps_fname, pps);
+}
+
+
+static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
+				  const char *pps_fname)
+{
+	/*
+	 * Update wpa_supplicant credentials and reconnect using updated
+	 * information.
+	 */
+	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+	cmd_set_pps(ctx, pps_fname);
+
+	if (ctx->no_reconnect)
+		return;
+
+	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+		wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
+				       xml_node_t *cmd,
+				       const char *session_id,
+				       const char *pps_fname)
+{
+	xml_namespace_t *ns;
+	xml_node_t *node, *ret_node;
+	char *urn;
+
+	urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
+	if (!urn) {
+		wpa_printf(MSG_INFO, "No URN included");
+		return NULL;
+	}
+	wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
+	if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+		wpa_printf(MSG_INFO, "Unsupported moURN");
+		xml_node_get_attr_value_free(ctx->xml, urn);
+		return NULL;
+	}
+	xml_node_get_attr_value_free(ctx->xml, urn);
+
+	if (!pps_fname) {
+		wpa_printf(MSG_INFO, "PPS file name no known");
+		return NULL;
+	}
+
+	node = build_spp_post_dev_data(ctx, &ns, session_id,
+				       "MO upload");
+	if (node == NULL)
+		return NULL;
+	add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
+
+	ret_node = soap_send_receive(ctx->http, node);
+	if (ret_node == NULL)
+		return NULL;
+
+	debug_dump_node(ctx, "Received response to MO upload", ret_node);
+
+	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return NULL;
+	}
+
+	return ret_node;
+}
+
+
+static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
+		       char *fname, size_t fname_len)
+{
+	char *uri, *urn;
+	int ret;
+
+	debug_dump_node(ctx, "Received addMO", add_mo);
+
+	urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
+	if (urn == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
+	if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+		wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
+		xml_node_get_attr_value_free(ctx->xml, urn);
+		return -1;
+	}
+	xml_node_get_attr_value_free(ctx->xml, urn);
+
+	uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
+	if (uri == NULL) {
+		wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
+
+	ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
+	xml_node_get_attr_value_free(ctx->xml, uri);
+	return ret;
+}
+
+
+static int process_spp_user_input_response(struct hs20_osu_client *ctx,
+					   const char *session_id,
+					   xml_node_t *add_mo)
+{
+	int ret;
+	char fname[300];
+
+	debug_dump_node(ctx, "addMO", add_mo);
+
+	wpa_printf(MSG_INFO, "Subscription registration completed");
+
+	if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
+		wpa_printf(MSG_INFO, "Could not add MO");
+		ret = hs20_spp_update_response(
+			ctx, session_id,
+			"Error occurred",
+			"MO addition or update failed");
+		return 0;
+	}
+
+	ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
+	if (ret == 0)
+		hs20_sub_rem_complete(ctx, fname);
+
+	return 0;
+}
+
+
+static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
+						    const char *session_id)
+{
+	xml_node_t *node, *ret_node;
+
+	node = build_spp_post_dev_data(ctx, NULL, session_id,
+				       "User input completed");
+	if (node == NULL)
+		return NULL;
+
+	ret_node = soap_send_receive(ctx->http, node);
+	if (!ret_node) {
+		if (soap_reinit_client(ctx->http) < 0)
+			return NULL;
+		wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+		node = build_spp_post_dev_data(ctx, NULL, session_id,
+					       "User input completed");
+		if (node == NULL)
+			return NULL;
+		ret_node = soap_send_receive(ctx->http, node);
+		if (ret_node == NULL)
+			return NULL;
+		wpa_printf(MSG_INFO, "Continue with new connection");
+	}
+
+	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return NULL;
+	}
+
+	return ret_node;
+}
+
+
+static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
+					     xml_node_t *cmd,
+					     const char *session_id,
+					     const char *pps_fname)
+{
+	xml_namespace_t *ns;
+	xml_node_t *node, *ret_node;
+	int res;
+
+	wpa_printf(MSG_INFO, "Client certificate enrollment");
+
+	res = osu_get_certificate(ctx, cmd);
+	if (res < 0)
+		wpa_printf(MSG_INFO, "EST simpleEnroll failed");
+
+	node = build_spp_post_dev_data(ctx, &ns, session_id,
+				       res == 0 ?
+				       "Certificate enrollment completed" :
+				       "Certificate enrollment failed");
+	if (node == NULL)
+		return NULL;
+
+	ret_node = soap_send_receive(ctx->http, node);
+	if (ret_node == NULL)
+		return NULL;
+
+	debug_dump_node(ctx, "Received response to certificate enrollment "
+			"completed", ret_node);
+
+	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return NULL;
+	}
+
+	return ret_node;
+}
+
+
+static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
+			 const char *session_id, const char *pps_fname,
+			 xml_node_t *pps, xml_node_t **ret_node)
+{
+	xml_node_t *cmd;
+	const char *name;
+	char *uri;
+	char *id = strdup(session_id);
+
+	if (id == NULL)
+		return -1;
+
+	*ret_node = NULL;
+
+	debug_dump_node(ctx, "exec", exec);
+
+	xml_node_for_each_child(ctx->xml, cmd, exec) {
+		xml_node_for_each_check(ctx->xml, cmd);
+		break;
+	}
+	if (!cmd) {
+		wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
+			   cmd);
+		free(id);
+		return -1;
+	}
+
+	name = xml_node_get_localname(ctx->xml, cmd);
+
+	if (strcasecmp(name, "launchBrowserToURI") == 0) {
+		int res;
+		uri = xml_node_get_text(ctx->xml, cmd);
+		if (!uri) {
+			wpa_printf(MSG_INFO, "No URI found");
+			free(id);
+			return -1;
+		}
+		wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
+		write_summary(ctx, "Launch browser to URI '%s'", uri);
+		res = hs20_web_browser(uri);
+		xml_node_get_text_free(ctx->xml, uri);
+		if (res > 0) {
+			wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
+				   id);
+			write_summary(ctx, "User response in browser completed successfully");
+			*ret_node = hs20_spp_user_input_completed(ctx, id);
+			free(id);
+			return *ret_node ? 0 : -1;
+		} else {
+			wpa_printf(MSG_INFO, "Failed to receive user response");
+			write_summary(ctx, "Failed to receive user response");
+			hs20_spp_update_response(
+				ctx, id, "Error occurred", "Other");
+			free(id);
+			return -1;
+		}
+		return 0;
+	}
+
+	if (strcasecmp(name, "uploadMO") == 0) {
+		if (pps_fname == NULL)
+			return -1;
+		*ret_node = hs20_spp_upload_mo(ctx, cmd, id,
+					       pps_fname);
+		free(id);
+		return *ret_node ? 0 : -1;
+	}
+
+	if (strcasecmp(name, "getCertificate") == 0) {
+		*ret_node = hs20_spp_get_certificate(ctx, cmd, id,
+						     pps_fname);
+		free(id);
+		return *ret_node ? 0 : -1;
+	}
+
+	wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
+	free(id);
+	return -1;
+}
+
+
+enum spp_post_dev_data_use {
+	SPP_SUBSCRIPTION_REMEDIATION,
+	SPP_POLICY_UPDATE,
+	SPP_SUBSCRIPTION_REGISTRATION,
+};
+
+static void process_spp_post_dev_data_response(
+	struct hs20_osu_client *ctx,
+	enum spp_post_dev_data_use use, xml_node_t *node,
+	const char *pps_fname, xml_node_t *pps)
+{
+	xml_node_t *child;
+	char *status = NULL;
+	xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
+	char *session_id = NULL;
+
+	debug_dump_node(ctx, "sppPostDevDataResponse node", node);
+
+	status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+	if (status == NULL) {
+		wpa_printf(MSG_INFO, "No sppStatus attribute");
+		goto out;
+	}
+	write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
+		      status);
+
+	session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+	if (session_id == NULL) {
+		wpa_printf(MSG_INFO, "No sessionID attribute");
+		goto out;
+	}
+
+	wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s'  sessionID: '%s'",
+		   status, session_id);
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		const char *name;
+		xml_node_for_each_check(ctx->xml, child);
+		debug_dump_node(ctx, "child", child);
+		name = xml_node_get_localname(ctx->xml, child);
+		wpa_printf(MSG_INFO, "localname: '%s'", name);
+		if (!update && strcasecmp(name, "updateNode") == 0)
+			update = child;
+		if (!exec && strcasecmp(name, "exec") == 0)
+			exec = child;
+		if (!add_mo && strcasecmp(name, "addMO") == 0)
+			add_mo = child;
+		if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
+			no_mo = child;
+	}
+
+	if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+	    strcasecmp(status,
+		       "Remediation complete, request sppUpdateResponse") == 0)
+	{
+		int res, ret;
+		if (!update && !no_mo) {
+			wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
+			goto out;
+		}
+		wpa_printf(MSG_INFO, "Subscription remediation completed");
+		res = update_pps(ctx, update, pps_fname, pps);
+		if (res < 0)
+			wpa_printf(MSG_INFO, "Failed to update PPS MO");
+		ret = hs20_spp_update_response(
+			ctx, session_id,
+			res < 0 ? "Error occurred" : "OK",
+			res < 0 ? "MO addition or update failed" : NULL);
+		if (res == 0 && ret == 0)
+			hs20_sub_rem_complete(ctx, pps_fname);
+		goto out;
+	}
+
+	if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+	    strcasecmp(status, "Exchange complete, release TLS connection") ==
+	    0) {
+		if (!no_mo) {
+			wpa_printf(MSG_INFO, "No noMOUpdate element");
+			goto out;
+		}
+		wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
+		goto out;
+	}
+
+	if (use == SPP_POLICY_UPDATE &&
+	    strcasecmp(status, "Update complete, request sppUpdateResponse") ==
+	    0) {
+		int res, ret;
+		wpa_printf(MSG_INFO, "Policy update received - update PPS");
+		res = update_pps(ctx, update, pps_fname, pps);
+		ret = hs20_spp_update_response(
+			ctx, session_id,
+			res < 0 ? "Error occurred" : "OK",
+			res < 0 ? "MO addition or update failed" : NULL);
+		if (res == 0 && ret == 0)
+			hs20_policy_update_complete(ctx, pps_fname);
+		goto out;
+	}
+
+	if (use == SPP_SUBSCRIPTION_REGISTRATION &&
+	    strcasecmp(status, "Provisioning complete, request "
+		       "sppUpdateResponse")  == 0) {
+		if (!add_mo) {
+			wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
+			goto out;
+		}
+		process_spp_user_input_response(ctx, session_id, add_mo);
+		node = NULL;
+		goto out;
+	}
+
+	if (strcasecmp(status, "No update available at this time") == 0) {
+		wpa_printf(MSG_INFO, "No update available at this time");
+		goto out;
+	}
+
+	if (strcasecmp(status, "OK") == 0) {
+		int res;
+		xml_node_t *ret;
+
+		if (!exec) {
+			wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
+			goto out;
+		}
+		res = hs20_spp_exec(ctx, exec, session_id,
+				    pps_fname, pps, &ret);
+		/* xml_node_free(ctx->xml, node); */
+		node = NULL;
+		if (res == 0 && ret)
+			process_spp_post_dev_data_response(ctx, use,
+							   ret, pps_fname, pps);
+		goto out;
+	}
+
+	if (strcasecmp(status, "Error occurred") == 0) {
+		xml_node_t *err;
+		char *code = NULL;
+		err = get_node(ctx->xml, node, "sppError");
+		if (err)
+			code = xml_node_get_attr_value(ctx->xml, err,
+						       "errorCode");
+		wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
+			   code ? code : "N/A");
+		xml_node_get_attr_value_free(ctx->xml, code);
+		goto out;
+	}
+
+	wpa_printf(MSG_INFO,
+		   "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
+		   status);
+out:
+	xml_node_get_attr_value_free(ctx->xml, status);
+	xml_node_get_attr_value_free(ctx->xml, session_id);
+	xml_node_free(ctx->xml, node);
+}
+
+
+static int spp_post_dev_data(struct hs20_osu_client *ctx,
+			     enum spp_post_dev_data_use use,
+			     const char *reason,
+			     const char *pps_fname, xml_node_t *pps)
+{
+	xml_node_t *payload;
+	xml_node_t *ret_node;
+
+	payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
+	if (payload == NULL)
+		return -1;
+
+	ret_node = soap_send_receive(ctx->http, payload);
+	if (!ret_node) {
+		const char *err = http_get_err(ctx->http);
+		if (err) {
+			wpa_printf(MSG_INFO, "HTTP error: %s", err);
+			write_result(ctx, "HTTP error: %s", err);
+		} else {
+			write_summary(ctx, "Failed to send SOAP message");
+		}
+		return -1;
+	}
+
+	if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return -1;
+	}
+
+	process_spp_post_dev_data_response(ctx, use, ret_node,
+					   pps_fname, pps);
+	return 0;
+}
+
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+		 const char *pps_fname,
+		 const char *client_cert, const char *client_key,
+		 const char *cred_username, const char *cred_password,
+		 xml_node_t *pps)
+{
+	wpa_printf(MSG_INFO, "SPP subscription remediation");
+	write_summary(ctx, "SPP subscription remediation");
+
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(address);
+
+	if (soap_init_client(ctx->http, address, ctx->ca_fname,
+			     cred_username, cred_password, client_cert,
+			     client_key) == 0) {
+		spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
+				  "Subscription remediation", pps_fname, pps);
+	}
+}
+
+
+static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
+					const char *pps_fname)
+{
+	wpa_printf(MSG_INFO, "Policy update completed");
+
+	/*
+	 * Update wpa_supplicant credentials and reconnect using updated
+	 * information.
+	 */
+	wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+	cmd_set_pps(ctx, pps_fname);
+
+	wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+	if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+		wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
+					 xml_node_t *node)
+{
+	char *status, *session_id;
+
+	debug_dump_node(ctx, "sppExchangeComplete", node);
+
+	status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+	if (status == NULL) {
+		wpa_printf(MSG_INFO, "No sppStatus attribute");
+		return -1;
+	}
+	write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
+		      status);
+
+	session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+	if (session_id == NULL) {
+		wpa_printf(MSG_INFO, "No sessionID attribute");
+		xml_node_get_attr_value_free(ctx->xml, status);
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s'  sessionID: '%s'",
+		   status, session_id);
+	xml_node_get_attr_value_free(ctx->xml, session_id);
+
+	if (strcasecmp(status, "Exchange complete, release TLS connection") ==
+	    0) {
+		xml_node_get_attr_value_free(ctx->xml, status);
+		return 0;
+	}
+
+	wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
+	write_summary(ctx, "Unexpected sppStatus '%s'", status);
+	xml_node_get_attr_value_free(ctx->xml, status);
+	return -1;
+}
+
+
+static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
+					      const char *session_id,
+					      const char *spp_status,
+					      const char *error_code)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *node;
+
+	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+					"sppUpdateResponse");
+	if (spp_node == NULL)
+		return NULL;
+
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
+
+	if (error_code) {
+		node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+		if (node)
+			xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+					  error_code);
+	}
+
+	return spp_node;
+}
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+				    const char *session_id,
+				    const char *spp_status,
+				    const char *error_code)
+{
+	xml_node_t *node, *ret_node;
+	int ret;
+
+	write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
+		      spp_status, error_code);
+	node = build_spp_update_response(ctx, session_id, spp_status,
+					 error_code);
+	if (node == NULL)
+		return -1;
+	ret_node = soap_send_receive(ctx->http, node);
+	if (!ret_node) {
+		if (soap_reinit_client(ctx->http) < 0)
+			return -1;
+		wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+		node = build_spp_update_response(ctx, session_id, spp_status,
+						 error_code);
+		if (node == NULL)
+			return -1;
+		ret_node = soap_send_receive(ctx->http, node);
+		if (ret_node == NULL)
+			return -1;
+		wpa_printf(MSG_INFO, "Continue with new connection");
+	}
+
+	if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
+		wpa_printf(MSG_INFO, "SPP validation failed");
+		xml_node_free(ctx->xml, ret_node);
+		return -1;
+	}
+
+	ret = process_spp_exchange_complete(ctx, ret_node);
+	xml_node_free(ctx->xml, ret_node);
+	return ret;
+}
+
+
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+		 const char *pps_fname,
+		 const char *client_cert, const char *client_key,
+		 const char *cred_username, const char *cred_password,
+		 xml_node_t *pps)
+{
+	wpa_printf(MSG_INFO, "SPP policy update");
+	write_summary(ctx, "SPP policy update");
+
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(address);
+
+	if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
+			     cred_password, client_cert, client_key) == 0) {
+		spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
+				  pps_fname, pps);
+	}
+}
+
+
+int cmd_prov(struct hs20_osu_client *ctx, const char *url)
+{
+	unlink("Cert/est_cert.der");
+	unlink("Cert/est_cert.pem");
+
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "Credential provisioning requested");
+
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(url);
+
+	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+			     NULL) < 0)
+		return -1;
+	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+			  "Subscription registration", NULL, NULL);
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+	if (url == NULL) {
+		wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "SIM provisioning requested");
+
+	os_free(ctx->server_url);
+	ctx->server_url = os_strdup(url);
+
+	wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+
+	if (wait_ip_addr(ctx->ifname, 15) < 0) {
+		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+	}
+
+	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+			     NULL) < 0)
+		return -1;
+	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+			  "Subscription provisioning", NULL, NULL);
+
+	return ctx->pps_cred_set ? 0 : -1;
+}
diff --git a/hs20/server/Makefile b/hs20/server/Makefile
new file mode 100644
index 0000000..587633b
--- /dev/null
+++ b/hs20/server/Makefile
@@ -0,0 +1,45 @@
+all: hs20_spp_server
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/crypto
+
+LIBS += -lsqlite3
+
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+
+OBJS=spp_server.o
+OBJS += hs20_spp_server.o
+OBJS += ../../src/utils/xml-utils.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/utils/os_unix.o
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/crypto/md5-internal.o
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+OBJS += ../../src/utils/xml_libxml2.o
+
+hs20_spp_server: $(OBJS)
+	$(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
+
+clean:
+	rm -f core *~ *.o *.d hs20_spp_server
+	rm -f ../../src/utils/*.o
+	rm -f ../../src/utils/*.d
+	rm -f ../../src/crypto/*.o
+	rm -f ../../src/crypto/*.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hs20/server/ca/clean.sh b/hs20/server/ca/clean.sh
new file mode 100644
index 0000000..c69a1f5
--- /dev/null
+++ b/hs20/server/ca/clean.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+for i in server-client server server-revoked user ocsp; do
+    rm -f $i.csr $i.key $i.pem
+done
+
+rm -f openssl.cnf.tmp
+rm -r demoCA
+rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
+#rm -r rootCA
diff --git a/hs20/server/ca/est-csrattrs.cnf b/hs20/server/ca/est-csrattrs.cnf
new file mode 100644
index 0000000..b50ea00
--- /dev/null
+++ b/hs20/server/ca/est-csrattrs.cnf
@@ -0,0 +1,17 @@
+asn1 = SEQUENCE:attrs
+
+[attrs]
+#oid1 = OID:challengePassword
+attr1 = SEQUENCE:extreq
+oid2 = OID:sha256WithRSAEncryption
+
+[extreq]
+oid = OID:extensionRequest
+vals = SET:extreqvals
+
+[extreqvals]
+
+oid1 = OID:macAddress
+#oid2 = OID:imei
+#oid3 = OID:meid
+#oid4 = OID:DevId
diff --git a/hs20/server/ca/est-csrattrs.sh b/hs20/server/ca/est-csrattrs.sh
new file mode 100644
index 0000000..0b73a04
--- /dev/null
+++ b/hs20/server/ca/est-csrattrs.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+openssl asn1parse -genconf est-csrattrs.cnf -out est-csrattrs.der -oid hs20.oid
+base64 est-csrattrs.der > est-attrs.b64
diff --git a/hs20/server/ca/hs20.oid b/hs20/server/ca/hs20.oid
new file mode 100644
index 0000000..a829ff2
--- /dev/null
+++ b/hs20/server/ca/hs20.oid
@@ -0,0 +1,7 @@
+1.3.6.1.1.1.1.22 macAddress
+1.2.840.113549.1.9.14 extensionRequest
+1.3.6.1.4.1.40808.1.1.1 id-wfa-hotspot-friendlyName
+1.3.6.1.4.1.40808.1.1.2 id-kp-HS2.0Auth
+1.3.6.1.4.1.40808.1.1.3 imei
+1.3.6.1.4.1.40808.1.1.4 meid
+1.3.6.1.4.1.40808.1.1.5 DevId
diff --git a/hs20/server/ca/ocsp-req.sh b/hs20/server/ca/ocsp-req.sh
new file mode 100644
index 0000000..931a206
--- /dev/null
+++ b/hs20/server/ca/ocsp-req.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+for i in *.pem; do
+    echo "===[ $i ]==================="
+    openssl ocsp -text -CAfile ca.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+#    openssl ocsp -text -CAfile rootCA/cacert.pem -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+#    openssl ocsp -text -CAfile rootCA/cacert.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+#    openssl ocsp -text -CAfile rootCA/cacert.pem -VAfile ca.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+done
diff --git a/hs20/server/ca/ocsp-responder-ica.sh b/hs20/server/ca/ocsp-responder-ica.sh
new file mode 100644
index 0000000..116c6e1
--- /dev/null
+++ b/hs20/server/ca/ocsp-responder-ica.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner demoCA/cacert.pem -rkey demoCA/private/cakey-plain.pem -CA demoCA/cacert.pem -resp_no_certs -text
diff --git a/hs20/server/ca/ocsp-responder.sh b/hs20/server/ca/ocsp-responder.sh
new file mode 100644
index 0000000..8cebd74
--- /dev/null
+++ b/hs20/server/ca/ocsp-responder.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner ocsp.pem -rkey ocsp.key -CA demoCA/cacert.pem -text
diff --git a/hs20/server/ca/ocsp-update-cache.sh b/hs20/server/ca/ocsp-update-cache.sh
new file mode 100644
index 0000000..8ddef9b
--- /dev/null
+++ b/hs20/server/ca/ocsp-update-cache.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+openssl ocsp \
+	-no_nonce \
+	-CAfile ca.pem \
+	-verify_other demoCA/cacert.pem \
+	-issuer demoCA/cacert.pem \
+	-cert server.pem \
+	-url http://localhost:8888/ \
+	-respout ocsp-server-cache.der
diff --git a/hs20/server/ca/openssl-root.cnf b/hs20/server/ca/openssl-root.cnf
new file mode 100644
index 0000000..5b220fe
--- /dev/null
+++ b/hs20/server/ca/openssl-root.cnf
@@ -0,0 +1,125 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Root CA)
+
+HOME			= .
+RANDFILE		= $ENV::HOME/.rnd
+oid_section		= new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca	= CA_default		# The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir		= ./rootCA		# Where everything is kept
+certs		= $dir/certs		# Where the issued certs are kept
+crl_dir		= $dir/crl		# Where the issued crl are kept
+database	= $dir/index.txt	# database index file.
+#unique_subject	= no			# Set to 'no' to allow creation of
+					# several certificates with same subject
+new_certs_dir	= $dir/newcerts		# default place for new certs.
+
+certificate	= $dir/cacert.pem 	# The CA certificate
+serial		= $dir/serial 		# The current serial number
+crlnumber	= $dir/crlnumber	# the current crl number
+					# must be commented out to leave a V1 CRL
+crl		= $dir/crl.pem 		# The current CRL
+private_key	= $dir/private/cakey.pem# The private key
+RANDFILE	= $dir/private/.rand	# private random number file
+
+x509_extensions	= usr_cert		# The extentions to add to the cert
+
+name_opt 	= ca_default		# Subject Name options
+cert_opt 	= ca_default		# Certificate field options
+
+default_days	= 365			# how long to certify for
+default_crl_days= 30			# how long before next CRL
+default_md	= default		# use public key default MD
+preserve	= no			# keep passed DN ordering
+
+policy		= policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName		= match
+stateOrProvinceName	= optional
+organizationName	= match
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+[ policy_anything ]
+countryName		= optional
+stateOrProvinceName	= optional
+localityName		= optional
+organizationName	= optional
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+####################################################################
+[ req ]
+default_bits		= 2048
+default_keyfile 	= privkey.pem
+distinguished_name	= req_distinguished_name
+attributes		= req_attributes
+x509_extensions	= v3_ca	# The extentions to add to the self signed cert
+
+input_password = whatever
+output_password = whatever
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName			= Country Name (2 letter code)
+countryName_default		= US
+countryName_min			= 2
+countryName_max			= 2
+
+localityName			= Locality Name (eg, city)
+localityName_default		= Tuusula
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= WFA Hotspot 2.0
+
+##organizationalUnitName		= Organizational Unit Name (eg, section)
+#organizationalUnitName_default	=
+#@OU@
+
+commonName			= Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max			= 64
+
+emailAddress			= Email Address
+emailAddress_max		= 64
+
+[ req_attributes ]
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName=DNS:example.com,DNS:another.example.com
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+basicConstraints = critical,CA:true
+keyUsage = critical, cRLSign, keyCertSign
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
diff --git a/hs20/server/ca/openssl.cnf b/hs20/server/ca/openssl.cnf
new file mode 100644
index 0000000..a939f08
--- /dev/null
+++ b/hs20/server/ca/openssl.cnf
@@ -0,0 +1,200 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Intermediate CA)
+
+HOME			= .
+RANDFILE		= $ENV::HOME/.rnd
+oid_section		= new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca	= CA_default		# The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir		= ./demoCA		# Where everything is kept
+certs		= $dir/certs		# Where the issued certs are kept
+crl_dir		= $dir/crl		# Where the issued crl are kept
+database	= $dir/index.txt	# database index file.
+#unique_subject	= no			# Set to 'no' to allow creation of
+					# several certificates with same subject
+new_certs_dir	= $dir/newcerts		# default place for new certs.
+
+certificate	= $dir/cacert.pem 	# The CA certificate
+serial		= $dir/serial 		# The current serial number
+crlnumber	= $dir/crlnumber	# the current crl number
+					# must be commented out to leave a V1 CRL
+crl		= $dir/crl.pem 		# The current CRL
+private_key	= $dir/private/cakey.pem# The private key
+RANDFILE	= $dir/private/.rand	# private random number file
+
+x509_extensions	= ext_client		# The extentions to add to the cert
+
+name_opt 	= ca_default		# Subject Name options
+cert_opt 	= ca_default		# Certificate field options
+
+# Extension copying option: use with caution.
+copy_extensions = copy
+
+default_days	= 365			# how long to certify for
+default_crl_days= 30			# how long before next CRL
+default_md	= default		# use public key default MD
+preserve	= no			# keep passed DN ordering
+
+policy		= policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName		= supplied
+stateOrProvinceName	= optional
+organizationName	= supplied
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+[ policy_osu_server ]
+countryName		= match
+stateOrProvinceName	= optional
+organizationName	= match
+organizationalUnitName	= supplied
+commonName		= supplied
+emailAddress		= optional
+
+[ policy_anything ]
+countryName		= optional
+stateOrProvinceName	= optional
+localityName		= optional
+organizationName	= optional
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+####################################################################
+[ req ]
+default_bits		= 2048
+default_keyfile 	= privkey.pem
+distinguished_name	= req_distinguished_name
+attributes		= req_attributes
+x509_extensions	= v3_ca	# The extentions to add to the self signed cert
+
+input_password = whatever
+output_password = whatever
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName			= Country Name (2 letter code)
+countryName_default		= FI
+countryName_min			= 2
+countryName_max			= 2
+
+localityName			= Locality Name (eg, city)
+localityName_default		= Tuusula
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= w1.fi
+
+##organizationalUnitName		= Organizational Unit Name (eg, section)
+#organizationalUnitName_default	=
+#@OU@
+
+commonName			= Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max			= 64
+
+emailAddress			= Email Address
+emailAddress_max		= 64
+
+[ req_attributes ]
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, cRLSign, keyCertSign
+authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+# For SP intermediate CA
+#subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
+#nameConstraints=permitted;DNS:.w1.fi
+#1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+
+[ v3_osu_server ]
+
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, keyEncipherment
+#@ALTNAME@
+
+#logotypeoid=ASN1:SEQUENCE:LogotypeExtn
+1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+[LogotypeExtn]
+communityLogos=EXP:0,SEQUENCE:LogotypeInfo
+[LogotypeInfo]
+# note: implicit tag converted to explicit for CHOICE
+direct=EXP:0,SEQUENCE:LogotypeData
+[LogotypeData]
+image=SEQUENCE:LogotypeImage
+[LogotypeImage]
+imageDetails=SEQUENCE:LogotypeDetails
+imageInfo=SEQUENCE:LogotypeImageInfo
+[LogotypeDetails]
+mediaType=IA5STRING:image/png
+logotypeHash=SEQUENCE:HashAlgAndValues
+logotypeURI=SEQUENCE:URI
+[HashAlgAndValues]
+value1=SEQUENCE:HashAlgAndValueSHA256
+#value2=SEQUENCE:HashAlgAndValueSHA1
+[HashAlgAndValueSHA256]
+hashAlg=SEQUENCE:sha256_alg
+hashValue=FORMAT:HEX,OCTETSTRING:4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d
+[HashAlgAndValueSHA1]
+hashAlg=SEQUENCE:sha1_alg
+hashValue=FORMAT:HEX,OCTETSTRING:5e1d5085676eede6b02da14d31c523ec20ffba0b
+[sha256_alg]
+algorithm=OID:sha256
+[sha1_alg]
+algorithm=OID:sha1
+[URI]
+uri=IA5STRING:http://osu.w1.fi/w1fi_logo.png
+[LogotypeImageInfo]
+# default value color(1), component optional
+#type=IMP:0,INTEGER:1
+fileSize=INTEGER:7549
+xSize=INTEGER:128
+ySize=INTEGER:80
+language=IMP:4,IA5STRING:zxx
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+#@ALTNAME@
+extendedKeyUsage = clientAuth
+
+[ ext_server ]
+
+# Hotspot 2.0 PKI requirements
+basicConstraints=critical, CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+#@ALTNAME@
+extendedKeyUsage = critical, serverAuth
+keyUsage = critical, keyEncipherment
diff --git a/hs20/server/ca/setup.sh b/hs20/server/ca/setup.sh
new file mode 100644
index 0000000..f61bf73
--- /dev/null
+++ b/hs20/server/ca/setup.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+if [ -z "$OPENSSL" ]; then
+    OPENSSL=openssl
+fi
+export OPENSSL_CONF=$PWD/openssl.cnf
+PASS=whatever
+
+fail()
+{
+    echo "$*"
+    exit 1
+}
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+cat openssl-root.cnf | sed "s/#@CN@/commonName_default = Hotspot 2.0 Trust Root CA - 99/" > openssl.cnf.tmp
+mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
+touch rootCA/index.txt
+if [ -e rootCA/private/cakey.pem ]; then
+    echo " * Use existing Root CA"
+else
+    echo " * Generate Root CA private key"
+    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
+    echo " * Sign Root CA certificate"
+    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
+fi
+if [ ! -e rootCA/crlnumber ]; then
+    echo 00 > rootCA/crlnumber
+fi
+
+echo
+echo "---[ Intermediate CA ]--------------------------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = w1.fi Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
+mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
+touch demoCA/index.txt
+if [ -e demoCA/private/cakey.pem ]; then
+    echo " * Use existing Intermediate CA"
+else
+    echo " * Generate Intermediate CA private key"
+    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
+    echo " * Sign Intermediate CA certificate"
+    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
+    # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
+    openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
+fi
+if [ ! -e demoCA/crlnumber ]; then
+    echo 00 > demoCA/crlnumber
+fi
+
+echo
+echo "OCSP responder"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = ocsp.w1.fi/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP
+
+echo
+echo "---[ Server - to be revoked ] ------------------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = osu-revoked.w1.fi/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
+$OPENSSL ca -revoke server-revoked.pem -key $PASS
+
+echo
+echo "---[ Server - with client ext key use ] ---------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = osu-client.w1.fi/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+ALT="DNS:osu.w1.fi"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engw1.fi TESTING USE"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:finw1.fi TESTIKÄYTTÖ"
+
+cat openssl.cnf |
+	sed "s/#@CN@/commonName_default = osu.w1.fi/" |
+	sed "s/^##organizationalUnitName/organizationalUnitName/" |
+	sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
+	sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
+	> openssl.cnf.tmp
+echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
+
+#dump logotype details for debugging
+$OPENSSL x509 -in server.pem -out server.der -outform DER
+openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
+openssl asn1parse -in logo.der -inform DER > logo.asn1
+
+
+echo
+echo "---[ CRL ]---------------------------------------------------------------"
+echo
+
+$OPENSSL ca -config $PWD/openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
+
+echo
+echo "---[ Verify ]------------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
+$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
+
+cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
diff --git a/hs20/server/ca/w1fi_logo.png b/hs20/server/ca/w1fi_logo.png
new file mode 100644
index 0000000..ac7c259
--- /dev/null
+++ b/hs20/server/ca/w1fi_logo.png
Binary files differ
diff --git a/hs20/server/hs20-osu-server.txt b/hs20/server/hs20-osu-server.txt
new file mode 100644
index 0000000..80985f7
--- /dev/null
+++ b/hs20/server/hs20-osu-server.txt
@@ -0,0 +1,196 @@
+Hotspot 2.0 OSU server
+======================
+
+The information in this document is based on the assumption that Ubuntu
+12.04 server (64-bit) distribution is used and the web server is
+Apache2. Neither of these are requirements for the installation, but if
+other combinations are used, the package names and configuration
+parameters may need to be adjusted.
+
+NOTE: This implementation and the example configuration here is meant
+only for testing purposes in a lab environment. This design is not
+secure to be installed in a publicly available Internet server without
+considerable amount of modification and review for security issues.
+
+NOTE: While this describes use on Ubuntu 12.04, the version of Apache2
+included in that distribution is not new enough to support all OSU
+server validation steps. In other words, it may be most adapt the steps
+described here to Ubuntu 13.10.
+
+
+Build dependencies
+------------------
+
+Ubuntu 12.04 server
+- default installation
+- upgraded to latest package versions
+  sudo apt-get update
+  sudo apt-get upgrade
+
+Packages needed for running the service:
+  sudo apt-get install sqlite3
+  sudo apt-get install apache2
+  sudo apt-get install php5-sqlite libapache2-mod-php5
+
+Additional packages needed for building the components:
+  sudo apt-get install build-essential
+  sudo apt-get install libsqlite3-dev
+  sudo apt-get install libssl-dev
+  sudo apt-get install libxml2-dev
+
+
+Installation location
+---------------------
+
+Select a location for the installation root directory. The example here
+assumes /home/user/hs20-server to be used, but this can be changed by
+editing couple of files as indicated below.
+
+sudo mkdir -p /home/user/hs20-server
+sudo chown $USER /home/user/hs20-server
+mkdir -p /home/user/hs20-server/spp
+mkdir -p /home/user/hs20-server/AS
+
+
+Build
+-----
+
+# hostapd as RADIUS server
+cd hostapd
+
+#example build configuration
+cat > .config <<EOF
+CONFIG_DRIVER_NONE=y
+CONFIG_PKCS12=y
+CONFIG_RADIUS_SERVER=y
+CONFIG_EAP=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA_PRIME=y
+CONFIG_SQLITE=y
+CONFIG_HS20=y
+EOF
+
+make hostapd hlr_auc_gw
+cp hostapd hlr_auc_gw /home/user/hs20-server/AS
+
+# build hs20_spp_server
+cd ../hs20/server
+make clean
+make
+cp hs20_spp_server /home/user/hs20-server/spp
+# prepare database (web server user/group needs to have write access)
+mkdir -p /home/user/hs20-server/AS/DB
+sudo chgrp www-data /home/user/hs20-server/AS/DB
+sudo chmod g+w /home/user/hs20-server/AS/DB
+sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql.txt
+sudo chgrp www-data /home/user/hs20-server/AS/DB/eap_user.db
+sudo chmod g+w /home/user/hs20-server/AS/DB/eap_user.db
+# add example configuration (note: need to update URLs to match the system)
+sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql-example.txt
+
+# copy PHP scripts
+# Modify config.php if different installation directory is used.
+# Modify PHP scripts to get the desired behavior for user interaction (or use
+# the examples as-is for initial testing).
+cp -r www /home/user/hs20-server
+
+
+# Configure subscription policies
+mkdir -p /home/user/hs20-server/spp/policy
+cat > /home/user/hs20-server/spp/policy/default.xml <<EOF
+<Policy>
+	<PolicyUpdate>
+		<UpdateInterval>30</UpdateInterval>
+		<UpdateMethod>ClientInitiated</UpdateMethod>
+		<Restriction>Unrestricted</Restriction>
+		<URI>https://policy-server.osu.example.com/hs20/spp.php</URI>
+	</PolicyUpdate>
+</Policy>
+EOF
+
+
+# Install Hotspot 2.0 SPP and OMA DM XML schema/DTD files
+
+# XML schema for SPP
+# Copy the latest XML schema into /home/user/hs20-server/spp/spp.xsd
+
+# OMA DM Device Description Framework DTD
+# Copy into /home/user/hs20-server/spp/dm_ddf-v1_2.dtd
+# http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd
+
+
+# Configure RADIUS authentication service
+# Note: Change the URL to match the setup
+# Note: Install AAA server key/certificate and root CA in Key directory
+
+cat > /home/user/hs20-server/AS/as-sql.conf <<EOF
+driver=none
+radius_server_clients=as.radius_clients
+eap_server=1
+eap_user_file=sqlite:DB/eap_user.db
+ca_cert=Key/ca.pem
+server_cert=Key/server.pem
+private_key=Key/server.key
+private_key_passwd=passphrase
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=eap_sim.db
+subscr_remediation_url=https://subscription-server.osu.example.com/hs20/spp.php
+EOF
+
+# Set RADIUS passphrase for the APs
+# Note: Modify to match the setup
+cat > /home/user/hs20-server/AS/as.radius_clients <<EOF
+0.0.0.0/0	radius
+EOF
+
+
+Start RADIUS authentication server
+----------------------------------
+
+cd /home/user/hs20-server/AS
+./hostapd -B as-sql.conf
+
+
+Configure web server
+--------------------
+
+Edit /etc/apache2/sites-available/default-ssl
+
+Add following block just before "SSL Engine Switch" line":
+
+        Alias /hs20/ "/home/user/hs20-server/www/"
+        <Directory "/home/user/hs20-server/www/">
+                Options Indexes MultiViews FollowSymLinks
+                AllowOverride None
+                Order allow,deny
+                Allow from all
+        </Directory>
+
+Update SSL configuration to use the OSU server certificate/key.
+
+Enable default-ssl site and restart Apache2:
+  sudo a2ensite default-ssl
+  sudo a2enmod ssl
+  sudo service apache2 restart
+
+
+Management UI
+-------------
+
+The sample PHP scripts include a management UI for testing
+purposes. That is available at https://<server>/hs20/users.php
+
+
+AP configuration
+----------------
+
+APs can now be configured to use the OSU server as the RADIUS
+authentication server. In addition, the OSU Provider List ANQP element
+should be configured to use the SPP (SOAP+XML) option and with the
+following Server URL:
+https://<server>/hs20/spp.php/signup?realm=example.com
diff --git a/hs20/server/hs20_spp_server.c b/hs20/server/hs20_spp_server.c
new file mode 100644
index 0000000..591f66b
--- /dev/null
+++ b/hs20/server/hs20_spp_server.c
@@ -0,0 +1,187 @@
+/*
+ * Hotspot 2.0 SPP server - standalone version
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sqlite3.h>
+
+#include "common.h"
+#include "xml-utils.h"
+#include "spp_server.h"
+
+
+static void write_timestamp(FILE *f)
+{
+	time_t t;
+	struct tm *tm;
+
+	time(&t);
+	tm = localtime(&t);
+
+	fprintf(f, "%04u-%02u-%02u %02u:%02u:%02u ",
+		tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+		tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+{
+	va_list ap;
+
+	if (ctx->debug_log == NULL)
+		return;
+
+	write_timestamp(ctx->debug_log);
+	va_start(ap, fmt);
+	vfprintf(ctx->debug_log, fmt, ap);
+	va_end(ap);
+
+	fprintf(ctx->debug_log, "\n");
+}
+
+
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node)
+{
+	char *str;
+
+	if (ctx->debug_log == NULL)
+		return;
+	str = xml_node_to_str(ctx->xml, node);
+	if (str == NULL)
+		return;
+
+	write_timestamp(ctx->debug_log);
+	fprintf(ctx->debug_log, "%s: '%s'\n", title, str);
+	os_free(str);
+}
+
+
+static int process(struct hs20_svc *ctx)
+{
+	int dmacc = 0;
+	xml_node_t *soap, *spp, *resp;
+	char *user, *realm, *post, *str;
+
+	ctx->addr = getenv("HS20ADDR");
+	if (ctx->addr)
+		debug_print(ctx, 1, "Connection from %s", ctx->addr);
+
+	user = getenv("HS20USER");
+	if (user && strlen(user) == 0)
+		user = NULL;
+	realm = getenv("HS20REALM");
+	if (realm == NULL) {
+		debug_print(ctx, 1, "HS20REALM not set");
+		return -1;
+	}
+	post = getenv("HS20POST");
+	if (post == NULL) {
+		debug_print(ctx, 1, "HS20POST not set");
+		return -1;
+	}
+
+	soap = xml_node_from_buf(ctx->xml, post);
+	if (soap == NULL) {
+		debug_print(ctx, 1, "Could not parse SOAP data");
+		return -1;
+	}
+	debug_dump_node(ctx, "Received SOAP message", soap);
+	spp = soap_get_body(ctx->xml, soap);
+	if (spp == NULL) {
+		debug_print(ctx, 1, "Could not get SPP message");
+		xml_node_free(ctx->xml, soap);
+		return -1;
+	}
+	debug_dump_node(ctx, "Received SPP message", spp);
+
+	resp = hs20_spp_server_process(ctx, spp, user, realm, dmacc);
+	xml_node_free(ctx->xml, soap);
+	if (resp == NULL && user == NULL) {
+		debug_print(ctx, 1, "Request HTTP authentication");
+		return 2; /* Request authentication */
+	}
+	if (resp == NULL) {
+		debug_print(ctx, 1, "No response");
+		return -1;
+	}
+
+	soap = soap_build_envelope(ctx->xml, resp);
+	if (soap == NULL) {
+		debug_print(ctx, 1, "SOAP envelope building failed");
+		return -1;
+	}
+	str = xml_node_to_str(ctx->xml, soap);
+	xml_node_free(ctx->xml, soap);
+	if (str == NULL) {
+		debug_print(ctx, 1, "Could not get node string");
+		return -1;
+	}
+	printf("%s", str);
+	free(str);
+
+	return 0;
+}
+
+
+static void usage(void)
+{
+	printf("usage:\n"
+	       "hs20_spp_server -r<root directory> [-f<debug log>]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct hs20_svc ctx;
+	int ret;
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	for (;;) {
+		int c = getopt(argc, argv, "f:r:");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'f':
+			if (ctx.debug_log)
+				break;
+			ctx.debug_log = fopen(optarg, "a");
+			if (ctx.debug_log == NULL) {
+				printf("Could not write to %s\n", optarg);
+				return -1;
+			}
+			break;
+		case 'r':
+			ctx.root_dir = optarg;
+			break;
+		default:
+			usage();
+			return -1;
+		}
+	}
+	if (ctx.root_dir == NULL) {
+		usage();
+		return -1;
+	}
+	ctx.xml = xml_node_init_ctx(&ctx, NULL);
+	if (ctx.xml == NULL)
+		return -1;
+	if (hs20_spp_server_init(&ctx) < 0) {
+		xml_node_deinit_ctx(ctx.xml);
+		return -1;
+	}
+
+	ret = process(&ctx);
+	debug_print(&ctx, 1, "process() --> %d", ret);
+
+	xml_node_deinit_ctx(ctx.xml);
+	hs20_spp_server_deinit(&ctx);
+	if (ctx.debug_log)
+		fclose(ctx.debug_log);
+
+	return ret;
+}
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
new file mode 100644
index 0000000..4d77d0e
--- /dev/null
+++ b/hs20/server/spp_server.c
@@ -0,0 +1,2263 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sqlite3.h>
+
+#include "common.h"
+#include "base64.h"
+#include "md5_i.h"
+#include "xml-utils.h"
+#include "spp_server.h"
+
+
+#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
+
+#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
+#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
+#define URN_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+/* TODO: timeout to expire sessions */
+
+enum hs20_session_operation {
+	NO_OPERATION,
+	UPDATE_PASSWORD,
+	CONTINUE_SUBSCRIPTION_REMEDIATION,
+	CONTINUE_POLICY_UPDATE,
+	USER_REMEDIATION,
+	SUBSCRIPTION_REGISTRATION,
+	POLICY_REMEDIATION,
+	POLICY_UPDATE,
+	FREE_REMEDIATION,
+};
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+				 const char *realm, const char *session_id,
+				 const char *field);
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+				    const char *field);
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+				 const char *realm, int use_dmacc);
+
+
+static int db_add_session(struct hs20_svc *ctx,
+			  const char *user, const char *realm,
+			  const char *sessionid, const char *pw,
+			  const char *redirect_uri,
+			  enum hs20_session_operation operation)
+{
+	char *sql;
+	int ret = 0;
+
+	sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
+			      "operation,password,redirect_uri) "
+			      "VALUES "
+			      "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+			      "%Q,%Q,%Q,%d,%Q,%Q)",
+			      sessionid, user ? user : "", realm ? realm : "",
+			      operation, pw ? pw : "",
+			      redirect_uri ? redirect_uri : "");
+	if (sql == NULL)
+		return -1;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add session entry into sqlite "
+			    "database: %s", sqlite3_errmsg(ctx->db));
+		ret = -1;
+	}
+	sqlite3_free(sql);
+	return ret;
+}
+
+
+static void db_update_session_password(struct hs20_svc *ctx, const char *user,
+				       const char *realm, const char *sessionid,
+				       const char *pw)
+{
+	char *sql;
+
+	sql = sqlite3_mprintf("UPDATE sessions SET password=%Q WHERE id=%Q AND "
+			      "user=%Q AND realm=%Q",
+			      pw, sessionid, user, realm);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to update session password: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
+			       const char *realm, const char *sessionid,
+			       xml_node_t *node)
+{
+	char *str;
+	char *sql;
+
+	str = xml_node_to_str(ctx->xml, node);
+	if (str == NULL)
+		return;
+	sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
+			      "user=%Q AND realm=%Q",
+			      str, sessionid, user, realm);
+	free(str);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add session pps: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
+				   xml_node_t *node)
+{
+	char *str;
+	char *sql;
+
+	str = xml_node_to_str(ctx->xml, node);
+	if (str == NULL)
+		return;
+	sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
+			      str, sessionid);
+	free(str);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add session devinfo: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_add_session_devdetail(struct hs20_svc *ctx,
+				     const char *sessionid,
+				     xml_node_t *node)
+{
+	char *str;
+	char *sql;
+
+	str = xml_node_to_str(ctx->xml, node);
+	if (str == NULL)
+		return;
+	sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
+			      str, sessionid);
+	free(str);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add session devdetail: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_remove_session(struct hs20_svc *ctx,
+			      const char *user, const char *realm,
+			      const char *sessionid)
+{
+	char *sql;
+
+	if (user == NULL || realm == NULL) {
+		sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+				      "id=%Q", sessionid);
+	} else {
+		sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+				      "user=%Q AND realm=%Q AND id=%Q",
+				      user, realm, sessionid);
+	}
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to delete session entry from "
+			    "sqlite database: %s", sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog(struct hs20_svc *ctx,
+			  const char *user, const char *realm,
+			  const char *sessionid, const char *notes,
+			  const char *dump)
+{
+	char *sql;
+	char *user_buf = NULL, *realm_buf = NULL;
+
+	debug_print(ctx, 1, "eventlog: %s", notes);
+
+	if (user == NULL) {
+		user_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+					      "user");
+		user = user_buf;
+		realm_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+					       "realm");
+		realm = realm_buf;
+	}
+
+	sql = sqlite3_mprintf("INSERT INTO eventlog"
+			      "(user,realm,sessionid,timestamp,notes,dump,addr)"
+			      " VALUES (%Q,%Q,%Q,"
+			      "strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+			      "%Q,%Q,%Q)",
+			      user, realm, sessionid, notes,
+			      dump ? dump : "", ctx->addr ? ctx->addr : "");
+	free(user_buf);
+	free(realm_buf);
+	if (sql == NULL)
+		return;
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add eventlog entry into sqlite "
+			    "database: %s", sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog_node(struct hs20_svc *ctx,
+			       const char *user, const char *realm,
+			       const char *sessionid, const char *notes,
+			       xml_node_t *node)
+{
+	char *str;
+
+	if (node)
+		str = xml_node_to_str(ctx->xml, node);
+	else
+		str = NULL;
+	hs20_eventlog(ctx, user, realm, sessionid, notes, str);
+	free(str);
+}
+
+
+static void db_update_mo_str(struct hs20_svc *ctx, const char *user,
+			     const char *realm, const char *name,
+			     const char *str)
+{
+	char *sql;
+	if (user == NULL || realm == NULL || name == NULL)
+		return;
+	sql = sqlite3_mprintf("UPDATE users SET %s=%Q "
+		 "WHERE identity=%Q AND realm=%Q AND phase2=1",
+			      name, str, user, realm);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to update user MO entry in sqlite "
+			    "database: %s", sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
+static void db_update_mo(struct hs20_svc *ctx, const char *user,
+			 const char *realm, const char *name, xml_node_t *mo)
+{
+	char *str;
+
+	str = xml_node_to_str(ctx->xml, mo);
+	if (str == NULL)
+		return;
+
+	db_update_mo_str(ctx, user, realm, name, str);
+	free(str);
+}
+
+
+static void add_text_node(struct hs20_svc *ctx, xml_node_t *parent,
+			  const char *name, const char *value)
+{
+	xml_node_create_text(ctx->xml, parent, NULL, name, value ? value : "");
+}
+
+
+static void add_text_node_conf(struct hs20_svc *ctx, const char *realm,
+			       xml_node_t *parent, const char *name,
+			       const char *field)
+{
+	char *val;
+	val = db_get_osu_config_val(ctx, realm, field);
+	xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+	os_free(val);
+}
+
+
+static int new_password(char *buf, int buflen)
+{
+	int i;
+
+	if (buflen < 1)
+		return -1;
+	buf[buflen - 1] = '\0';
+	if (os_get_random((unsigned char *) buf, buflen - 1) < 0)
+		return -1;
+
+	for (i = 0; i < buflen - 1; i++) {
+		unsigned char val = buf[i];
+		val %= 2 * 26 + 10;
+		if (val < 26)
+			buf[i] = 'a' + val;
+		else if (val < 2 * 26)
+			buf[i] = 'A' + val - 26;
+		else
+			buf[i] = '0' + val - 2 * 26;
+	}
+
+	return 0;
+}
+
+
+struct get_db_field_data {
+	const char *field;
+	char *value;
+};
+
+
+static int get_db_field(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct get_db_field_data *data = ctx;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], data->field) == 0 && argv[i]) {
+			os_free(data->value);
+			data->value = os_strdup(argv[i]);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+static char * db_get_val(struct hs20_svc *ctx, const char *user,
+			 const char *realm, const char *field, int dmacc)
+{
+	char *cmd;
+	struct get_db_field_data data;
+
+	cmd = sqlite3_mprintf("SELECT %s FROM users WHERE "
+			      "%s=%Q AND realm=%Q AND phase2=1",
+			      field, dmacc ? "osu_user" : "identity",
+			      user, realm);
+	if (cmd == NULL)
+		return NULL;
+	memset(&data, 0, sizeof(data));
+	data.field = field;
+	if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+	{
+		debug_print(ctx, 1, "Could not find user '%s'", user);
+		sqlite3_free(cmd);
+		return NULL;
+	}
+	sqlite3_free(cmd);
+
+	debug_print(ctx, 1, "DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
+		    "value='%s'", user, realm, field, dmacc, data.value);
+
+	return data.value;
+}
+
+
+static int db_update_val(struct hs20_svc *ctx, const char *user,
+			 const char *realm, const char *field,
+			 const char *val, int dmacc)
+{
+	char *cmd;
+	int ret;
+
+	cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE "
+			      "%s=%Q AND realm=%Q AND phase2=1",
+			      field, val, dmacc ? "osu_user" : "identity", user,
+			      realm);
+	if (cmd == NULL)
+		return -1;
+	debug_print(ctx, 1, "DB: %s", cmd);
+	if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1,
+			    "Failed to update user in sqlite database: %s",
+			    sqlite3_errmsg(ctx->db));
+		ret = -1;
+	} else {
+		debug_print(ctx, 1,
+			    "DB: user='%s' realm='%s' field='%s' set to '%s'",
+			    user, realm, field, val);
+		ret = 0;
+	}
+	sqlite3_free(cmd);
+
+	return ret;
+}
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+				 const char *realm, const char *session_id,
+				 const char *field)
+{
+	char *cmd;
+	struct get_db_field_data data;
+
+	if (user == NULL || realm == NULL) {
+		cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+				      "id=%Q", field, session_id);
+	} else {
+		cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+				      "user=%Q AND realm=%Q AND id=%Q",
+				      field, user, realm, session_id);
+	}
+	if (cmd == NULL)
+		return NULL;
+	debug_print(ctx, 1, "DB: %s", cmd);
+	memset(&data, 0, sizeof(data));
+	data.field = field;
+	if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+	{
+		debug_print(ctx, 1, "DB: Could not find session %s: %s",
+			    session_id, sqlite3_errmsg(ctx->db));
+		sqlite3_free(cmd);
+		return NULL;
+	}
+	sqlite3_free(cmd);
+
+	debug_print(ctx, 1, "DB: return '%s'", data.value);
+	return data.value;
+}
+
+
+static int update_password(struct hs20_svc *ctx, const char *user,
+			   const char *realm, const char *pw, int dmacc)
+{
+	char *cmd;
+
+	cmd = sqlite3_mprintf("UPDATE users SET password=%Q, "
+			      "remediation='' "
+			      "WHERE %s=%Q AND phase2=1",
+			      pw, dmacc ? "osu_user" : "identity",
+			      user);
+	if (cmd == NULL)
+		return -1;
+	debug_print(ctx, 1, "DB: %s", cmd);
+	if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to update database for user '%s'",
+			    user);
+	}
+	sqlite3_free(cmd);
+
+	return 0;
+}
+
+
+static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent)
+{
+	xml_node_t *node;
+
+	node = xml_node_create(ctx->xml, parent, NULL, "EAPMethod");
+	if (node == NULL)
+		return -1;
+
+	add_text_node(ctx, node, "EAPType", "21");
+	add_text_node(ctx, node, "InnerMethod", "MS-CHAP-V2");
+
+	return 0;
+}
+
+
+static xml_node_t * build_username_password(struct hs20_svc *ctx,
+					    xml_node_t *parent,
+					    const char *user, const char *pw)
+{
+	xml_node_t *node;
+	char *b64;
+
+	node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
+	if (node == NULL)
+		return NULL;
+
+	add_text_node(ctx, node, "Username", user);
+
+	b64 = (char *) base64_encode((unsigned char *) pw, strlen(pw), NULL);
+	if (b64 == NULL)
+		return NULL;
+	add_text_node(ctx, node, "Password", b64);
+	free(b64);
+
+	return node;
+}
+
+
+static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
+				 const char *user, const char *pw)
+{
+	xml_node_t *node;
+
+	node = build_username_password(ctx, cred, user, pw);
+	if (node == NULL)
+		return -1;
+
+	add_text_node(ctx, node, "MachineManaged", "TRUE");
+	add_text_node(ctx, node, "SoftTokenApp", "");
+	add_eap_ttls(ctx, node);
+
+	return 0;
+}
+
+
+static void add_creation_date(struct hs20_svc *ctx, xml_node_t *cred)
+{
+	char str[30];
+	time_t now;
+	struct tm tm;
+
+	time(&now);
+	gmtime_r(&now, &tm);
+	snprintf(str, sizeof(str), "%04u-%02u-%02uT%02u:%02u:%02uZ",
+		 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+		 tm.tm_hour, tm.tm_min, tm.tm_sec);
+	xml_node_create_text(ctx->xml, cred, NULL, "CreationDate", str);
+}
+
+
+static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
+					const char *user, const char *realm,
+					const char *pw)
+{
+	xml_node_t *cred;
+
+	cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+	if (cred == NULL) {
+		debug_print(ctx, 1, "Failed to create Credential node");
+		return NULL;
+	}
+	add_creation_date(ctx, cred);
+	if (add_username_password(ctx, cred, user, pw) < 0) {
+		xml_node_free(ctx->xml, cred);
+		return NULL;
+	}
+	add_text_node(ctx, cred, "Realm", realm);
+
+	return cred;
+}
+
+
+static xml_node_t * build_credential(struct hs20_svc *ctx,
+				     const char *user, const char *realm,
+				     char *new_pw, size_t new_pw_len)
+{
+	if (new_password(new_pw, new_pw_len) < 0)
+		return NULL;
+	debug_print(ctx, 1, "Update password to '%s'", new_pw);
+	return build_credential_pw(ctx, user, realm, new_pw);
+}
+
+
+static xml_node_t * build_credential_cert(struct hs20_svc *ctx,
+					  const char *user, const char *realm,
+					  const char *cert_fingerprint)
+{
+	xml_node_t *cred, *cert;
+
+	cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+	if (cred == NULL) {
+		debug_print(ctx, 1, "Failed to create Credential node");
+		return NULL;
+	}
+	add_creation_date(ctx, cred);
+	cert = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate");
+	add_text_node(ctx, cert, "CertificateType", "x509v3");
+	add_text_node(ctx, cert, "CertSHA256Fingerprint", cert_fingerprint);
+	add_text_node(ctx, cred, "Realm", realm);
+
+	return cred;
+}
+
+
+static xml_node_t * build_post_dev_data_response(struct hs20_svc *ctx,
+						 xml_namespace_t **ret_ns,
+						 const char *session_id,
+						 const char *status,
+						 const char *error_code)
+{
+	xml_node_t *spp_node = NULL;
+	xml_namespace_t *ns;
+
+	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+					"sppPostDevDataResponse");
+	if (spp_node == NULL)
+		return NULL;
+	if (ret_ns)
+		*ret_ns = ns;
+
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+	if (error_code) {
+		xml_node_t *node;
+		node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+		if (node)
+			xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+					  error_code);
+	}
+
+	return spp_node;
+}
+
+
+static int add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node,
+			   xml_namespace_t *ns, const char *uri,
+			   xml_node_t *upd_node)
+{
+	xml_node_t *node, *tnds;
+	char *str;
+
+	tnds = mo_to_tnds(ctx->xml, upd_node, 0, NULL, NULL);
+	if (!tnds)
+		return -1;
+
+	str = xml_node_to_str(ctx->xml, tnds);
+	xml_node_free(ctx->xml, tnds);
+	if (str == NULL)
+		return -1;
+	node = xml_node_create_text(ctx->xml, spp_node, ns, "updateNode", str);
+	free(str);
+
+	xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", uri);
+
+	return 0;
+}
+
+
+static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx,
+				       const char *user, const char *realm,
+				       const char *session_id,
+				       int machine_rem, int dmacc)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *cred;
+	char buf[400];
+	char new_pw[33];
+	char *real_user = NULL;
+	char *status;
+	char *cert;
+
+	if (dmacc) {
+		real_user = db_get_val(ctx, user, realm, "identity", dmacc);
+		if (real_user == NULL) {
+			debug_print(ctx, 1, "Could not find user identity for "
+				    "dmacc user '%s'", user);
+			return NULL;
+		}
+	}
+
+	cert = db_get_val(ctx, user, realm, "cert", dmacc);
+	if (cert && cert[0] == '\0')
+		cert = NULL;
+	if (cert) {
+		cred = build_credential_cert(ctx, real_user ? real_user : user,
+					     realm, cert);
+	} else {
+		cred = build_credential(ctx, real_user ? real_user : user,
+					realm, new_pw, sizeof(new_pw));
+	}
+	free(real_user);
+	if (!cred) {
+		debug_print(ctx, 1, "Could not build credential");
+		return NULL;
+	}
+
+	status = "Remediation complete, request sppUpdateResponse";
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+						NULL);
+	if (spp_node == NULL) {
+		debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+		return NULL;
+	}
+
+	snprintf(buf, sizeof(buf),
+		 "./Wi-Fi/%s/PerProviderSubscription/Credential1/Credential",
+		 realm);
+
+	if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+		debug_print(ctx, 1, "Could not add update node");
+		xml_node_free(ctx->xml, spp_node);
+		return NULL;
+	}
+
+	hs20_eventlog_node(ctx, user, realm, session_id,
+			   machine_rem ? "machine remediation" :
+			   "user remediation", cred);
+	xml_node_free(ctx->xml, cred);
+
+	if (cert) {
+		debug_print(ctx, 1, "Certificate credential - no need for DB "
+			    "password update on success notification");
+	} else {
+		debug_print(ctx, 1, "Request DB password update on success "
+			    "notification");
+		db_add_session(ctx, user, realm, session_id, new_pw, NULL,
+			       UPDATE_PASSWORD);
+	}
+
+	return spp_node;
+}
+
+
+static xml_node_t * machine_remediation(struct hs20_svc *ctx,
+					const char *user,
+					const char *realm,
+					const char *session_id, int dmacc)
+{
+	return build_sub_rem_resp(ctx, user, realm, session_id, 1, dmacc);
+}
+
+
+static xml_node_t * policy_remediation(struct hs20_svc *ctx,
+				       const char *user, const char *realm,
+				       const char *session_id, int dmacc)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *policy;
+	char buf[400];
+	const char *status;
+
+	hs20_eventlog(ctx, user, realm, session_id,
+		      "requires policy remediation", NULL);
+
+	db_add_session(ctx, user, realm, session_id, NULL, NULL,
+		       POLICY_REMEDIATION);
+
+	policy = build_policy(ctx, user, realm, dmacc);
+	if (!policy) {
+		return build_post_dev_data_response(
+			ctx, NULL, session_id,
+			"No update available at this time", NULL);
+	}
+
+	status = "Remediation complete, request sppUpdateResponse";
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	snprintf(buf, sizeof(buf),
+		 "./Wi-Fi/%s/PerProviderSubscription/Credential1/Policy",
+		 realm);
+
+	if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+		xml_node_free(ctx->xml, spp_node);
+		xml_node_free(ctx->xml, policy);
+		return NULL;
+	}
+
+	hs20_eventlog_node(ctx, user, realm, session_id,
+			   "policy update (sub rem)", policy);
+	xml_node_free(ctx->xml, policy);
+
+	return spp_node;
+}
+
+
+static xml_node_t * browser_remediation(struct hs20_svc *ctx,
+					const char *session_id,
+					const char *redirect_uri,
+					const char *uri)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *exec_node;
+
+	if (redirect_uri == NULL) {
+		debug_print(ctx, 1, "Missing redirectURI attribute for user "
+			    "remediation");
+		return NULL;
+	}
+	debug_print(ctx, 1, "redirectURI %s", redirect_uri);
+
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+		NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+	xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+			     uri);
+	return spp_node;
+}
+
+
+static xml_node_t * user_remediation(struct hs20_svc *ctx, const char *user,
+				     const char *realm, const char *session_id,
+				     const char *redirect_uri)
+{
+	char uri[300], *val;
+
+	hs20_eventlog(ctx, user, realm, session_id,
+		      "requires user remediation", NULL);
+	val = db_get_osu_config_val(ctx, realm, "remediation_url");
+	if (val == NULL)
+		return NULL;
+
+	db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+		       USER_REMEDIATION);
+
+	snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+	os_free(val);
+	return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * free_remediation(struct hs20_svc *ctx,
+				     const char *user, const char *realm,
+				     const char *session_id,
+				     const char *redirect_uri)
+{
+	char uri[300], *val;
+
+	hs20_eventlog(ctx, user, realm, session_id,
+		      "requires free/public account remediation", NULL);
+	val = db_get_osu_config_val(ctx, realm, "free_remediation_url");
+	if (val == NULL)
+		return NULL;
+
+	db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+		       FREE_REMEDIATION);
+
+	snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+	os_free(val);
+	return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * no_sub_rem(struct hs20_svc *ctx,
+			       const char *user, const char *realm,
+			       const char *session_id)
+{
+	const char *status;
+
+	hs20_eventlog(ctx, user, realm, session_id,
+		      "no subscription mediation available", NULL);
+
+	status = "No update available at this time";
+	return build_post_dev_data_response(ctx, NULL, session_id, status,
+					    NULL);
+}
+
+
+static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx,
+						  const char *user,
+						  const char *realm,
+						  const char *session_id,
+						  int dmacc,
+						  const char *redirect_uri)
+{
+	char *type, *identity;
+	xml_node_t *ret;
+	char *free_account;
+
+	identity = db_get_val(ctx, user, realm, "identity", dmacc);
+	if (identity == NULL || strlen(identity) == 0) {
+		hs20_eventlog(ctx, user, realm, session_id,
+			      "user not found in database for remediation",
+			      NULL);
+		os_free(identity);
+		return build_post_dev_data_response(ctx, NULL, session_id,
+						    "Error occurred",
+						    "Not found");
+	}
+	os_free(identity);
+
+	free_account = db_get_osu_config_val(ctx, realm, "free_account");
+	if (free_account && strcmp(free_account, user) == 0) {
+		free(free_account);
+		return no_sub_rem(ctx, user, realm, session_id);
+	}
+	free(free_account);
+
+	type = db_get_val(ctx, user, realm, "remediation", dmacc);
+	if (type && strcmp(type, "free") != 0) {
+		char *val;
+		int shared = 0;
+		val = db_get_val(ctx, user, realm, "shared", dmacc);
+		if (val)
+			shared = atoi(val);
+		free(val);
+		if (shared) {
+			free(type);
+			return no_sub_rem(ctx, user, realm, session_id);
+		}
+	}
+	if (type && strcmp(type, "user") == 0)
+		ret = user_remediation(ctx, user, realm, session_id,
+				       redirect_uri);
+	else if (type && strcmp(type, "free") == 0)
+		ret = free_remediation(ctx, user, realm, session_id,
+				       redirect_uri);
+	else if (type && strcmp(type, "policy") == 0)
+		ret = policy_remediation(ctx, user, realm, session_id, dmacc);
+	else
+		ret = machine_remediation(ctx, user, realm, session_id, dmacc);
+	free(type);
+
+	return ret;
+}
+
+
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+				 const char *realm, int use_dmacc)
+{
+	char *policy_id;
+	char fname[200];
+	xml_node_t *policy, *node;
+
+	policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc);
+	if (policy_id == NULL || strlen(policy_id) == 0) {
+		free(policy_id);
+		policy_id = strdup("default");
+		if (policy_id == NULL)
+			return NULL;
+	}
+
+	snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
+		 ctx->root_dir, policy_id);
+	free(policy_id);
+	debug_print(ctx, 1, "Use policy file %s", fname);
+
+	policy = node_from_file(ctx->xml, fname);
+	if (policy == NULL)
+		return NULL;
+
+	node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
+	if (node) {
+		char *url;
+		url = db_get_osu_config_val(ctx, realm, "policy_url");
+		if (url == NULL) {
+			xml_node_free(ctx->xml, policy);
+			return NULL;
+		}
+		xml_node_set_text(ctx->xml, node, url);
+		free(url);
+	}
+
+	node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate");
+	if (node && use_dmacc) {
+		char *pw;
+		pw = db_get_val(ctx, user, realm, "osu_password", use_dmacc);
+		if (pw == NULL ||
+		    build_username_password(ctx, node, user, pw) == NULL) {
+			debug_print(ctx, 1, "Failed to add Policy/PolicyUpdate/"
+				    "UsernamePassword");
+			free(pw);
+			xml_node_free(ctx->xml, policy);
+			return NULL;
+		}
+		free(pw);
+	}
+
+	return policy;
+}
+
+
+static xml_node_t * hs20_policy_update(struct hs20_svc *ctx,
+				       const char *user, const char *realm,
+				       const char *session_id, int dmacc)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node;
+	xml_node_t *policy;
+	char buf[400];
+	const char *status;
+	char *identity;
+
+	identity = db_get_val(ctx, user, realm, "identity", dmacc);
+	if (identity == NULL || strlen(identity) == 0) {
+		hs20_eventlog(ctx, user, realm, session_id,
+			      "user not found in database for policy update",
+			      NULL);
+		os_free(identity);
+		return build_post_dev_data_response(ctx, NULL, session_id,
+						    "Error occurred",
+						    "Not found");
+	}
+	os_free(identity);
+
+	policy = build_policy(ctx, user, realm, dmacc);
+	if (!policy) {
+		return build_post_dev_data_response(
+			ctx, NULL, session_id,
+			"No update available at this time", NULL);
+	}
+
+	db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE);
+
+	status = "Update complete, request sppUpdateResponse";
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	snprintf(buf, sizeof(buf),
+		 "./Wi-Fi/%s/PerProviderSubscription/Credential1/Policy",
+		 realm);
+
+	if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+		xml_node_free(ctx->xml, spp_node);
+		xml_node_free(ctx->xml, policy);
+		return NULL;
+	}
+
+	hs20_eventlog_node(ctx, user, realm, session_id, "policy update",
+			   policy);
+	xml_node_free(ctx->xml, policy);
+
+	return spp_node;
+}
+
+
+static xml_node_t * spp_get_mo(struct hs20_svc *ctx, xml_node_t *node,
+			       const char *urn, int *valid, char **ret_err)
+{
+	xml_node_t *child, *tnds, *mo;
+	const char *name;
+	char *mo_urn;
+	char *str;
+	char fname[200];
+
+	*valid = -1;
+	if (ret_err)
+		*ret_err = NULL;
+
+	xml_node_for_each_child(ctx->xml, child, node) {
+		xml_node_for_each_check(ctx->xml, child);
+		name = xml_node_get_localname(ctx->xml, child);
+		if (strcmp(name, "moContainer") != 0)
+			continue;
+		mo_urn = xml_node_get_attr_value_ns(ctx->xml, child, SPP_NS_URI,
+						    "moURN");
+		if (strcasecmp(urn, mo_urn) == 0) {
+			xml_node_get_attr_value_free(ctx->xml, mo_urn);
+			break;
+		}
+		xml_node_get_attr_value_free(ctx->xml, mo_urn);
+	}
+
+	if (child == NULL)
+		return NULL;
+
+	debug_print(ctx, 1, "moContainer text for %s", urn);
+	debug_dump_node(ctx, "moContainer", child);
+
+	str = xml_node_get_text(ctx->xml, child);
+	debug_print(ctx, 1, "moContainer payload: '%s'", str);
+	tnds = xml_node_from_buf(ctx->xml, str);
+	xml_node_get_text_free(ctx->xml, str);
+	if (tnds == NULL) {
+		debug_print(ctx, 1, "could not parse moContainer text");
+		return NULL;
+	}
+
+	snprintf(fname, sizeof(fname), "%s/spp/dm_ddf-v1_2.dtd", ctx->root_dir);
+	if (xml_validate_dtd(ctx->xml, tnds, fname, ret_err) == 0)
+		*valid = 1;
+	else if (ret_err && *ret_err &&
+		 os_strcmp(*ret_err, "No declaration for attribute xmlns of element MgmtTree\n") == 0) {
+		free(*ret_err);
+		debug_print(ctx, 1, "Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute");
+		*ret_err = NULL;
+		*valid = 1;
+	} else
+		*valid = 0;
+
+	mo = tnds_to_mo(ctx->xml, tnds);
+	xml_node_free(ctx->xml, tnds);
+	if (mo == NULL) {
+		debug_print(ctx, 1, "invalid moContainer for %s", urn);
+	}
+
+	return mo;
+}
+
+
+static xml_node_t * spp_exec_upload_mo(struct hs20_svc *ctx,
+				       const char *session_id, const char *urn)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *node, *exec_node;
+
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+	node = xml_node_create(ctx->xml, exec_node, ns, "uploadMO");
+	xml_node_add_attr(ctx->xml, node, ns, "moURN", urn);
+
+	return spp_node;
+}
+
+
+static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
+						   const char *realm,
+						   const char *session_id,
+						   const char *redirect_uri)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *exec_node;
+	char uri[300], *val;
+
+	if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
+			   SUBSCRIPTION_REGISTRATION) < 0)
+		return NULL;
+	val = db_get_osu_config_val(ctx, realm, "signup_url");
+	if (val == NULL)
+		return NULL;
+
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+	snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+	os_free(val);
+	xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+			     uri);
+	return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_remediation(struct hs20_svc *ctx,
+						const char *user,
+						const char *realm, int dmacc,
+						const char *session_id)
+{
+	return build_sub_rem_resp(ctx, user, realm, session_id, 0, dmacc);
+}
+
+
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+				    const char *field)
+{
+	char *cmd;
+	struct get_db_field_data data;
+
+	cmd = sqlite3_mprintf("SELECT value FROM osu_config WHERE realm=%Q AND "
+			      "field=%Q", realm, field);
+	if (cmd == NULL)
+		return NULL;
+	debug_print(ctx, 1, "DB: %s", cmd);
+	memset(&data, 0, sizeof(data));
+	data.field = "value";
+	if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+	{
+		debug_print(ctx, 1, "DB: Could not find osu_config %s: %s",
+			    realm, sqlite3_errmsg(ctx->db));
+		sqlite3_free(cmd);
+		return NULL;
+	}
+	sqlite3_free(cmd);
+
+	debug_print(ctx, 1, "DB: return '%s'", data.value);
+	return data.value;
+}
+
+
+static xml_node_t * build_pps(struct hs20_svc *ctx,
+			      const char *user, const char *realm,
+			      const char *pw, const char *cert,
+			      int machine_managed)
+{
+	xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp;
+	xml_node_t *cred, *eap, *userpw;
+
+	pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+				   "PerProviderSubscription");
+	if (pps == NULL)
+		return NULL;
+
+	add_text_node(ctx, pps, "UpdateIdentifier", "1");
+
+	c = xml_node_create(ctx->xml, pps, NULL, "Credential1");
+
+	add_text_node(ctx, c, "CredentialPriority", "1");
+
+	aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot");
+	aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
+	add_text_node_conf(ctx, realm, aaa1, "CertURL",
+			   "aaa_trust_root_cert_url");
+	add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
+			   "aaa_trust_root_cert_fingerprint");
+
+	upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
+	add_text_node(ctx, upd, "UpdateInterval", "4294967295");
+	add_text_node(ctx, upd, "UpdateMethod", "ClientInitiated");
+	add_text_node(ctx, upd, "Restriction", "HomeSP");
+	add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
+	trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+	add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
+	add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
+			   "trust_root_cert_fingerprint");
+
+	homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
+	add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
+	add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn");
+
+	xml_node_create(ctx->xml, c, NULL, "SubscriptionParameters");
+
+	cred = xml_node_create(ctx->xml, c, NULL, "Credential");
+	add_creation_date(ctx, cred);
+	if (cert) {
+		xml_node_t *dc;
+		dc = xml_node_create(ctx->xml, cred, NULL,
+				     "DigitalCertificate");
+		add_text_node(ctx, dc, "CertificateType", "x509v3");
+		add_text_node(ctx, dc, "CertSHA256Fingerprint", cert);
+	} else {
+		userpw = build_username_password(ctx, cred, user, pw);
+		add_text_node(ctx, userpw, "MachineManaged",
+			      machine_managed ? "TRUE" : "FALSE");
+		eap = xml_node_create(ctx->xml, userpw, NULL, "EAPMethod");
+		add_text_node(ctx, eap, "EAPType", "21");
+		add_text_node(ctx, eap, "InnerMethod", "MS-CHAP-V2");
+	}
+	add_text_node(ctx, cred, "Realm", realm);
+
+	return pps;
+}
+
+
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+					     const char *session_id,
+					     const char *user,
+					     const char *realm)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *enroll, *exec_node;
+	char *val;
+	char password[11];
+	char *b64;
+
+	if (new_password(password, sizeof(password)) < 0)
+		return NULL;
+
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+	enroll = xml_node_create(ctx->xml, exec_node, ns, "getCertificate");
+	xml_node_add_attr(ctx->xml, enroll, NULL, "enrollmentProtocol", "EST");
+
+	val = db_get_osu_config_val(ctx, realm, "est_url");
+	xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI",
+			     val ? val : "");
+	os_free(val);
+	xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
+
+	b64 = (char *) base64_encode((unsigned char *) password,
+				     strlen(password), NULL);
+	if (b64 == NULL) {
+		xml_node_free(ctx->xml, spp_node);
+		return NULL;
+	}
+	xml_node_create_text(ctx->xml, enroll, ns, "estPassword", b64);
+	free(b64);
+
+	db_update_session_password(ctx, user, realm, session_id, password);
+
+	return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx,
+						 const char *session_id,
+						 int enrollment_done)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *node = NULL;
+	xml_node_t *pps, *tnds;
+	char buf[400];
+	char *str;
+	char *user, *realm, *pw, *type, *mm;
+	const char *status;
+	int cert = 0;
+	int machine_managed = 0;
+	char *fingerprint;
+
+	user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+	realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+	pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+
+	if (!user || !realm || !pw) {
+		debug_print(ctx, 1, "Could not find session info from DB for "
+			    "the new subscription");
+		free(user);
+		free(realm);
+		free(pw);
+		return NULL;
+	}
+
+	mm = db_get_session_val(ctx, NULL, NULL, session_id, "machine_managed");
+	if (mm && atoi(mm))
+		machine_managed = 1;
+	free(mm);
+
+	type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+	if (type && strcmp(type, "cert") == 0)
+		cert = 1;
+	free(type);
+
+	if (cert && !enrollment_done) {
+		xml_node_t *ret;
+		hs20_eventlog(ctx, user, realm, session_id,
+			      "request client certificate enrollment", NULL);
+		ret = spp_exec_get_certificate(ctx, session_id, user, realm);
+		free(user);
+		free(realm);
+		free(pw);
+		return ret;
+	}
+
+	if (!cert && strlen(pw) == 0) {
+		machine_managed = 1;
+		free(pw);
+		pw = malloc(11);
+		if (pw == NULL || new_password(pw, 11) < 0) {
+			free(user);
+			free(realm);
+			free(pw);
+			return NULL;
+		}
+	}
+
+	status = "Provisioning complete, request sppUpdateResponse";
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+	pps = build_pps(ctx, user, realm, pw,
+			fingerprint ? fingerprint : NULL, machine_managed);
+	free(fingerprint);
+	if (!pps) {
+		xml_node_free(ctx->xml, spp_node);
+		free(user);
+		free(realm);
+		free(pw);
+		return NULL;
+	}
+
+	debug_print(ctx, 1, "Request DB subscription registration on success "
+		    "notification");
+	db_add_session_pps(ctx, user, realm, session_id, pps);
+
+	hs20_eventlog_node(ctx, user, realm, session_id,
+			   "new subscription", pps);
+	free(user);
+	free(pw);
+
+	tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+	xml_node_free(ctx->xml, pps);
+	if (!tnds) {
+		xml_node_free(ctx->xml, spp_node);
+		free(realm);
+		return NULL;
+	}
+
+	str = xml_node_to_str(ctx->xml, tnds);
+	xml_node_free(ctx->xml, tnds);
+	if (str == NULL) {
+		xml_node_free(ctx->xml, spp_node);
+		free(realm);
+		return NULL;
+	}
+
+	node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+	free(str);
+	snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+	free(realm);
+	xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+	xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+	return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
+						     const char *user,
+						     const char *realm,
+						     const char *session_id)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node;
+	xml_node_t *cred;
+	char buf[400];
+	char *status;
+	char *free_account, *pw;
+
+	free_account = db_get_osu_config_val(ctx, realm, "free_account");
+	if (free_account == NULL)
+		return NULL;
+	pw = db_get_val(ctx, free_account, realm, "password", 0);
+	if (pw == NULL) {
+		free(free_account);
+		return NULL;
+	}
+
+	cred = build_credential_pw(ctx, free_account, realm, pw);
+	free(free_account);
+	free(pw);
+	if (!cred) {
+		xml_node_free(ctx->xml, cred);
+		return NULL;
+	}
+
+	status = "Remediation complete, request sppUpdateResponse";
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+
+	snprintf(buf, sizeof(buf),
+		 "./Wi-Fi/%s/PerProviderSubscription/Credential1/Credential",
+		 realm);
+
+	if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+		xml_node_free(ctx->xml, spp_node);
+		return NULL;
+	}
+
+	hs20_eventlog_node(ctx, user, realm, session_id,
+			   "free/public remediation", cred);
+	xml_node_free(ctx->xml, cred);
+
+	return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx,
+					     const char *user,
+					     const char *realm, int dmacc,
+					     const char *session_id)
+{
+	char *val;
+	enum hs20_session_operation oper;
+
+	val = db_get_session_val(ctx, user, realm, session_id, "operation");
+	if (val == NULL) {
+		debug_print(ctx, 1, "No session %s found to continue",
+			    session_id);
+		return NULL;
+	}
+	oper = atoi(val);
+	free(val);
+
+	if (oper == USER_REMEDIATION) {
+		return hs20_user_input_remediation(ctx, user, realm, dmacc,
+						   session_id);
+	}
+
+	if (oper == FREE_REMEDIATION) {
+		return hs20_user_input_free_remediation(ctx, user, realm,
+							session_id);
+	}
+
+	if (oper == SUBSCRIPTION_REGISTRATION) {
+		return hs20_user_input_registration(ctx, session_id, 0);
+	}
+
+	debug_print(ctx, 1, "User session %s not in state for user input "
+		    "completion", session_id);
+	return NULL;
+}
+
+
+static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx,
+					       const char *user,
+					       const char *realm, int dmacc,
+					       const char *session_id)
+{
+	char *val;
+	enum hs20_session_operation oper;
+
+	val = db_get_session_val(ctx, user, realm, session_id, "operation");
+	if (val == NULL) {
+		debug_print(ctx, 1, "No session %s found to continue",
+			    session_id);
+		return NULL;
+	}
+	oper = atoi(val);
+	free(val);
+
+	if (oper == SUBSCRIPTION_REGISTRATION)
+		return hs20_user_input_registration(ctx, session_id, 1);
+
+	debug_print(ctx, 1, "User session %s not in state for certificate "
+		    "enrollment completion", session_id);
+	return NULL;
+}
+
+
+static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx,
+					    const char *user,
+					    const char *realm, int dmacc,
+					    const char *session_id)
+{
+	char *val;
+	enum hs20_session_operation oper;
+	xml_node_t *spp_node, *node;
+	char *status;
+	xml_namespace_t *ns;
+
+	val = db_get_session_val(ctx, user, realm, session_id, "operation");
+	if (val == NULL) {
+		debug_print(ctx, 1, "No session %s found to continue",
+			    session_id);
+		return NULL;
+	}
+	oper = atoi(val);
+	free(val);
+
+	if (oper != SUBSCRIPTION_REGISTRATION) {
+		debug_print(ctx, 1, "User session %s not in state for "
+			    "enrollment failure", session_id);
+		return NULL;
+	}
+
+	status = "Error occurred";
+	spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+						NULL);
+	if (spp_node == NULL)
+		return NULL;
+	node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+	xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+			  "Credentials cannot be provisioned at this time");
+	db_remove_session(ctx, user, realm, session_id);
+
+	return spp_node;
+}
+
+
+static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx,
+					   xml_node_t *node,
+					   const char *user,
+					   const char *realm,
+					   const char *session_id,
+					   int dmacc)
+{
+	const char *req_reason;
+	char *redirect_uri = NULL;
+	char *req_reason_buf = NULL;
+	char str[200];
+	xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
+	xml_node_t *mo;
+	char *version;
+	int valid;
+	char *supp, *pos;
+	char *err;
+
+	version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+					     "sppVersion");
+	if (version == NULL || strstr(version, "1.0") == NULL) {
+		ret = build_post_dev_data_response(
+			ctx, NULL, session_id, "Error occurred",
+			"SPP version not supported");
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "Unsupported sppVersion", ret);
+		xml_node_get_attr_value_free(ctx->xml, version);
+		return ret;
+	}
+	xml_node_get_attr_value_free(ctx->xml, version);
+
+	mo = get_node(ctx->xml, node, "supportedMOList");
+	if (mo == NULL) {
+		ret = build_post_dev_data_response(
+			ctx, NULL, session_id, "Error occurred",
+			"Other");
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "No supportedMOList element", ret);
+		return ret;
+	}
+	supp = xml_node_get_text(ctx->xml, mo);
+	for (pos = supp; pos && *pos; pos++)
+		*pos = tolower(*pos);
+	if (supp == NULL ||
+	    strstr(supp, URN_OMA_DM_DEVINFO) == NULL ||
+	    strstr(supp, URN_OMA_DM_DEVDETAIL) == NULL ||
+	    strstr(supp, URN_HS20_PPS) == NULL) {
+		xml_node_get_text_free(ctx->xml, supp);
+		ret = build_post_dev_data_response(
+			ctx, NULL, session_id, "Error occurred",
+			"One or more mandatory MOs not supported");
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "Unsupported MOs", ret);
+		return ret;
+	}
+	xml_node_get_text_free(ctx->xml, supp);
+
+	req_reason_buf = xml_node_get_attr_value(ctx->xml, node,
+						 "requestReason");
+	if (req_reason_buf == NULL) {
+		debug_print(ctx, 1, "No requestReason attribute");
+		return NULL;
+	}
+	req_reason = req_reason_buf;
+
+	redirect_uri = xml_node_get_attr_value(ctx->xml, node, "redirectURI");
+
+	debug_print(ctx, 1, "requestReason: %s  sessionID: %s  redirectURI: %s",
+		    req_reason, session_id, redirect_uri);
+	snprintf(str, sizeof(str), "sppPostDevData: requestReason=%s",
+		 req_reason);
+	hs20_eventlog(ctx, user, realm, session_id, str, NULL);
+
+	devinfo = spp_get_mo(ctx, node, URN_OMA_DM_DEVINFO, &valid, &err);
+	if (devinfo == NULL) {
+		ret = build_post_dev_data_response(ctx, NULL, session_id,
+						   "Error occurred", "Other");
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "No DevInfo moContainer in sppPostDevData",
+				   ret);
+		os_free(err);
+		goto out;
+	}
+
+	hs20_eventlog_node(ctx, user, realm, session_id,
+			   "Received DevInfo MO", devinfo);
+	if (valid == 0) {
+		hs20_eventlog(ctx, user, realm, session_id,
+			      "OMA-DM DDF DTD validation errors in DevInfo MO",
+			      err);
+		ret = build_post_dev_data_response(ctx, NULL, session_id,
+						   "Error occurred", "Other");
+		os_free(err);
+		goto out;
+	}
+	os_free(err);
+	if (user)
+		db_update_mo(ctx, user, realm, "devinfo", devinfo);
+
+	devdetail = spp_get_mo(ctx, node, URN_OMA_DM_DEVDETAIL, &valid, &err);
+	if (devdetail == NULL) {
+		ret = build_post_dev_data_response(ctx, NULL, session_id,
+						   "Error occurred", "Other");
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "No DevDetail moContainer in sppPostDevData",
+				   ret);
+		os_free(err);
+		goto out;
+	}
+
+	hs20_eventlog_node(ctx, user, realm, session_id,
+			   "Received DevDetail MO", devdetail);
+	if (valid == 0) {
+		hs20_eventlog(ctx, user, realm, session_id,
+			      "OMA-DM DDF DTD validation errors "
+			      "in DevDetail MO", err);
+		ret = build_post_dev_data_response(ctx, NULL, session_id,
+						   "Error occurred", "Other");
+		os_free(err);
+		goto out;
+	}
+	os_free(err);
+	if (user)
+		db_update_mo(ctx, user, realm, "devdetail", devdetail);
+
+	if (user)
+		mo = spp_get_mo(ctx, node, URN_HS20_PPS, &valid, &err);
+	else {
+		mo = NULL;
+		err = NULL;
+	}
+	if (user && mo) {
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "Received PPS MO", mo);
+		if (valid == 0) {
+			hs20_eventlog(ctx, user, realm, session_id,
+				      "OMA-DM DDF DTD validation errors "
+				      "in PPS MO", err);
+			xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+			os_free(err);
+			return build_post_dev_data_response(
+				ctx, NULL, session_id,
+				"Error occurred", "Other");
+		}
+		db_update_mo(ctx, user, realm, "pps", mo);
+		db_update_val(ctx, user, realm, "fetch_pps", "0", dmacc);
+		xml_node_free(ctx->xml, mo);
+	}
+	os_free(err);
+
+	if (user && !mo) {
+		char *fetch;
+		int fetch_pps;
+
+		fetch = db_get_val(ctx, user, realm, "fetch_pps", dmacc);
+		fetch_pps = fetch ? atoi(fetch) : 0;
+		free(fetch);
+
+		if (fetch_pps) {
+			enum hs20_session_operation oper;
+			if (strcasecmp(req_reason, "Subscription remediation")
+			    == 0)
+				oper = CONTINUE_SUBSCRIPTION_REMEDIATION;
+			else if (strcasecmp(req_reason, "Policy update") == 0)
+				oper = CONTINUE_POLICY_UPDATE;
+			else
+				oper = NO_OPERATION;
+			if (db_add_session(ctx, user, realm, session_id, NULL,
+					   NULL, oper) < 0)
+				goto out;
+
+			ret = spp_exec_upload_mo(ctx, session_id,
+						 URN_HS20_PPS);
+			hs20_eventlog_node(ctx, user, realm, session_id,
+					   "request PPS MO upload",
+					   ret);
+			goto out;
+		}
+	}
+
+	if (user && strcasecmp(req_reason, "MO upload") == 0) {
+		char *val = db_get_session_val(ctx, user, realm, session_id,
+					       "operation");
+		enum hs20_session_operation oper;
+		if (!val) {
+			debug_print(ctx, 1, "No session %s found to continue",
+				    session_id);
+			goto out;
+		}
+		oper = atoi(val);
+		free(val);
+		if (oper == CONTINUE_SUBSCRIPTION_REMEDIATION)
+			req_reason = "Subscription remediation";
+		else if (oper == CONTINUE_POLICY_UPDATE)
+			req_reason = "Policy update";
+		else {
+			debug_print(ctx, 1,
+				    "No pending operation in session %s",
+				    session_id);
+			goto out;
+		}
+	}
+
+	if (strcasecmp(req_reason, "Subscription registration") == 0) {
+		ret = hs20_subscription_registration(ctx, realm, session_id,
+						     redirect_uri);
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "subscription registration response",
+				   ret);
+		goto out;
+	}
+	if (user && strcasecmp(req_reason, "Subscription remediation") == 0) {
+		ret = hs20_subscription_remediation(ctx, user, realm,
+						    session_id, dmacc,
+						    redirect_uri);
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "subscription remediation response",
+				   ret);
+		goto out;
+	}
+	if (user && strcasecmp(req_reason, "Policy update") == 0) {
+		ret = hs20_policy_update(ctx, user, realm, session_id, dmacc);
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "policy update response",
+				   ret);
+		goto out;
+	}
+
+	if (strcasecmp(req_reason, "User input completed") == 0) {
+		if (devinfo)
+			db_add_session_devinfo(ctx, session_id, devinfo);
+		if (devdetail)
+			db_add_session_devdetail(ctx, session_id, devdetail);
+		ret = hs20_user_input_complete(ctx, user, realm, dmacc,
+					       session_id);
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "user input completed response", ret);
+		goto out;
+	}
+
+	if (strcasecmp(req_reason, "Certificate enrollment completed") == 0) {
+		ret = hs20_cert_enroll_completed(ctx, user, realm, dmacc,
+						 session_id);
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "certificate enrollment response", ret);
+		goto out;
+	}
+
+	if (strcasecmp(req_reason, "Certificate enrollment failed") == 0) {
+		ret = hs20_cert_enroll_failed(ctx, user, realm, dmacc,
+					      session_id);
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "certificate enrollment failed response",
+				   ret);
+		goto out;
+	}
+
+	debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'",
+		    req_reason, user);
+out:
+	xml_node_get_attr_value_free(ctx->xml, req_reason_buf);
+	xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+	if (devinfo)
+		xml_node_free(ctx->xml, devinfo);
+	if (devdetail)
+		xml_node_free(ctx->xml, devdetail);
+	return ret;
+}
+
+
+static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx,
+						const char *session_id,
+						const char *status,
+						const char *error_code)
+{
+	xml_namespace_t *ns;
+	xml_node_t *spp_node, *node;
+
+	spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+					"sppExchangeComplete");
+
+
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+	xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+	if (error_code) {
+		node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+		xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+				  error_code);
+	}
+
+	return spp_node;
+}
+
+
+static int add_subscription(struct hs20_svc *ctx, const char *session_id)
+{
+	char *user, *realm, *pw, *pw_mm, *pps, *str;
+	char *sql;
+	int ret = -1;
+	char *free_account;
+	int free_acc;
+	char *type;
+	int cert = 0;
+	char *cert_pem, *fingerprint;
+
+	user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+	realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+	pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+	pw_mm = db_get_session_val(ctx, NULL, NULL, session_id,
+				   "machine_managed");
+	pps = db_get_session_val(ctx, NULL, NULL, session_id, "pps");
+	cert_pem = db_get_session_val(ctx, NULL, NULL, session_id, "cert_pem");
+	fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+	type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+	if (type && strcmp(type, "cert") == 0)
+		cert = 1;
+	free(type);
+
+	if (!user || !realm || !pw) {
+		debug_print(ctx, 1, "Could not find session info from DB for "
+			    "the new subscription");
+		goto out;
+	}
+
+	free_account = db_get_osu_config_val(ctx, realm, "free_account");
+	free_acc = free_account && strcmp(free_account, user) == 0;
+	free(free_account);
+
+	debug_print(ctx, 1,
+		    "New subscription: user='%s' realm='%s' free_acc=%d",
+		    user, realm, free_acc);
+	debug_print(ctx, 1, "New subscription: pps='%s'", pps);
+
+	sql = sqlite3_mprintf("UPDATE eventlog SET user=%Q, realm=%Q WHERE "
+			      "sessionid=%Q AND (user='' OR user IS NULL)",
+			      user, realm, session_id);
+	if (sql) {
+		debug_print(ctx, 1, "DB: %s", sql);
+		if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+			debug_print(ctx, 1, "Failed to update eventlog in "
+				    "sqlite database: %s",
+				    sqlite3_errmsg(ctx->db));
+		}
+		sqlite3_free(sql);
+	}
+
+	if (free_acc) {
+		hs20_eventlog(ctx, user, realm, session_id,
+			      "completed shared free account registration",
+			      NULL);
+		ret = 0;
+		goto out;
+	}
+
+	sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,"
+			      "methods,cert,cert_pem,machine_managed) VALUES "
+			      "(%Q,%Q,1,%Q,%Q,%Q,%d)",
+			      user, realm, cert ? "TLS" : "TTLS-MSCHAPV2",
+			      fingerprint ? fingerprint : "",
+			      cert_pem ? cert_pem : "",
+			      pw_mm && atoi(pw_mm) ? 1 : 0);
+	if (sql == NULL)
+		goto out;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1, "Failed to add user in sqlite database: %s",
+			    sqlite3_errmsg(ctx->db));
+		sqlite3_free(sql);
+		goto out;
+	}
+	sqlite3_free(sql);
+
+	if (cert)
+		ret = 0;
+	else
+		ret = update_password(ctx, user, realm, pw, 0);
+	if (ret < 0) {
+		sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND "
+				      "realm=%Q AND phase2=1",
+				      user, realm);
+		if (sql) {
+			debug_print(ctx, 1, "DB: %s", sql);
+			sqlite3_exec(ctx->db, sql, NULL, NULL, NULL);
+			sqlite3_free(sql);
+		}
+	}
+
+	if (pps)
+		db_update_mo_str(ctx, user, realm, "pps", pps);
+
+	str = db_get_session_val(ctx, NULL, NULL, session_id, "devinfo");
+	if (str) {
+		db_update_mo_str(ctx, user, realm, "devinfo", str);
+		free(str);
+	}
+
+	str = db_get_session_val(ctx, NULL, NULL, session_id, "devdetail");
+	if (str) {
+		db_update_mo_str(ctx, user, realm, "devdetail", str);
+		free(str);
+	}
+
+	if (ret == 0) {
+		hs20_eventlog(ctx, user, realm, session_id,
+			      "completed subscription registration", NULL);
+	}
+
+out:
+	free(user);
+	free(realm);
+	free(pw);
+	free(pw_mm);
+	free(pps);
+	free(cert_pem);
+	free(fingerprint);
+	return ret;
+}
+
+
+static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx,
+					     xml_node_t *node,
+					     const char *user,
+					     const char *realm,
+					     const char *session_id,
+					     int dmacc)
+{
+	char *status;
+	xml_node_t *ret;
+	char *val;
+	enum hs20_session_operation oper;
+
+	status = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+					    "sppStatus");
+	if (status == NULL) {
+		debug_print(ctx, 1, "No sppStatus attribute");
+		return NULL;
+	}
+
+	debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s  sessionID: %s",
+		    status, session_id);
+
+	val = db_get_session_val(ctx, user, realm, session_id, "operation");
+	if (!val) {
+		debug_print(ctx, 1,
+			    "No session active for user: %s  sessionID: %s",
+			    user, session_id);
+		oper = NO_OPERATION;
+	} else
+		oper = atoi(val);
+
+	if (strcasecmp(status, "OK") == 0) {
+		char *new_pw = NULL;
+
+		xml_node_get_attr_value_free(ctx->xml, status);
+
+		if (oper == USER_REMEDIATION) {
+			new_pw = db_get_session_val(ctx, user, realm,
+						    session_id, "password");
+			if (new_pw == NULL || strlen(new_pw) == 0) {
+				free(new_pw);
+				ret = build_spp_exchange_complete(
+					ctx, session_id, "Error occurred",
+					"Other");
+				hs20_eventlog_node(ctx, user, realm,
+						   session_id, "No password "
+						   "had been assigned for "
+						   "session", ret);
+				db_remove_session(ctx, user, realm, session_id);
+				return ret;
+			}
+			oper = UPDATE_PASSWORD;
+		}
+		if (oper == UPDATE_PASSWORD) {
+			if (!new_pw) {
+				new_pw = db_get_session_val(ctx, user, realm,
+							    session_id,
+							    "password");
+				if (!new_pw) {
+					db_remove_session(ctx, user, realm,
+							  session_id);
+					return NULL;
+				}
+			}
+			debug_print(ctx, 1, "Update user '%s' password in DB",
+				    user);
+			if (update_password(ctx, user, realm, new_pw, dmacc) <
+			    0) {
+				debug_print(ctx, 1, "Failed to update user "
+					    "'%s' password in DB", user);
+				ret = build_spp_exchange_complete(
+					ctx, session_id, "Error occurred",
+					"Other");
+				hs20_eventlog_node(ctx, user, realm,
+						   session_id, "Failed to "
+						   "update database", ret);
+				db_remove_session(ctx, user, realm, session_id);
+				return ret;
+			}
+			hs20_eventlog(ctx, user, realm,
+				      session_id, "Updated user password "
+				      "in database", NULL);
+		}
+		if (oper == SUBSCRIPTION_REGISTRATION) {
+			if (add_subscription(ctx, session_id) < 0) {
+				debug_print(ctx, 1, "Failed to add "
+					    "subscription into DB");
+				ret = build_spp_exchange_complete(
+					ctx, session_id, "Error occurred",
+					"Other");
+				hs20_eventlog_node(ctx, user, realm,
+						   session_id, "Failed to "
+						   "update database", ret);
+				db_remove_session(ctx, user, realm, session_id);
+				return ret;
+			}
+		}
+		if (oper == POLICY_REMEDIATION || oper == POLICY_UPDATE) {
+			char *val;
+			val = db_get_val(ctx, user, realm, "remediation",
+					 dmacc);
+			if (val && strcmp(val, "policy") == 0)
+				db_update_val(ctx, user, realm, "remediation",
+					      "", dmacc);
+			free(val);
+		}
+		ret = build_spp_exchange_complete(
+			ctx, session_id,
+			"Exchange complete, release TLS connection", NULL);
+		hs20_eventlog_node(ctx, user, realm, session_id,
+				   "Exchange completed", ret);
+		db_remove_session(ctx, user, realm, session_id);
+		return ret;
+	}
+
+	ret = build_spp_exchange_complete(ctx, session_id, "Error occurred",
+					  "Other");
+	hs20_eventlog_node(ctx, user, realm, session_id, "Error occurred", ret);
+	db_remove_session(ctx, user, realm, session_id);
+	xml_node_get_attr_value_free(ctx->xml, status);
+	return ret;
+}
+
+
+#define SPP_SESSION_ID_LEN 16
+
+static char * gen_spp_session_id(void)
+{
+	FILE *f;
+	int i;
+	char *session;
+
+	session = os_malloc(SPP_SESSION_ID_LEN * 2 + 1);
+	if (session == NULL)
+		return NULL;
+
+	f = fopen("/dev/urandom", "r");
+	if (f == NULL) {
+		os_free(session);
+		return NULL;
+	}
+	for (i = 0; i < SPP_SESSION_ID_LEN; i++)
+		os_snprintf(session + i * 2, 3, "%02x", fgetc(f));
+
+	fclose(f);
+	return session;
+}
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+				     const char *auth_user,
+				     const char *auth_realm, int dmacc)
+{
+	xml_node_t *ret = NULL;
+	char *session_id;
+	const char *op_name;
+	char *xml_err;
+	char fname[200];
+
+	debug_dump_node(ctx, "received request", node);
+
+	if (!dmacc && auth_user && auth_realm) {
+		char *real;
+		real = db_get_val(ctx, auth_user, auth_realm, "identity", 0);
+		if (!real) {
+			real = db_get_val(ctx, auth_user, auth_realm,
+					  "identity", 1);
+			if (real)
+				dmacc = 1;
+		}
+		os_free(real);
+	}
+
+	snprintf(fname, sizeof(fname), "%s/spp/spp.xsd", ctx->root_dir);
+	if (xml_validate(ctx->xml, node, fname, &xml_err) < 0) {
+		/*
+		 * We may not be able to extract the sessionID from invalid
+		 * input, but well, we can try.
+		 */
+		session_id = xml_node_get_attr_value_ns(ctx->xml, node,
+							SPP_NS_URI,
+							"sessionID");
+		debug_print(ctx, 1, "SPP message failed validation");
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "SPP message failed validation", node);
+		hs20_eventlog(ctx, auth_user, auth_realm, session_id,
+			      "Validation errors", xml_err);
+		os_free(xml_err);
+		xml_node_get_attr_value_free(ctx->xml, session_id);
+		/* TODO: what to return here? */
+		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+					   "SppValidationError");
+		return ret;
+	}
+
+	session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+						"sessionID");
+	if (session_id) {
+		char *tmp;
+		debug_print(ctx, 1, "Received sessionID %s", session_id);
+		tmp = os_strdup(session_id);
+		xml_node_get_attr_value_free(ctx->xml, session_id);
+		if (tmp == NULL)
+			return NULL;
+		session_id = tmp;
+	} else {
+		session_id = gen_spp_session_id();
+		if (session_id == NULL) {
+			debug_print(ctx, 1, "Failed to generate sessionID");
+			return NULL;
+		}
+		debug_print(ctx, 1, "Generated sessionID %s", session_id);
+	}
+
+	op_name = xml_node_get_localname(ctx->xml, node);
+	if (op_name == NULL) {
+		debug_print(ctx, 1, "Could not get op_name");
+		return NULL;
+	}
+
+	if (strcmp(op_name, "sppPostDevData") == 0) {
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "sppPostDevData received and validated",
+				   node);
+		ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
+					     session_id, dmacc);
+	} else if (strcmp(op_name, "sppUpdateResponse") == 0) {
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "sppUpdateResponse received and validated",
+				   node);
+		ret = hs20_spp_update_response(ctx, node, auth_user,
+					       auth_realm, session_id, dmacc);
+	} else {
+		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+				   "Unsupported SPP message received and "
+				   "validated", node);
+		debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
+		/* TODO: what to return here? */
+		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+					   "SppUnknownCommandError");
+	}
+	os_free(session_id);
+
+	if (ret == NULL) {
+		/* TODO: what to return here? */
+		ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+					   "SppInternalError");
+	}
+
+	return ret;
+}
+
+
+int hs20_spp_server_init(struct hs20_svc *ctx)
+{
+	char fname[200];
+	ctx->db = NULL;
+	snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
+	if (sqlite3_open(fname, &ctx->db)) {
+		printf("Failed to open sqlite database: %s\n",
+		       sqlite3_errmsg(ctx->db));
+		sqlite3_close(ctx->db);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void hs20_spp_server_deinit(struct hs20_svc *ctx)
+{
+	sqlite3_close(ctx->db);
+	ctx->db = NULL;
+}
diff --git a/hs20/server/spp_server.h b/hs20/server/spp_server.h
new file mode 100644
index 0000000..7b27be3
--- /dev/null
+++ b/hs20/server/spp_server.h
@@ -0,0 +1,32 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SPP_SERVER_H
+#define SPP_SERVER_H
+
+struct hs20_svc {
+	const void *ctx;
+	struct xml_node_ctx *xml;
+	char *root_dir;
+	FILE *debug_log;
+	sqlite3 *db;
+	const char *addr;
+};
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+	__attribute__ ((format (printf, 3, 4)));
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node);
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+				     const char *auth_user,
+				     const char *auth_realm, int dmacc);
+int hs20_spp_server_init(struct hs20_svc *ctx);
+void hs20_spp_server_deinit(struct hs20_svc *ctx);
+
+#endif /* SPP_SERVER_H */
diff --git a/hs20/server/sql-example.txt b/hs20/server/sql-example.txt
new file mode 100644
index 0000000..20dcf2f
--- /dev/null
+++ b/hs20/server/sql-example.txt
@@ -0,0 +1,17 @@
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','fqdn','example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','friendly_name','Example Operator');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','spp_http_auth_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/spp-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/aaa-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_account','free');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','policy_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','remediation_url','https://subscription-server.osu.example.com/hs20/remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_remediation_url','https://subscription-server.osu.example.com/hs20/free-remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','signup_url','https://subscription-server.osu.example.com/hs20/signup.php?session_id=');
+
+
+INSERT INTO users(identity,realm,methods,password,phase2,shared) VALUES('free','example.com','TTLS-MSCHAPV2','free',1,1);
+
+INSERT INTO wildcards(identity,methods) VALUES('','TTLS,TLS');
diff --git a/hs20/server/sql.txt b/hs20/server/sql.txt
new file mode 100644
index 0000000..6609538
--- /dev/null
+++ b/hs20/server/sql.txt
@@ -0,0 +1,59 @@
+CREATE TABLE eventlog(
+	user TEXT,
+	realm TEXT,
+	sessionid TEXT COLLATE NOCASE,
+	timestamp TEXT,
+	notes TEXT,
+	dump TEXT,
+	addr TEXT
+);
+
+CREATE TABLE sessions(
+	timestamp TEXT,
+	id TEXT COLLATE NOCASE,
+	user TEXT,
+	realm TEXT,
+	password TEXT,
+	machine_managed BOOLEAN,
+	operation INTEGER,
+	type TEXT,
+	pps TEXT,
+	redirect_uri TEXT,
+	devinfo TEXT,
+	devdetail TEXT,
+	cert TEXT,
+	cert_pem TEXT
+);
+
+CREATE index sessions_id_index ON sessions(id);
+
+CREATE TABLE osu_config(
+       realm TEXT,
+       field TEXT,
+       value TEXT
+);
+
+CREATE TABLE users(
+	identity TEXT PRIMARY KEY,
+	methods TEXT,
+	password TEXT,
+	machine_managed BOOLEAN,
+	remediation TEXT,
+	phase2 INTEGER,
+	realm TEXT,
+	policy TEXT,
+	devinfo TEXT,
+	devdetail TEXT,
+	pps TEXT,
+	fetch_pps INTEGER,
+	osu_user TEXT,
+	osu_password TEXT,
+	shared INTEGER,
+	cert TEXT,
+	cert_pem TEXT
+);
+
+CREATE TABLE wildcards(
+	identity TEXT PRIMARY KEY,
+	methods TEXT
+);
diff --git a/hs20/server/www/add-free.php b/hs20/server/www/add-free.php
new file mode 100644
index 0000000..1efc655
--- /dev/null
+++ b/hs20/server/www/add-free.php
@@ -0,0 +1,50 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+   die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+  $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+  die("Missing session id");
+if (strlen($id) < 32)
+  die("Invalid session id");
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+   die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+$realm = $row['realm'];
+
+$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
+if (!$row || strlen($row['value']) == 0) {
+  die("Free account disabled");
+}
+
+$user = $row['value'];
+
+$row = $db->query("SELECT password FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
+if (!$row)
+  die("Free account not found");
+
+$pw = $row['password'];
+
+if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', machine_managed='1' WHERE rowid=$rowid")) {
+  die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+	"VALUES ('$user', '$realm', '$id', " .
+	"strftime('%Y-%m-%d %H:%M:%f','now'), " .
+	"'completed user input response for a new PPS MO')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/hs20/server/www/add-mo.php b/hs20/server/www/add-mo.php
new file mode 100644
index 0000000..a3b4513
--- /dev/null
+++ b/hs20/server/www/add-mo.php
@@ -0,0 +1,56 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+   die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+  $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+  die("Missing session id");
+
+$user = $_POST["user"];
+$pw = $_POST["password"];
+if (strlen($id) < 32 || !isset($user) || !isset($pw)) {
+  die("Invalid POST data");
+}
+
+if (strlen($user) < 1 || strncasecmp($user, "cert-", 5) == 0) {
+  echo "<html><body><p><red>Invalid username</red></p>\n";
+  echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
+  echo "</body></html>\n";
+  exit;
+}
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+   die("Session not found");
+}
+$realm = $row['realm'];
+
+$userrow = $db->query("SELECT identity FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
+if ($userrow) {
+  echo "<html><body><p><red>Selected username is not available</red></p>\n";
+  echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
+  echo "</body></html>\n";
+  exit;
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+
+if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', type='password' WHERE rowid=$rowid")) {
+  die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+	"VALUES ('$user', '$realm', '$id', " .
+	"strftime('%Y-%m-%d %H:%M:%f','now'), " .
+	"'completed user input response for a new PPS MO')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/hs20/server/www/cert-enroll.php b/hs20/server/www/cert-enroll.php
new file mode 100644
index 0000000..f023ca5
--- /dev/null
+++ b/hs20/server/www/cert-enroll.php
@@ -0,0 +1,39 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+   die($sqliteerror);
+}
+
+if (isset($_GET["id"]))
+  $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
+else
+  die("Missing session id");
+if (strlen($id) < 32)
+  die("Invalid session id");
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+   die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+$realm = $row['realm'];
+
+$user = sha1(mt_rand());
+
+if (!$db->exec("UPDATE sessions SET user='$user', type='cert' WHERE rowid=$rowid")) {
+  die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+	"VALUES ('', '$realm', '$id', " .
+	"strftime('%Y-%m-%d %H:%M:%f','now'), " .
+	"'completed user input response for client certificate enrollment')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/hs20/server/www/config.php b/hs20/server/www/config.php
new file mode 100644
index 0000000..e3af435
--- /dev/null
+++ b/hs20/server/www/config.php
@@ -0,0 +1,4 @@
+<?php
+$osu_root = "/home/user/hs20-server";
+$osu_db = "sqlite:$osu_root/AS/DB/eap_user.db";
+?>
diff --git a/hs20/server/www/est.php b/hs20/server/www/est.php
new file mode 100644
index 0000000..a45648b
--- /dev/null
+++ b/hs20/server/www/est.php
@@ -0,0 +1,198 @@
+<?php
+
+require('config.php');
+
+$params = split("/", $_SERVER["PATH_INFO"], 3);
+$realm = $params[1];
+$cmd = $params[2];
+$method = $_SERVER["REQUEST_METHOD"];
+
+unset($user);
+unset($rowid);
+
+if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
+  $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
+		  'uri'=>1, 'response'=>1);
+  $data = array();
+  $keys = implode('|', array_keys($needed));
+  preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
+		 $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
+  foreach ($matches as $m) {
+    $data[$m[1]] = $m[3] ? $m[3] : $m[4];
+    unset($needed[$m[1]]);
+  }
+  if ($needed) {
+    error_log("EST: Missing auth parameter");
+    die('Authentication failed');
+  }
+  $user = $data['username'];
+  if (strlen($user) < 1) {
+    error_log("EST: Empty username");
+    die('Authentication failed');
+  }
+
+  $db = new PDO($osu_db);
+  if (!$db) {
+    error_log("EST: Could not access database");
+    die("Could not access database");
+  }
+
+  $sql = "SELECT rowid,password,operation FROM sessions " .
+    "WHERE user='$user' AND realm='$realm'";
+  $q = $db->query($sql);
+  if (!$q) {
+    error_log("EST: Session not found for user=$user realm=$realm");
+    die("Session not found");
+  }
+  $row = $q->fetch();
+  if (!$row) {
+    error_log("EST: Session fetch failed for user=$user realm=$realm");
+    die('Session not found');
+  }
+  $rowid = $row['rowid'];
+
+  $oper = $row['operation'];
+  if ($oper != '5') {
+    error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
+    die("Session not found");
+  }
+  $pw = $row['password'];
+  if (strlen($pw) < 1) {
+    error_log("EST: Empty password for user=$user realm=$realm");
+    die('Authentication failed');
+  }
+
+  $A1 = md5($user . ':' . $realm . ':' . $pw);
+  $A2 = md5($method . ':' . $data['uri']);
+  $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
+	      $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
+  if ($data['response'] != $resp) {
+    error_log("EST: Incorrect authentication response for user=$user realm=$realm");
+    die('Authentication failed');
+  }
+}
+
+
+if ($method == "GET" && $cmd == "cacerts") {
+  $fname = "$osu_root/est/$realm-cacerts.pkcs7";
+  if (!file_exists($fname)) {
+    error_log("EST: cacerts - unknown realm $realm");
+    die("Unknown realm");
+  }
+
+  header("Content-Transfer-Encoding: base64");
+  header("Content-Type: application/pkcs7-mime");
+
+  $data = file_get_contents($fname);
+  echo wordwrap(base64_encode($data), 72, "\n", true);
+  echo "\n";
+  error_log("EST: cacerts");
+} else if ($method == "GET" && $cmd == "csrattrs") {
+  header("Content-Transfer-Encoding: base64");
+  header("Content-Type: application/csrattrs");
+  readfile("$osu_root/est/est-attrs.b64");
+  error_log("EST: csrattrs");
+} else if ($method == "POST" && $cmd == "simpleenroll") {
+  if (!isset($user) || strlen($user) == 0) {
+    header('HTTP/1.1 401 Unauthorized');
+    header('WWW-Authenticate: Digest realm="'.$realm.
+	   '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+    error_log("EST: simpleenroll - require authentication");
+    die('Authentication required');
+  }
+  if (!isset($_SERVER["CONTENT_TYPE"])) {
+    error_log("EST: simpleenroll without Content-Type");
+    die("Missing Content-Type");
+  }
+  if (!stristr($_SERVER["CONTENT_TYPE"], "application/pkcs10")) {
+    error_log("EST: simpleenroll - unexpected Content-Type: " .
+	      $_SERVER["CONTENT_TYPE"]);
+    die("Unexpected Content-Type");
+  }
+
+  $data = file_get_contents("php://input");
+  error_log("EST: simpleenroll - POST data from php://input: " . $data);
+  $req = base64_decode($data);
+  if ($req == FALSE) {
+    error_log("EST: simpleenroll - Invalid base64-encoded PKCS#10 data");
+    die("Invalid base64-encoded PKCS#10 data");
+  }
+  $cadir = "$osu_root/est";
+  $reqfile = "$cadir/tmp/cert-req.pkcs10";
+  $f = fopen($reqfile, "wb");
+  fwrite($f, $req);
+  fclose($f);
+
+  $req_pem = "$reqfile.pem";
+  if (file_exists($req_pem))
+    unlink($req_pem);
+  exec("openssl req -in $reqfile -inform DER -out $req_pem -outform PEM");
+  if (!file_exists($req_pem)) {
+    error_log("EST: simpleenroll - Failed to parse certificate request");
+    die("Failed to parse certificate request");
+  }
+
+  /* FIX: validate request and add HS 2.0 extensions to cert */
+  $cert_pem = "$cadir/tmp/req-signed.pem";
+  if (file_exists($cert_pem))
+    unlink($cert_pem);
+  exec("openssl x509 -req -in $req_pem -CAkey $cadir/cakey.pem -out $cert_pem -CA $cadir/cacert.pem -CAserial $cadir/serial -days 365 -text");
+  if (!file_exists($cert_pem)) {
+    error_log("EST: simpleenroll - Failed to sign certificate");
+    die("Failed to sign certificate");
+  }
+
+  $cert = file_get_contents($cert_pem);
+  $handle = popen("openssl x509 -in $cert_pem -serial -noout", "r");
+  $serial = fread($handle, 200);
+  pclose($handle);
+  $pattern = "/serial=(?P<snhex>[0-9a-fA-F:]*)/m";
+  preg_match($pattern, $serial, $matches);
+  if (!isset($matches['snhex']) || strlen($matches['snhex']) < 1) {
+    error_log("EST: simpleenroll - Could not get serial number");
+    die("Could not get serial number");
+  }
+  $sn = str_replace(":", "", strtoupper($matches['snhex']));
+
+  $user = "cert-$sn";
+  error_log("EST: user = $user");
+
+  $cert_der = "$cadir/tmp/req-signed.der";
+  if (file_exists($cert_der))
+    unlink($cert_der);
+  exec("openssl x509 -in $cert_pem -inform PEM -out $cert_der -outform DER");
+  if (!file_exists($cert_der)) {
+    error_log("EST: simpleenroll - Failed to convert certificate");
+    die("Failed to convert certificate");
+  }
+  $der = file_get_contents($cert_der);
+  $fingerprint = hash("sha256", $der);
+
+  $pkcs7 = "$cadir/tmp/est-client.pkcs7";
+  if (file_exists($pkcs7))
+    unlink($pkcs7);
+  exec("openssl crl2pkcs7 -nocrl -certfile $cert_pem -out $pkcs7 -outform DER");
+  if (!file_exists($pkcs7)) {
+    error_log("EST: simpleenroll - Failed to prepare PKCS#7 file");
+    die("Failed to prepare PKCS#7 file");
+  }
+
+  if (!$db->exec("UPDATE sessions SET user='$user', cert='$fingerprint', cert_pem='$cert' WHERE rowid=$rowid")) {
+    error_log("EST: simpleenroll - Failed to update session database");
+    die("Failed to update session database");
+  }
+
+  header("Content-Transfer-Encoding: base64");
+  header("Content-Type: application/pkcs7-mime");
+
+  $data = file_get_contents($pkcs7);
+  $resp = wordwrap(base64_encode($data), 72, "\n", true);
+  echo $resp . "\n";
+  error_log("EST: simpleenroll - PKCS#7 response: " . $resp);
+} else {
+  header("HTTP/1.0 404 Not Found");
+  error_log("EST: Unexpected method or path");
+  die("Unexpected method or path");
+}
+
+?>
diff --git a/hs20/server/www/free-remediation.php b/hs20/server/www/free-remediation.php
new file mode 100644
index 0000000..5648b30
--- /dev/null
+++ b/hs20/server/www/free-remediation.php
@@ -0,0 +1,19 @@
+<html>
+<head>
+<title>Hotspot 2.0 - public and free hotspot - remediation</title>
+</head>
+<body>
+
+<h3>Hotspot 2.0 - public and free hotspot</h3>
+
+<p>Terms and conditions have changed. You need to accept the new terms
+to continue using this network.</p>
+
+<p>Terms and conditions..</p>
+
+<?php
+echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Accept</a><br>\n";
+?>
+
+</body>
+</html>
diff --git a/hs20/server/www/free.php b/hs20/server/www/free.php
new file mode 100644
index 0000000..8195069
--- /dev/null
+++ b/hs20/server/www/free.php
@@ -0,0 +1,23 @@
+<html>
+<head>
+<title>Hotspot 2.0 - public and free hotspot</title>
+</head>
+<body>
+
+<?php
+
+$id = $_GET["session_id"];
+
+echo "<h3>Hotspot 2.0 - public and free hotspot</h3>\n";
+
+echo "<form action=\"add-free.php\" method=\"POST\">\n";
+echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+
+?>
+
+<p>Terms and conditions..</p>
+<input type="submit" value="Accept">
+</form>
+
+</body>
+</html>
diff --git a/hs20/server/www/redirect.php b/hs20/server/www/redirect.php
new file mode 100644
index 0000000..8fc9cd6
--- /dev/null
+++ b/hs20/server/www/redirect.php
@@ -0,0 +1,32 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+   die($sqliteerror);
+}
+
+if (isset($_GET["id"]))
+	$id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
+else
+	$id = 0;
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+   die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+
+header("Location: $uri", true, 302);
+
+$user = $row['user'];
+$realm = $row['realm'];
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+	  "VALUES ('$user', '$realm', '$id', " .
+	  "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+	  "'redirected after user input')");
+
+?>
diff --git a/hs20/server/www/remediation.php b/hs20/server/www/remediation.php
new file mode 100644
index 0000000..392a7bd
--- /dev/null
+++ b/hs20/server/www/remediation.php
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Hotspot 2.0 subscription remediation</title>
+</head>
+<body>
+
+<?php
+
+echo "SessionID: " . $_GET["session_id"] . "<br>\n";
+
+echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
+
+?>
+
+This will provide a new machine-generated password.
+
+</body>
+</html>
diff --git a/hs20/server/www/signup.php b/hs20/server/www/signup.php
new file mode 100644
index 0000000..a626704
--- /dev/null
+++ b/hs20/server/www/signup.php
@@ -0,0 +1,46 @@
+<html>
+<head>
+<title>Hotspot 2.0 signup</title>
+</head>
+<body>
+
+<?php
+
+$id = $_GET["session_id"];
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+   die($sqliteerror);
+}
+
+$row = $db->query("SELECT realm FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+   die("Session not found");
+}
+$realm = $row['realm'];
+
+echo "<h3>Sign up for a subscription - $realm</h3>\n";
+
+$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
+if ($row && strlen($row['value']) > 0) {
+  echo "<p><a href=\"free.php?session_id=$id\">Sign up for free access</a></p>\n";
+}
+
+echo "<form action=\"add-mo.php\" method=\"POST\">\n";
+echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+?>
+Select a username and password. Leave password empty to get automatically
+generated and machine managed password.<br>
+Username: <input type="text" name="user"><br>
+Password: <input type="password" name="password"><br>
+<input type="submit" value="Complete subscription registration">
+</form>
+
+<?php
+echo "<p><a href=\"cert-enroll.php?id=$id\">Enroll a client certificate</a></p>\n"
+?>
+
+</body>
+</html>
diff --git a/hs20/server/www/spp.php b/hs20/server/www/spp.php
new file mode 100644
index 0000000..dde4434
--- /dev/null
+++ b/hs20/server/www/spp.php
@@ -0,0 +1,127 @@
+<?php
+
+require('config.php');
+
+if (!stristr($_SERVER["CONTENT_TYPE"], "application/soap+xml")) {
+  error_log("spp.php - Unexpected Content-Type " . $_SERVER["CONTENT_TYPE"]);
+  die("Unexpected Content-Type");
+}
+
+if ($_SERVER["REQUEST_METHOD"] != "POST") {
+  error_log("spp.php - Unexpected method " . $_SERVER["REQUEST_METHOD"]);
+  die("Unexpected method");
+}
+
+if (isset($_GET["realm"])) {
+  $realm = $_GET["realm"];
+  $realm = PREG_REPLACE("/[^0-9a-zA-Z\.\-]/i", '', $realm);
+} else {
+  error_log("spp.php - Realm not specified");
+  die("Realm not specified");
+}
+
+unset($user);
+putenv("HS20CERT");
+
+if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
+  $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
+		  'uri'=>1, 'response'=>1);
+  $data = array();
+  $keys = implode('|', array_keys($needed));
+  preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
+		 $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
+  foreach ($matches as $m) {
+    $data[$m[1]] = $m[3] ? $m[3] : $m[4];
+    unset($needed[$m[1]]);
+  }
+  if ($needed) {
+    error_log("spp.php - Authentication failed - missing: " . print_r($needed));
+    die('Authentication failed');
+  }
+  $user = $data['username'];
+  if (strlen($user) < 1) {
+    error_log("spp.php - Authentication failed - empty username");
+    die('Authentication failed');
+  }
+
+
+  $db = new PDO($osu_db);
+  if (!$db) {
+    error_log("spp.php - Could not access database");
+    die("Could not access database");
+  }
+  $row = $db->query("SELECT password FROM users " .
+		    "WHERE identity='$user' AND realm='$realm'")->fetch();
+  if (!$row) {
+    $row = $db->query("SELECT osu_password FROM users " .
+		      "WHERE osu_user='$user' AND realm='$realm'")->fetch();
+    $pw = $row['osu_password'];
+  } else
+    $pw = $row['password'];
+  if (!$row) {
+    error_log("spp.php - Authentication failed - user '$user' not found");
+    die('Authentication failed');
+  }
+  if (strlen($pw) < 1) {
+    error_log("spp.php - Authentication failed - empty password");
+    die('Authentication failed');
+  }
+
+  $A1 = md5($user . ':' . $realm . ':' . $pw);
+  $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
+  $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
+	      $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
+  if ($data['response'] != $resp) {
+    error_log("Authentication failure - response mismatch");
+    die('Authentication failed');
+  }
+} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
+	   $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
+	   isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
+  $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
+  putenv("HS20CERT=yes");
+} else if (!isset($_SERVER["PATH_INFO"]) ||
+	   $_SERVER["PATH_INFO"] != "/signup") {
+  header('HTTP/1.1 401 Unauthorized');
+  header('WWW-Authenticate: Digest realm="'.$realm.
+	 '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+  error_log("spp.php - Authentication required (not signup)");
+  die('Authentication required (not signup)');
+}
+
+
+if (isset($user) && strlen($user) > 0)
+  putenv("HS20USER=$user");
+else
+  putenv("HS20USER");
+
+putenv("HS20REALM=$realm");
+putenv("HS20POST=$HTTP_RAW_POST_DATA");
+$addr = $_SERVER["REMOTE_ADDR"];
+putenv("HS20ADDR=$addr");
+
+$last = exec("$osu_root/spp/hs20_spp_server -r$osu_root -f/tmp/hs20_spp_server.log", $output, $ret);
+
+if ($ret == 2) {
+  if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
+    header('HTTP/1.1 401 Unauthorized');
+    header('WWW-Authenticate: Digest realm="'.$realm.
+           '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+    error_log("spp.php - Authentication required (ret 2)");
+    die('Authentication required');
+  } else {
+    error_log("spp.php - Unexpected authentication error");
+    die("Unexpected authentication error");
+  }
+}
+if ($ret != 0) {
+  error_log("spp.php - Failed to process SPP request");
+  die("Failed to process SPP request");
+}
+//error_log("spp.php: Response: " . implode($output));
+
+header("Content-Type: application/soap+xml");
+
+echo implode($output);
+
+?>
diff --git a/hs20/server/www/users.php b/hs20/server/www/users.php
new file mode 100644
index 0000000..c340a33
--- /dev/null
+++ b/hs20/server/www/users.php
@@ -0,0 +1,349 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+   die($sqliteerror);
+}
+
+if (isset($_GET["id"])) {
+	$id = $_GET["id"];
+	if (!is_numeric($id))
+		$id = 0;
+} else
+	$id = 0;
+if (isset($_GET["cmd"]))
+	$cmd = $_GET["cmd"];
+else
+	$cmd = '';
+
+if ($cmd == 'eventlog' && $id > 0) {
+	$row = $db->query("SELECT dump FROM eventlog WHERE rowid=$id")->fetch();
+	$dump = $row['dump'];
+	if ($dump[0] == '<') {
+	  header("Content-type: text/xml");
+	  echo "<?xml version=\"1.0\"?>\n";
+	  echo $dump;
+	} else {
+	  header("Content-type: text/plain");
+	  echo $dump;
+	}
+	exit;
+}
+
+if ($cmd == 'mo' && $id > 0) {
+	$mo = $_GET["mo"];
+	if (!isset($mo))
+		exit;
+	if ($mo != "devinfo" && $mo != "devdetail" && $mo != "pps")
+		exit;
+	$row = $db->query("SELECT $mo FROM users WHERE rowid=$id")->fetch();
+	header("Content-type: text/xml");
+	echo "<?xml version=\"1.0\"?>\n";
+	echo $row[$mo];
+	exit;
+}
+
+if ($cmd == 'cert' && $id > 0) {
+	$row = $db->query("SELECT cert_pem FROM users WHERE rowid=$id")->fetch();
+	header("Content-type: text/plain");
+	echo $row['cert_pem'];
+	exit;
+}
+
+?>
+
+<html>
+<head><title>HS 2.0 users</title></head>
+<body>
+
+<?php
+
+if ($cmd == 'subrem-clear' && $id > 0) {
+	$db->exec("UPDATE users SET remediation='' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-user' && $id > 0) {
+	$db->exec("UPDATE users SET remediation='user' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-machine' && $id > 0) {
+	$db->exec("UPDATE users SET remediation='machine' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-policy' && $id > 0) {
+	$db->exec("UPDATE users SET remediation='policy' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-free' && $id > 0) {
+	$db->exec("UPDATE users SET remediation='free' WHERE rowid=$id");
+}
+if ($cmd == 'fetch-pps-on' && $id > 0) {
+	$db->exec("UPDATE users SET fetch_pps=1 WHERE rowid=$id");
+}
+if ($cmd == 'fetch-pps-off' && $id > 0) {
+	$db->exec("UPDATE users SET fetch_pps=0 WHERE rowid=$id");
+}
+if ($cmd == 'reset-pw' && $id > 0) {
+	$db->exec("UPDATE users SET password='ChangeMe' WHERE rowid=$id");
+}
+if ($cmd == "policy" && $id > 0 && isset($_GET["policy"])) {
+	$policy = $_GET["policy"];
+	if ($policy == "no-policy" ||
+	    is_readable("$osu_root/spp/policy/$policy.xml")) {
+		$db->exec("UPDATE users SET policy='$policy' WHERE rowid=$id");
+	}
+}
+if ($cmd == "account-type" && $id > 0 && isset($_GET["type"])) {
+	$type = $_GET["type"];
+	if ($type == "shared")
+		$db->exec("UPDATE users SET shared=1 WHERE rowid=$id");
+	if ($type == "default")
+		$db->exec("UPDATE users SET shared=0 WHERE rowid=$id");
+}
+
+if ($cmd == "set-osu-cred" && $id > 0) {
+  $osu_user = $_POST["osu_user"];
+  $osu_password = $_POST["osu_password"];
+  if (strlen($osu_user) == 0)
+    $osu_password = "";
+  $db->exec("UPDATE users SET osu_user='$osu_user', osu_password='$osu_password' WHERE rowid=$id");
+}
+
+$dump = 0;
+
+if ($id > 0) {
+
+if (isset($_GET["dump"])) {
+	$dump = $_GET["dump"];
+	if (!is_numeric($dump))
+		$dump = 0;
+} else
+	$dump = 0;
+
+echo "[<a href=\"users.php\">All users</a>] ";
+if ($dump == 0)
+	echo "[<a href=\"users.php?id=$id&dump=1\">Include debug dump</a>] ";
+else
+	echo "[<a href=\"users.php?id=$id\">Without debug dump</a>] ";
+echo "<br>\n";
+
+$row = $db->query("SELECT rowid,* FROM users WHERE rowid=$id")->fetch();
+
+echo "<H3>" . $row['identity'] . "@" . $row['realm'] . "</H3>\n";
+
+echo "MO: ";
+if (strlen($row['devinfo']) > 0) {
+	echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devinfo\">DevInfo</a>]\n";
+}
+if (strlen($row['devdetail']) > 0) {
+	echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devdetail\">DevDetail</a>]\n";
+}
+if (strlen($row['pps']) > 0) {
+	echo "[<a href=\"users.php?cmd=mo&id=$id&mo=pps\">PPS</a>]\n";
+}
+if (strlen($row['cert_pem']) > 0) {
+	echo "[<a href=\"users.php?cmd=cert&id=$id\">Certificate</a>]\n";
+}
+echo "<BR>\n";
+
+echo "Fetch PPS MO: ";
+if ($row['fetch_pps'] == "1") {
+	echo "On next connection " .
+		"[<a href=\"users.php?cmd=fetch-pps-off&id=$id\">" .
+		"do not fetch</a>]<br>\n";
+} else {
+	echo "Do not fetch " .
+		"[<a href=\"users.php?cmd=fetch-pps-on&id=$id\">" .
+		"request fetch</a>]<br>\n";
+}
+
+$cert = $row['cert'];
+if (strlen($cert) > 0) {
+  echo "Certificate fingerprint: $cert<br>\n";
+}
+
+echo "Remediation: ";
+$rem = $row['remediation'];
+if ($rem == "") {
+	echo "Not required";
+	echo " [<a href=\"users.php?cmd=subrem-add-user&id=" .
+		   $row['rowid'] . "\">add:user</a>]";
+	echo " [<a href=\"users.php?cmd=subrem-add-machine&id=" .
+		   $row['rowid'] . "\">add:machine</a>]";
+	echo " [<a href=\"users.php?cmd=subrem-add-policy&id=" .
+		   $row['rowid'] . "\">add:policy</a>]";
+	echo " [<a href=\"users.php?cmd=subrem-add-free&id=" .
+		   $row['rowid'] . "\">add:free</a>]";
+} else if ($rem == "user") {
+	echo "User [<a href=\"users.php?cmd=subrem-clear&id=" .
+		       $row['rowid'] . "\">clear</a>]";
+} else if ($rem == "policy") {
+	echo "Policy [<a href=\"users.php?cmd=subrem-clear&id=" .
+		       $row['rowid'] . "\">clear</a>]";
+} else if ($rem == "free") {
+	echo "Free [<a href=\"users.php?cmd=subrem-clear&id=" .
+		       $row['rowid'] . "\">clear</a>]";
+} else  {
+	echo "Machine [<a href=\"users.php?cmd=subrem-clear&id=" .
+			  $row['rowid'] . "\">clear</a>]";
+}
+echo "<br>\n";
+
+echo "<form>Policy: <select name=\"policy\" " .
+	"onChange=\"window.location='users.php?cmd=policy&id=" .
+	$row['rowid'] . "&policy=' + this.value;\">\n";
+echo "<option value=\"" . $row['policy'] . "\" selected>" . $row['policy'] .
+      "</option>\n";
+$files = scandir("$osu_root/spp/policy");
+foreach ($files as $file) {
+	if (!preg_match("/.xml$/", $file))
+		continue;
+	if ($file == $row['policy'] . ".xml")
+		continue;
+	$p = substr($file, 0, -4);
+	echo "<option value=\"$p\">$p</option>\n";
+}
+echo "<option value=\"no-policy\">no policy</option>\n";
+echo "</select></form>\n";
+
+echo "<form>Account type: <select name=\"type\" " .
+	"onChange=\"window.location='users.php?cmd=account-type&id=" .
+	$row['rowid'] . "&type=' + this.value;\">\n";
+if ($row['shared'] > 0) {
+  $default_sel = "";
+  $shared_sel = " selected";
+} else {
+  $default_sel = " selected";
+  $shared_sel = "";
+}
+echo "<option value=\"default\"$default_sel>default</option>\n";
+echo "<option value=\"shared\"$shared_sel>shared</option>\n";
+echo "</select></form>\n";
+
+echo "Phase 2 method(s): " . $row['methods'] . "<br>\n";
+
+echo "<br>\n";
+echo "<a href=\"users.php?cmd=reset-pw&id=" .
+	 $row['rowid'] . "\">Reset AAA password</a><br>\n";
+
+echo "<br>\n";
+echo "<form action=\"users.php?cmd=set-osu-cred&id=" . $row['rowid'] .
+  "\" method=\"POST\">\n";
+echo "OSU credentials (if username empty, AAA credentials are used):<br>\n";
+echo "username: <input type=\"text\" name=\"osu_user\" value=\"" .
+  $row['osu_user'] . "\">\n";
+echo "password: <input type=\"password\" name=\"osu_password\">\n";
+echo "<input type=\"submit\" value=\"Set OSU credentials\">\n";
+echo "</form>\n";
+
+echo "<hr>\n";
+
+$user = $row['identity'];
+$osu_user = $row['osu_user'];
+$realm = $row['realm'];
+}
+
+if ($id > 0 || ($id == 0 && $cmd == 'eventlog')) {
+
+  if ($id == 0) {
+    echo "[<a href=\"users.php\">All users</a>] ";
+    echo "<br>\n";
+  }
+
+echo "<table border=1>\n";
+echo "<tr>";
+if ($id == 0) {
+  echo "<th>user<th>realm";
+}
+echo "<th>time<th>address<th>sessionID<th>notes";
+if ($dump > 0)
+	echo "<th>dump";
+echo "\n";
+if (isset($_GET["limit"])) {
+	$limit = $_GET["limit"];
+	if (!is_numeric($limit))
+		$limit = 20;
+} else
+	$limit = 20;
+if ($id == 0)
+  $res = $db->query("SELECT rowid,* FROM eventlog ORDER BY timestamp DESC LIMIT $limit");
+else if (strlen($osu_user) > 0)
+  $res = $db->query("SELECT rowid,* FROM eventlog WHERE (user='$user' OR user='$osu_user') AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
+else
+  $res = $db->query("SELECT rowid,* FROM eventlog WHERE user='$user' AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
+foreach ($res as $row) {
+	echo "<tr>";
+	if ($id == 0) {
+	  echo "<td>" . $row['user'] . "\n";
+	  echo "<td>" . $row['realm'] . "\n";
+	}
+	echo "<td>" . $row['timestamp'] . "\n";
+	echo "<td>" . $row['addr'] . "\n";
+	echo "<td>" . $row['sessionid'] . "\n";
+	echo "<td>" . $row['notes'] . "\n";
+	$d = $row['dump'];
+	if (strlen($d) > 0) {
+		echo "[<a href=\"users.php?cmd=eventlog&id=" . $row['rowid'] .
+		  "\">";
+		if ($d[0] == '<')
+		  echo "XML";
+		else
+		  echo "txt";
+		echo "</a>]\n";
+		if ($dump > 0)
+			echo "<td>" . htmlspecialchars($d) . "\n";
+	}
+}
+echo "</table>\n";
+
+}
+
+
+if ($id == 0 && $cmd != 'eventlog') {
+
+echo "[<a href=\"users.php?cmd=eventlog&limit=50\">Eventlog</a>] ";
+echo "<br>\n";
+
+echo "<table border=1>\n";
+echo "<tr><th>User<th>Realm<th>Remediation<th>Policy<th>Account type<th>Phase 2 method(s)<th>DevId\n";
+
+$res = $db->query('SELECT rowid,* FROM users WHERE phase2=1');
+foreach ($res as $row) {
+	echo "<tr><td><a href=\"users.php?id=" . $row['rowid'] . "\"> " .
+	    $row['identity'] . " </a>";
+	echo "<td>" . $row['realm'];
+	$rem = $row['remediation'];
+	echo "<td>";
+	if ($rem == "") {
+		echo "Not required";
+	} else if ($rem == "user") {
+		echo "User";
+	} else if ($rem == "policy") {
+		echo "Policy";
+	} else if ($rem == "free") {
+		echo "Free";
+	} else  {
+		echo "Machine";
+	}
+	echo "<td>" . $row['policy'];
+	if ($row['shared'] > 0)
+	  echo "<td>shared";
+	else
+	  echo "<td>default";
+	echo "<td>" . $row['methods'];
+	echo "<td>";
+	$xml = xml_parser_create();
+	xml_parse_into_struct($xml, $row['devinfo'], $devinfo);
+	foreach($devinfo as $k) {
+	  if ($k['tag'] == 'DEVID') {
+	    echo $k['value'];
+	    break;
+	  }
+	}
+	echo "\n";
+}
+echo "</table>\n";
+
+}
+
+?>
+
+</html>
diff --git a/src/Makefile b/src/Makefile
index d73a175..10e0171 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
 
 all:
 	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 9c41962..adfd3df 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 9540531..6290d3f 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -10,7 +10,6 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
-#include "drivers/driver.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "hostapd.h"
@@ -44,7 +43,7 @@
 	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
 			     radius_client_get_id(hapd->radius));
 	if (msg == NULL) {
-		printf("Could not create net RADIUS packet\n");
+		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
 		return NULL;
 	}
 
@@ -55,7 +54,7 @@
 			    sta->acct_session_id_hi, sta->acct_session_id_lo);
 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
 					 (u8 *) buf, os_strlen(buf))) {
-			printf("Could not add Acct-Session-Id\n");
+			wpa_printf(MSG_INFO, "Could not add Acct-Session-Id");
 			goto fail;
 		}
 	} else {
@@ -64,7 +63,7 @@
 
 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
 				       status_type)) {
-		printf("Could not add Acct-Status-Type\n");
+		wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
 		goto fail;
 	}
 
@@ -74,7 +73,7 @@
 				       hapd->conf->ieee802_1x ?
 				       RADIUS_ACCT_AUTHENTIC_RADIUS :
 				       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
-		printf("Could not add Acct-Authentic\n");
+		wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
 		goto fail;
 	}
 
@@ -99,7 +98,7 @@
 
 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
 					 len)) {
-			printf("Could not add User-Name\n");
+			wpa_printf(MSG_INFO, "Could not add User-Name");
 			goto fail;
 		}
 	}
@@ -117,7 +116,7 @@
 
 			if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
 						 val, len)) {
-				printf("Could not add Class\n");
+				wpa_printf(MSG_INFO, "Could not add Class");
 				goto fail;
 			}
 		}
@@ -202,7 +201,6 @@
 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	struct radius_msg *msg;
-	struct os_time t;
 	int interval;
 
 	if (sta->acct_session_started)
@@ -213,8 +211,7 @@
 		       "starting accounting session %08X-%08X",
 		       sta->acct_session_id_hi, sta->acct_session_id_lo);
 
-	os_get_time(&t);
-	sta->acct_session_start = t.sec;
+	os_get_reltime(&sta->acct_session_start);
 	sta->last_rx_bytes = sta->last_tx_bytes = 0;
 	sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
 	hostapd_drv_sta_clear_stats(hapd, sta->addr);
@@ -244,6 +241,7 @@
 	struct radius_msg *msg;
 	int cause = sta->acct_terminate_cause;
 	struct hostap_sta_driver_data data;
+	struct os_reltime now_r, diff;
 	struct os_time now;
 	u32 gigawords;
 
@@ -254,14 +252,16 @@
 			     stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
 			     RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
 	if (!msg) {
-		printf("Could not create RADIUS Accounting message\n");
+		wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
 		return;
 	}
 
+	os_get_reltime(&now_r);
 	os_get_time(&now);
+	os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
-				       now.sec - sta->acct_session_start)) {
-		printf("Could not add Acct-Session-Time\n");
+				       diff.sec)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
 		goto fail;
 	}
 
@@ -269,19 +269,19 @@
 		if (!radius_msg_add_attr_int32(msg,
 					       RADIUS_ATTR_ACCT_INPUT_PACKETS,
 					       data.rx_packets)) {
-			printf("Could not add Acct-Input-Packets\n");
+			wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
 			goto fail;
 		}
 		if (!radius_msg_add_attr_int32(msg,
 					       RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
 					       data.tx_packets)) {
-			printf("Could not add Acct-Output-Packets\n");
+			wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
 			goto fail;
 		}
 		if (!radius_msg_add_attr_int32(msg,
 					       RADIUS_ATTR_ACCT_INPUT_OCTETS,
 					       data.rx_bytes)) {
-			printf("Could not add Acct-Input-Octets\n");
+			wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
 			goto fail;
 		}
 		gigawords = sta->acct_input_gigawords;
@@ -292,13 +292,13 @@
 		    !radius_msg_add_attr_int32(
 			    msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
 			    gigawords)) {
-			printf("Could not add Acct-Input-Gigawords\n");
+			wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
 			goto fail;
 		}
 		if (!radius_msg_add_attr_int32(msg,
 					       RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
 					       data.tx_bytes)) {
-			printf("Could not add Acct-Output-Octets\n");
+			wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
 			goto fail;
 		}
 		gigawords = sta->acct_output_gigawords;
@@ -309,14 +309,14 @@
 		    !radius_msg_add_attr_int32(
 			    msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
 			    gigawords)) {
-			printf("Could not add Acct-Output-Gigawords\n");
+			wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
 			goto fail;
 		}
 	}
 
 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
 				       now.sec)) {
-		printf("Could not add Event-Timestamp\n");
+		wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
 		goto fail;
 	}
 
@@ -326,7 +326,7 @@
 	if (stop && cause &&
 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
 				       cause)) {
-		printf("Could not add Acct-Terminate-Cause\n");
+		wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
 		goto fail;
 	}
 
@@ -400,13 +400,12 @@
 		   void *data)
 {
 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
-		printf("Unknown RADIUS message code\n");
+		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
 		return RADIUS_RX_UNKNOWN;
 	}
 
 	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
-		printf("Incoming RADIUS packet did not have correct "
-		       "Authenticator - dropped\n");
+		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
 		return RADIUS_RX_INVALID_AUTHENTICATOR;
 	}
 
@@ -432,7 +431,7 @@
 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
 				       RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
 	{
-		printf("Could not add Acct-Terminate-Cause\n");
+		wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
 		radius_msg_free(msg);
 		return;
 	}
diff --git a/src/ap/acs.c b/src/ap/acs.c
index d5e3f59..b94b8a4 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -13,6 +13,7 @@
 #include "utils/common.h"
 #include "utils/list.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "drivers/driver.h"
 #include "hostapd.h"
 #include "ap_drv_ops.h"
@@ -279,10 +280,11 @@
 }
 
 
-void acs_fail(struct hostapd_iface *iface)
+static void acs_fail(struct hostapd_iface *iface)
 {
 	wpa_printf(MSG_ERROR, "ACS: Failed to start");
 	acs_cleanup(iface);
+	hostapd_disable_iface(iface);
 }
 
 
@@ -352,23 +354,26 @@
 }
 
 
-static int acs_usable_chan(struct hostapd_channel_data *chan)
-{
-	if (dl_list_empty(&chan->survey_list))
-		return 0;
-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
-		return 0;
-	return 1;
-}
-
-
 static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
 {
 	const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
 				157, 184, 192 };
 	unsigned int i;
 
-	for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++)
+	for (i = 0; i < ARRAY_SIZE(allowed); i++)
+		if (chan->chan == allowed[i])
+			return 1;
+
+	return 0;
+}
+
+
+static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+{
+	const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(allowed); i++)
 		if (chan->chan == allowed[i])
 			return 1;
 
@@ -398,28 +403,54 @@
 }
 
 
+static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey;
+
+	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+	{
+		if (!acs_survey_is_sufficient(survey)) {
+			wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data",
+				   chan->chan);
+			return 0;
+		}
+	}
+
+	return 1;
+
+}
+
+
 static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
 {
 	int i;
 	struct hostapd_channel_data *chan;
-	struct freq_survey *survey;
+	int valid = 0;
 
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
 		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
-		dl_list_for_each(survey, &chan->survey_list,
-				 struct freq_survey, list)
-		{
-			if (!acs_survey_is_sufficient(survey)) {
-				wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data",
-					   chan->chan);
-				return 0;
-			}
-		}
+		if (!acs_survey_list_is_sufficient(chan))
+			continue;
+
+		valid++;
 	}
 
+	/* We need at least survey data for one channel */
+	return !!valid;
+}
+
+
+static int acs_usable_chan(struct hostapd_channel_data *chan)
+{
+	if (dl_list_empty(&chan->survey_list))
+		return 0;
+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+		return 0;
+	if (!acs_survey_list_is_sufficient(chan))
+		return 0;
 	return 1;
 }
 
@@ -456,7 +487,7 @@
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
 
-		if (!acs_usable_chan(chan))
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
 		if (chan->freq == freq)
@@ -476,7 +507,8 @@
 static struct hostapd_channel_data *
 acs_find_ideal_chan(struct hostapd_iface *iface)
 {
-	struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL;
+	struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
+		*rand_chan = NULL;
 	long double factor, ideal_factor = 0;
 	int i, j;
 	int n_chans = 1;
@@ -508,9 +540,10 @@
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
 
-		if (!acs_usable_chan(chan))
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
+
 		/* HT40 on 5 GHz has a limited set of primary channels as per
 		 * 11n Annex J */
 		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
@@ -522,14 +555,26 @@
 			continue;
 		}
 
-		factor = chan->interference_factor;
+		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		    iface->conf->ieee80211ac &&
+		    iface->conf->vht_oper_chwidth == 1 &&
+		    !acs_usable_vht80_chan(chan)) {
+			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
+				   chan->chan);
+			continue;
+		}
+
+		factor = 0;
+		if (acs_usable_chan(chan))
+			factor = chan->interference_factor;
 
 		for (j = 1; j < n_chans; j++) {
 			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
 			if (!adj_chan)
 				break;
 
-			factor += adj_chan->interference_factor;
+			if (acs_usable_chan(adj_chan))
+				factor += adj_chan->interference_factor;
 		}
 
 		if (j != n_chans) {
@@ -548,22 +593,22 @@
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) - 5);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) - 10);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) + 5);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) + 10);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 			}
 		}
@@ -571,39 +616,49 @@
 		wpa_printf(MSG_DEBUG, "ACS:  * channel %d: total interference = %Lg",
 			   chan->chan, factor);
 
-		if (!ideal_chan || factor < ideal_factor) {
+		if (acs_usable_chan(chan) &&
+		    (!ideal_chan || factor < ideal_factor)) {
 			ideal_factor = factor;
 			ideal_chan = chan;
 		}
+
+		/* This channel would at least be usable */
+		if (!rand_chan)
+			rand_chan = chan;
 	}
 
-	if (ideal_chan)
+	if (ideal_chan) {
 		wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
 			   ideal_chan->chan, ideal_chan->freq, ideal_factor);
+		return ideal_chan;
+	}
 
-	return ideal_chan;
+	return rand_chan;
 }
 
 
 static void acs_adjust_vht_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:
-		iface->conf->vht_oper_centr_freq_seg0_idx =
-			iface->conf->channel + 2;
+		offset = 2 * iface->conf->secondary_channel;
 		break;
 	case VHT_CHANWIDTH_80MHZ:
-		iface->conf->vht_oper_centr_freq_seg0_idx =
-			iface->conf->channel + 6;
+		offset = 6;
 		break;
 	default:
 		/* TODO: How can this be calculated? Adjust
 		 * acs_find_ideal_chan() */
 		wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
-		break;
+		return;
 	}
+
+	iface->conf->vht_oper_centr_freq_seg0_idx =
+		iface->conf->channel + offset;
 }
 
 
@@ -655,6 +710,7 @@
 	ideal_chan = acs_find_ideal_chan(iface);
 	if (!ideal_chan) {
 		wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+		err = -1;
 		goto fail;
 	}
 
@@ -663,24 +719,20 @@
 	if (iface->conf->ieee80211ac)
 		acs_adjust_vht_center_freq(iface);
 
+	err = 0;
+fail:
 	/*
 	 * hostapd_setup_interface_complete() will return -1 on failure,
 	 * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
 	 */
-	switch (hostapd_acs_completed(iface)) {
-	case HOSTAPD_CHAN_VALID:
+	if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
 		acs_cleanup(iface);
 		return;
-	case HOSTAPD_CHAN_INVALID:
-	case HOSTAPD_CHAN_ACS:
-	default:
-		/* This can possibly happen if channel parameters (secondary
-		 * channel, center frequencies) are misconfigured */
-		wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
-		goto fail;
 	}
 
-fail:
+	/* This can possibly happen if channel parameters (secondary
+	 * channel, center frequencies) are misconfigured */
+	wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
 	acs_fail(iface);
 }
 
@@ -697,21 +749,24 @@
 	err = hostapd_drv_get_survey(iface->bss[0], 0);
 	if (err) {
 		wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
-		acs_fail(iface);
+		goto fail;
 	}
 
 	if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
 		err = acs_request_scan(iface);
 		if (err) {
 			wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
-			acs_fail(iface);
-			return;
+			goto fail;
 		}
 
 		return;
 	}
 
 	acs_study(iface);
+	return;
+fail:
+	hostapd_acs_completed(iface, 1);
+	acs_fail(iface);
 }
 
 
@@ -746,6 +801,7 @@
 	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
 		wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
 		acs_cleanup(iface);
+		os_free(params.freqs);
 		return -1;
 	}
 
@@ -766,5 +822,8 @@
 	if (err < 0)
 		return HOSTAPD_CHAN_INVALID;
 
+	hostapd_set_state(iface, HAPD_IFACE_ACS);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
+
 	return HOSTAPD_CHAN_ACS;
 }
diff --git a/src/ap/acs.h b/src/ap/acs.h
index 0d1d0f1..fc85259 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -13,7 +13,6 @@
 #ifdef CONFIG_ACS
 
 enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
-int hostapd_acs_completed(struct hostapd_iface *iface);
 
 #else /* CONFIG_ACS */
 
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index caf75c4..d127550 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration helper functions
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -73,6 +73,7 @@
 #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 */
@@ -106,9 +107,9 @@
 	const struct hostapd_wmm_ac_params ac_be =
 		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
 	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
-		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+		{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
 	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
-		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+		{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
 	const struct hostapd_tx_queue_params txq_bk =
 		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
 	const struct hostapd_tx_queue_params txq_be =
@@ -130,9 +131,17 @@
 		os_free(bss);
 		return NULL;
 	}
+	conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *));
+	if (conf->bss == NULL) {
+		os_free(conf);
+		os_free(bss);
+		return NULL;
+	}
+	conf->bss[0] = bss;
 
 	bss->radius = os_zalloc(sizeof(*bss->radius));
 	if (bss->radius == NULL) {
+		os_free(conf->bss);
 		os_free(conf);
 		os_free(bss);
 		return NULL;
@@ -141,12 +150,13 @@
 	hostapd_config_defaults_bss(bss);
 
 	conf->num_bss = 1;
-	conf->bss = bss;
 
 	conf->beacon_int = 100;
 	conf->rts_threshold = -1; /* use driver default: 2347 */
 	conf->fragm_threshold = -1; /* user driver default: 2346 */
 	conf->send_probe_response = 1;
+	/* Set to invalid value means do not add Power Constraint IE */
+	conf->local_pwr_constraint = -1;
 
 	conf->wmm_ac_params[0] = ac_be;
 	conf->wmm_ac_params[1] = ac_bk;
@@ -164,11 +174,11 @@
 	conf->ap_table_expiration_time = 60;
 
 #ifdef CONFIG_TESTING_OPTIONS
-	conf->ignore_probe_probability = 0.0d;
-	conf->ignore_auth_probability = 0.0d;
-	conf->ignore_assoc_probability = 0.0d;
-	conf->ignore_reassoc_probability = 0.0d;
-	conf->corrupt_gtk_rekey_mic_probability = 0.0d;
+	conf->ignore_probe_probability = 0.0;
+	conf->ignore_auth_probability = 0.0;
+	conf->ignore_assoc_probability = 0.0;
+	conf->ignore_reassoc_probability = 0.0;
+	conf->corrupt_gtk_rekey_mic_probability = 0.0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_ACS
@@ -329,20 +339,6 @@
 }
 
 
-int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b)
-{
-	int i;
-
-	if (a->idx != b->idx || a->default_len != b->default_len)
-		return 1;
-	for (i = 0; i < NUM_WEP_KEYS; i++)
-		if (a->len[i] != b->len[i] ||
-		    os_memcmp(a->key[i], b->key[i], a->len[i]) != 0)
-			return 1;
-	return 0;
-}
-
-
 static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
 				       int num_servers)
 {
@@ -379,10 +375,11 @@
 }
 
 
-static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
 {
+	hostapd_config_free_radius_attr(user->accept_attr);
 	os_free(user->identity);
-	os_free(user->password);
+	bin_clear_free(user->password, user->password_len);
 	os_free(user);
 }
 
@@ -391,13 +388,13 @@
 {
 	int i;
 	for (i = 0; i < NUM_WEP_KEYS; i++) {
-		os_free(keys->key[i]);
+		bin_clear_free(keys->key[i], keys->len[i]);
 		keys->key[i] = NULL;
 	}
 }
 
 
-static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
 	struct hostapd_wpa_psk *psk, *prev;
 	struct hostapd_eap_user *user, *prev_user;
@@ -409,10 +406,10 @@
 	while (psk) {
 		prev = psk;
 		psk = psk->next;
-		os_free(prev);
+		bin_clear_free(prev, sizeof(*prev));
 	}
 
-	os_free(conf->ssid.wpa_passphrase);
+	str_clear_free(conf->ssid.wpa_passphrase);
 	os_free(conf->ssid.wpa_psk_file);
 	hostapd_config_free_wep(&conf->ssid.wep);
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -427,15 +424,16 @@
 	}
 	os_free(conf->eap_user_sqlite);
 
-	os_free(conf->dump_log_name);
 	os_free(conf->eap_req_id_text);
 	os_free(conf->accept_mac);
 	os_free(conf->deny_mac);
 	os_free(conf->nas_identifier);
-	hostapd_config_free_radius(conf->radius->auth_servers,
-				   conf->radius->num_auth_servers);
-	hostapd_config_free_radius(conf->radius->acct_servers,
-				   conf->radius->num_acct_servers);
+	if (conf->radius) {
+		hostapd_config_free_radius(conf->radius->auth_servers,
+					   conf->radius->num_auth_servers);
+		hostapd_config_free_radius(conf->radius->acct_servers,
+					   conf->radius->num_acct_servers);
+	}
 	hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
 	hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
 	os_free(conf->rsn_preauth_interfaces);
@@ -518,13 +516,36 @@
 	os_free(conf->hs20_wan_metrics);
 	os_free(conf->hs20_connection_capability);
 	os_free(conf->hs20_operating_class);
+	os_free(conf->hs20_icons);
+	if (conf->hs20_osu_providers) {
+		size_t i;
+		for (i = 0; i < conf->hs20_osu_providers_count; i++) {
+			struct hs20_osu_provider *p;
+			size_t j;
+			p = &conf->hs20_osu_providers[i];
+			os_free(p->friendly_name);
+			os_free(p->server_uri);
+			os_free(p->method_list);
+			for (j = 0; j < p->icons_count; j++)
+				os_free(p->icons[j]);
+			os_free(p->icons);
+			os_free(p->osu_nai);
+			os_free(p->service_desc);
+		}
+		os_free(conf->hs20_osu_providers);
+	}
+	os_free(conf->subscr_remediation_url);
 #endif /* CONFIG_HS20 */
 
 	wpabuf_free(conf->vendor_elements);
 
 	os_free(conf->sae_groups);
 
+	os_free(conf->wowlan_triggers);
+
 	os_free(conf->server_id);
+
+	os_free(conf);
 }
 
 
@@ -540,10 +561,11 @@
 		return;
 
 	for (i = 0; i < conf->num_bss; i++)
-		hostapd_config_free_bss(&conf->bss[i]);
+		hostapd_config_free_bss(conf->bss[i]);
 	os_free(conf->bss);
 	os_free(conf->supported_rates);
 	os_free(conf->basic_rates);
+	os_free(conf->chanlist);
 
 	os_free(conf);
 }
@@ -631,12 +653,11 @@
 	struct hostapd_wpa_psk *psk;
 	int next_ok = prev_psk == NULL;
 
-	if (p2p_dev_addr) {
+	if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
 		wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
 			   " p2p_dev_addr=" MACSTR " prev_psk=%p",
 			   MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk);
-		if (!is_zero_ether_addr(p2p_dev_addr))
-			addr = NULL; /* Use P2P Device Address for matching */
+		addr = NULL; /* Use P2P Device Address for matching */
 	} else {
 		wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
 			   " prev_psk=%p",
@@ -658,3 +679,238 @@
 
 	return NULL;
 }
+
+
+static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
+				    struct hostapd_config *conf,
+				    int full_config)
+{
+	if (full_config && bss->ieee802_1x && !bss->eap_server &&
+	    !bss->radius->auth_servers) {
+		wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
+			   "EAP authenticator configured).");
+		return -1;
+	}
+
+	if (bss->wpa) {
+		int wep, i;
+
+		wep = bss->default_wep_key_len > 0 ||
+		       bss->individual_wep_key_len > 0;
+		for (i = 0; i < NUM_WEP_KEYS; i++) {
+			if (bss->ssid.wep.keys_set) {
+				wep = 1;
+				break;
+			}
+		}
+
+		if (wep) {
+			wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
+			return -1;
+		}
+	}
+
+	if (full_config && bss->wpa &&
+	    bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+	    bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+		wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
+			   "RADIUS checking (macaddr_acl=2) enabled.");
+		return -1;
+	}
+
+	if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
+	    bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
+	    bss->ssid.wpa_psk_file == NULL &&
+	    (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
+	     bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
+		wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
+			   "is not configured.");
+		return -1;
+	}
+
+	if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) {
+		size_t i;
+
+		for (i = 0; i < conf->num_bss; i++) {
+			if (conf->bss[i] != bss &&
+			    (hostapd_mac_comp(conf->bss[i]->bssid,
+					      bss->bssid) == 0)) {
+				wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
+					   " on interface '%s' and '%s'.",
+					   MAC2STR(bss->bssid),
+					   conf->bss[i]->iface, bss->iface);
+				return -1;
+			}
+		}
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
+	    (bss->nas_identifier == NULL ||
+	     os_strlen(bss->nas_identifier) < 1 ||
+	     os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
+		wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
+			   "nas_identifier to be configured as a 1..48 octet "
+			   "string");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211N
+	if (full_config && conf->ieee80211n &&
+	    conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
+		bss->disable_11n = 1;
+		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
+			   "allowed, disabling HT capabilites");
+	}
+
+	if (full_config && conf->ieee80211n &&
+	    bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+		bss->disable_11n = 1;
+		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
+			   "allowed, disabling HT capabilities");
+	}
+
+	if (full_config && conf->ieee80211n && bss->wpa &&
+	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				   WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+	{
+		bss->disable_11n = 1;
+		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
+			   "requires CCMP/GCMP to be enabled, disabling HT "
+			   "capabilities");
+	}
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_WPS
+	if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
+		wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
+			   "configuration forced WPS to be disabled");
+		bss->wps_state = 0;
+	}
+
+	if (full_config && bss->wps_state &&
+	    bss->ssid.wep.keys_set && bss->wpa == 0) {
+		wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
+			   "disabled");
+		bss->wps_state = 0;
+	}
+
+	if (full_config && bss->wps_state && bss->wpa &&
+	    (!(bss->wpa & 2) ||
+	     !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
+		wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+			   "WPA2/CCMP forced WPS to be disabled");
+		bss->wps_state = 0;
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_HS20
+	if (full_config && bss->hs20 &&
+	    (!(bss->wpa & 2) ||
+	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				    WPA_CIPHER_CCMP_256 |
+				    WPA_CIPHER_GCMP_256)))) {
+		wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
+			   "configuration is required for Hotspot 2.0 "
+			   "functionality");
+		return -1;
+	}
+#endif /* CONFIG_HS20 */
+
+	return 0;
+}
+
+
+int hostapd_config_check(struct hostapd_config *conf, int full_config)
+{
+	size_t i;
+
+	if (full_config && conf->ieee80211d &&
+	    (!conf->country[0] || !conf->country[1])) {
+		wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
+			   "setting the country_code");
+		return -1;
+	}
+
+	if (full_config && conf->ieee80211h && !conf->ieee80211d) {
+		wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
+			   "IEEE 802.11d enabled");
+		return -1;
+	}
+
+	if (full_config && conf->local_pwr_constraint != -1 &&
+	    !conf->ieee80211d) {
+		wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element");
+		return -1;
+	}
+
+	if (full_config && conf->spectrum_mgmt_required &&
+	    conf->local_pwr_constraint == -1) {
+		wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements");
+		return -1;
+	}
+
+	for (i = 0; i < conf->num_bss; i++) {
+		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+				 int full_config)
+{
+	if (bss->individual_wep_key_len == 0) {
+		/* individual keys are not use; can use key idx0 for
+		 * broadcast keys */
+		bss->broadcast_key_idx_min = 0;
+	}
+
+	if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+		bss->rsn_pairwise = bss->wpa_pairwise;
+	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
+						    bss->rsn_pairwise);
+
+	if (full_config) {
+		bss->radius->auth_server = bss->radius->auth_servers;
+		bss->radius->acct_server = bss->radius->acct_servers;
+	}
+
+	if (bss->wpa && bss->ieee802_1x) {
+		bss->ssid.security_policy = SECURITY_WPA;
+	} else if (bss->wpa) {
+		bss->ssid.security_policy = SECURITY_WPA_PSK;
+	} else if (bss->ieee802_1x) {
+		int cipher = WPA_CIPHER_NONE;
+		bss->ssid.security_policy = SECURITY_IEEE_802_1X;
+		bss->ssid.wep.default_len = bss->default_wep_key_len;
+		if (bss->default_wep_key_len)
+			cipher = bss->default_wep_key_len >= 13 ?
+				WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+		bss->wpa_group = cipher;
+		bss->wpa_pairwise = cipher;
+		bss->rsn_pairwise = cipher;
+	} else if (bss->ssid.wep.keys_set) {
+		int cipher = WPA_CIPHER_WEP40;
+		if (bss->ssid.wep.len[0] >= 13)
+			cipher = WPA_CIPHER_WEP104;
+		bss->ssid.security_policy = SECURITY_STATIC_WEP;
+		bss->wpa_group = cipher;
+		bss->wpa_pairwise = cipher;
+		bss->rsn_pairwise = cipher;
+	} else if (bss->osen) {
+		bss->ssid.security_policy = SECURITY_OSEN;
+		bss->wpa_group = WPA_CIPHER_CCMP;
+		bss->wpa_pairwise = 0;
+		bss->rsn_pairwise = WPA_CIPHER_CCMP;
+	} else {
+		bss->ssid.security_policy = SECURITY_PLAINTEXT;
+		bss->wpa_group = WPA_CIPHER_NONE;
+		bss->wpa_pairwise = WPA_CIPHER_NONE;
+		bss->rsn_pairwise = WPA_CIPHER_NONE;
+	}
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c5531fa..2858c6e 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -45,7 +45,8 @@
 	SECURITY_STATIC_WEP = 1,
 	SECURITY_IEEE_802_1X = 2,
 	SECURITY_WPA_PSK = 3,
-	SECURITY_WPA = 4
+	SECURITY_WPA = 4,
+	SECURITY_OSEN = 5
 } secpolicy;
 
 struct hostapd_ssid {
@@ -53,6 +54,8 @@
 	size_t ssid_len;
 	unsigned int ssid_set:1;
 	unsigned int utf8_ssid:1;
+	unsigned int wpa_passphrase_set:1;
+	unsigned int wpa_psk_set:1;
 
 	char vlan[IFNAMSIZ + 1];
 	secpolicy security_policy;
@@ -123,7 +126,10 @@
 	unsigned int wildcard_prefix:1;
 	unsigned int password_hash:1; /* whether password is hashed with
 				       * nt_password_hash() */
+	unsigned int remediation:1;
+	unsigned int macacl:1;
 	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+	struct hostapd_radius_attr *accept_attr;
 };
 
 struct hostapd_radius_attr {
@@ -187,8 +193,6 @@
 	unsigned int logger_syslog; /* module bitfield */
 	unsigned int logger_stdout; /* module bitfield */
 
-	char *dump_log_name; /* file name for state dump (SIGUSR1) */
-
 	int max_num_sta; /* maximum number of STAs in station table */
 
 	int dtim_period;
@@ -251,6 +255,7 @@
 	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) */
@@ -311,6 +316,7 @@
 
 	char *radius_server_clients;
 	int radius_server_auth_port;
+	int radius_server_acct_port;
 	int radius_server_ipv6;
 
 	char *test_socket; /* UNIX domain socket path for driver_test */
@@ -384,6 +390,12 @@
 #define P2P_MANAGE BIT(3)
 #define P2P_ALLOW_CROSS_CONNECTION BIT(4)
 	int p2p;
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 
 	int disassoc_low_ack;
 	int skip_inactivity_poll;
@@ -442,9 +454,14 @@
 	u16 gas_comeback_delay;
 	int gas_frag_limit;
 
+	u8 qos_map_set[16 + 2 * 21];
+	unsigned int qos_map_set_len;
+
+	int osen;
 #ifdef CONFIG_HS20
 	int hs20;
 	int disable_dgaf;
+	u16 anqp_domain_id;
 	unsigned int hs20_oper_friendly_name_count;
 	struct hostapd_lang_string *hs20_oper_friendly_name;
 	u8 *hs20_wan_metrics;
@@ -452,6 +469,32 @@
 	size_t hs20_connection_capability_len;
 	u8 *hs20_operating_class;
 	u8 hs20_operating_class_len;
+	struct hs20_icon {
+		u16 width;
+		u16 height;
+		char language[3];
+		char type[256];
+		char name[256];
+		char file[256];
+	} *hs20_icons;
+	size_t hs20_icons_count;
+	u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
+	size_t osu_ssid_len;
+	struct hs20_osu_provider {
+		unsigned int friendly_name_count;
+		struct hostapd_lang_string *friendly_name;
+		char *server_uri;
+		int *method_list;
+		char **icons;
+		size_t icons_count;
+		char *osu_nai;
+		unsigned int service_desc_count;
+		struct hostapd_lang_string *service_desc;
+	} *hs20_osu_providers, *last_osu;
+	size_t hs20_osu_providers_count;
+	unsigned int hs20_deauth_req_timeout;
+	char *subscr_remediation_url;
+	u8 subscr_remediation_method;
 #endif /* CONFIG_HS20 */
 
 	u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
@@ -464,6 +507,13 @@
 
 	unsigned int sae_anti_clogging_threshold;
 	int *sae_groups;
+
+	char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 bss_load_test[5];
+	u8 bss_load_test_set;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -471,7 +521,7 @@
  * struct hostapd_config - Per-radio interface configuration
  */
 struct hostapd_config {
-	struct hostapd_bss_config *bss, *last_bss;
+	struct hostapd_bss_config **bss, *last_bss;
 	size_t num_bss;
 
 	u16 beacon_int;
@@ -479,6 +529,7 @@
 	int fragm_threshold;
 	u8 send_probe_response;
 	u8 channel;
+	int *chanlist;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
 	enum {
 		LONG_PREAMBLE = 0,
@@ -504,6 +555,16 @@
 
 	int ieee80211h; /* DFS */
 
+	/*
+	 * Local power constraint is an octet encoded as an unsigned integer in
+	 * units of decibels. Invalid value -1 indicates that Power Constraint
+	 * element will not be added.
+	 */
+	int local_pwr_constraint;
+
+	/* Control Spectrum Management bit */
+	int spectrum_mgmt_required;
+
 	struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
 
 	/*
@@ -520,6 +581,7 @@
 	int ieee80211n;
 	int secondary_channel;
 	int require_ht;
+	int obss_interval;
 	u32 vht_capab;
 	int ieee80211ac;
 	int require_vht;
@@ -545,12 +607,12 @@
 int hostapd_mac_comp_empty(const void *a);
 struct hostapd_config * hostapd_config_defaults(void);
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_free_bss(struct hostapd_bss_config *conf);
 void hostapd_config_free(struct hostapd_config *conf);
 int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
 			  const u8 *addr, int *vlan_id);
 int hostapd_rate_found(int *list, int rate);
-int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
-			struct hostapd_wep_keys *b);
 const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
 			   const u8 *addr, const u8 *p2p_dev_addr,
 			   const u8 *prev_psk);
@@ -560,5 +622,8 @@
 					int vlan_id);
 struct hostapd_radius_attr *
 hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
+int hostapd_config_check(struct hostapd_config *conf, int full_config);
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+				 int full_config);
 
 #endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 3072562..cc4ac10 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -9,7 +9,6 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
-#include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "wps/wps.h"
 #include "p2p/p2p.h"
@@ -129,14 +128,14 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-#ifdef CONFIG_WPS2
+#ifdef CONFIG_WPS
 	if (hapd->conf->wps_state) {
 		struct wpabuf *a = wps_build_assoc_resp_ie();
 		if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
 			wpabuf_put_buf(assocresp, a);
 		wpabuf_free(a);
 	}
-#endif /* CONFIG_WPS2 */
+#endif /* CONFIG_WPS */
 
 #ifdef CONFIG_P2P_MANAGER
 	if (hapd->conf->p2p & P2P_MANAGE) {
@@ -171,6 +170,17 @@
 			goto fail;
 		wpabuf_put_data(proberesp, buf, pos - buf);
 	}
+
+	pos = hostapd_eid_osen(hapd, buf);
+	if (pos != buf) {
+		if (wpabuf_resize(&beacon, pos - buf) != 0)
+			goto fail;
+		wpabuf_put_data(beacon, buf, pos - buf);
+
+		if (wpabuf_resize(&proberesp, pos - buf) != 0)
+			goto fail;
+		wpabuf_put_data(proberesp, buf, pos - buf);
+	}
 #endif /* CONFIG_HS20 */
 
 	if (hapd->conf->vendor_elements) {
@@ -270,7 +280,8 @@
 		params.wpa = hapd->conf->wpa;
 		params.ieee802_1x = hapd->conf->ieee802_1x;
 		params.wpa_group = hapd->conf->wpa_group;
-		params.wpa_pairwise = hapd->conf->wpa_pairwise;
+		params.wpa_pairwise = hapd->conf->wpa_pairwise |
+			hapd->conf->rsn_pairwise;
 		params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
 		params.rsn_preauth = hapd->conf->rsn_preauth;
 #ifdef CONFIG_IEEE80211W
@@ -286,7 +297,7 @@
 	char force_ifname[IFNAMSIZ];
 	u8 if_addr[ETH_ALEN];
 	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
-			      NULL, NULL, force_ifname, if_addr, NULL);
+			      NULL, NULL, force_ifname, if_addr, NULL, 0);
 }
 
 
@@ -347,7 +358,7 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo)
+		    u32 flags, u8 qosinfo, u8 vht_opmode)
 {
 	struct hostapd_sta_add_params params;
 
@@ -365,6 +376,8 @@
 	params.listen_interval = listen_interval;
 	params.ht_capabilities = ht_capab;
 	params.vht_capabilities = vht_capab;
+	params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
+	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
 	params.qosinfo = qosinfo;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
@@ -417,20 +430,21 @@
 int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
 		   const char *ifname, const u8 *addr, void *bss_ctx,
 		   void **drv_priv, char *force_ifname, u8 *if_addr,
-		   const char *bridge)
+		   const char *bridge, int use_existing)
 {
 	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
 		return -1;
 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
 				    bss_ctx, drv_priv, force_ifname, if_addr,
-				    bridge);
+				    bridge, use_existing);
 }
 
 
 int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
 		      const char *ifname)
 {
-	if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->if_remove == NULL)
 		return -1;
 	return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
 }
@@ -463,44 +477,51 @@
 }
 
 
-int hostapd_set_freq(struct hostapd_data *hapd, int 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)
+int hostapd_set_freq_params(struct hostapd_freq_params *data, int 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)
 {
-	struct hostapd_freq_params data;
 	int tmp;
 
-	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.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;
+	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->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;
 
 	/*
 	 * This validation code is probably misplaced, maybe it should be
 	 * in src/ap/hw_features.c and check the hardware support as well.
 	 */
-	if (data.vht_enabled) switch (vht_oper_chwidth) {
+	if (data->vht_enabled) switch (vht_oper_chwidth) {
 	case VHT_CHANWIDTH_USE_HT:
 		if (center_segment1)
 			return -1;
-		if (5000 + center_segment0 * 5 != data.center_freq1)
+		if (center_segment0 != 0 &&
+		    5000 + center_segment0 * 5 != data->center_freq1 &&
+		    2407 + center_segment0 * 5 != data->center_freq1)
 			return -1;
 		break;
 	case VHT_CHANWIDTH_80P80MHZ:
+		if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
+			wpa_printf(MSG_ERROR,
+				   "80+80 channel width is not supported!");
+			return -1;
+		}
 		if (center_segment1 == center_segment0 + 4 ||
 		    center_segment1 == center_segment0 - 4)
 			return -1;
-		data.center_freq2 = 5000 + center_segment1 * 5;
+		data->center_freq2 = 5000 + center_segment1 * 5;
 		/* fall through */
 	case VHT_CHANWIDTH_80MHZ:
-		data.bandwidth = 80;
+		data->bandwidth = 80;
 		if (vht_oper_chwidth == 1 && center_segment1)
 			return -1;
 		if (vht_oper_chwidth == 3 && !center_segment1)
@@ -510,13 +531,19 @@
 		/* primary 40 part must match the HT configuration */
 		tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
 		tmp /= 2;
-		if (data.center_freq1 != 5000 +
+		if (data->center_freq1 != 5000 +
 					 center_segment0 * 5 - 20 + 40 * tmp)
 			return -1;
-		data.center_freq1 = 5000 + center_segment0 * 5;
+		data->center_freq1 = 5000 + center_segment0 * 5;
 		break;
 	case VHT_CHANWIDTH_160MHZ:
-		data.bandwidth = 160;
+		data->bandwidth = 160;
+		if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+				  VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
+			wpa_printf(MSG_ERROR,
+				   "160MHZ channel width is not supported!");
+			return -1;
+		}
 		if (center_segment1)
 			return -1;
 		if (!sec_channel_offset)
@@ -524,12 +551,31 @@
 		/* primary 40 part must match the HT configuration */
 		tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
 		tmp /= 2;
-		if (data.center_freq1 != 5000 +
+		if (data->center_freq1 != 5000 +
 					 center_segment0 * 5 - 60 + 40 * tmp)
 			return -1;
-		data.center_freq1 = 5000 + center_segment0 * 5;
+		data->center_freq1 = 5000 + center_segment0 * 5;
 		break;
 	}
+
+	return 0;
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, int 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)
+{
+	struct hostapd_freq_params data;
+
+	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+				    vht_enabled, sec_channel_offset,
+				    vht_oper_chwidth,
+				    center_segment0, center_segment1,
+				    hapd->iface->current_mode->vht_capab))
+		return -1;
+
 	if (hapd->driver == NULL)
 		return 0;
 	if (hapd->driver->set_freq == NULL)
@@ -683,7 +729,7 @@
 			 const u8 *peer, u8 *buf, u16 *buf_len)
 {
 	if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
-		return 0;
+		return -1;
 	return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
 				      buf_len);
 }
@@ -699,3 +745,50 @@
 					 hapd->own_addr, hapd->own_addr, data,
 					 len, 0);
 }
+
+
+int hostapd_start_dfs_cac(struct hostapd_iface *iface, int 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)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	struct hostapd_freq_params data;
+	int res;
+
+	if (!hapd->driver || !hapd->driver->start_dfs_cac)
+		return 0;
+
+	if (!iface->conf->ieee80211h) {
+		wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+			   "is not enabled");
+		return -1;
+	}
+
+	if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+				    vht_enabled, sec_channel_offset,
+				    vht_oper_chwidth, center_segment0,
+				    center_segment1,
+				    iface->current_mode->vht_capab)) {
+		wpa_printf(MSG_ERROR, "Can't set freq params");
+		return -1;
+	}
+
+	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+	if (!res) {
+		iface->cac_started = 1;
+		os_get_reltime(&iface->dfs_cac_start);
+	}
+
+	return res;
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+			    const u8 *qos_map_set, u8 qos_map_set_len)
+{
+	if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
+		return 0;
+	return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+					 qos_map_set_len);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 23277b6..7cc9d7d 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - Driver operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,7 @@
 struct wpa_driver_scan_params;
 struct ieee80211_ht_capabilities;
 struct ieee80211_vht_capabilities;
+struct hostapd_freq_params;
 
 u32 hostapd_sta_flags_to_drv(u32 flags);
 int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
@@ -39,7 +40,7 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo);
+		    u32 flags, u8 qosinfo, u8 vht_opmode);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
 			     size_t elem_len);
@@ -48,7 +49,7 @@
 int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
 		   const char *ifname, const u8 *addr, void *bss_ctx,
 		   void **drv_priv, char *force_ifname, u8 *if_addr,
-		   const char *bridge);
+		   const char *bridge, int use_existing);
 int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
 		      const char *ifname);
 int hostapd_set_ieee8021x(struct hostapd_data *hapd,
@@ -101,6 +102,15 @@
 		      int reassoc, u16 status, const u8 *ie, size_t len);
 int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
 		      u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_iface *iface, int 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);
+int hostapd_set_freq_params(struct hostapd_freq_params *data, int 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);
 
 
 #include "drivers/driver.h"
@@ -109,6 +119,9 @@
 			 enum wnm_oper oper, const u8 *peer,
 			 u8 *buf, u16 *buf_len);
 
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+			    u8 qos_map_set_len);
+
 static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
 						  int enabled)
 {
@@ -235,4 +248,47 @@
 	return hapd->driver->get_survey(hapd->drv_priv, freq);
 }
 
+static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+{
+	if (hapd->driver == NULL || hapd->driver->get_country == NULL)
+		return -1;
+	return hapd->driver->get_country(hapd->drv_priv, alpha2);
+}
+
+static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->get_radio_name == NULL)
+		return NULL;
+	return hapd->driver->get_radio_name(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
+					     struct csa_settings *settings)
+{
+	if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
+		return -ENOTSUP;
+
+	return hapd->driver->switch_channel(hapd->drv_priv, settings);
+}
+
+static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
+				     size_t buflen)
+{
+	if (hapd->driver == NULL || hapd->driver->status == NULL)
+		return -1;
+	return hapd->driver->status(hapd->drv_priv, buf, buflen);
+}
+
+static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+					 int vendor_id, int subcmd,
+					 const u8 *data, size_t data_len,
+					 struct wpabuf *buf)
+{
+	if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
+		return -1;
+	return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
+					data_len, buf);
+}
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 9f02151..287d520 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -14,7 +14,6 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ieee802_11.h"
@@ -33,7 +32,8 @@
 {
 	int i;
 
-	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+	if (iface->current_mode == NULL ||
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
 	    iface->conf->channel != ap->channel)
 		return 0;
 
@@ -172,7 +172,6 @@
 			    struct hostapd_frame_info *fi)
 {
 	struct ap_info *ap;
-	struct os_time now;
 	int new_ap = 0;
 	int set_beacon = 0;
 
@@ -210,8 +209,7 @@
 	else
 		ap->ht_support = 0;
 
-	os_get_time(&now);
-	ap->last_beacon = now.sec;
+	os_get_reltime(&ap->last_beacon);
 
 	if (!new_ap && ap != iface->ap_list) {
 		/* move AP entry into the beginning of the list so that the
@@ -252,7 +250,7 @@
 static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
 {
 	struct hostapd_iface *iface = eloop_ctx;
-	struct os_time now;
+	struct os_reltime now;
 	struct ap_info *ap;
 	int set_beacon = 0;
 
@@ -261,12 +259,12 @@
 	if (!iface->ap_list)
 		return;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 
 	while (iface->ap_list) {
 		ap = iface->ap_list->prev;
-		if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
-		    now.sec)
+		if (!os_reltime_expired(&now, &ap->last_beacon,
+					iface->conf->ap_table_expiration_time))
 			break;
 
 		ap_free_ap(iface, ap);
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index d0529a1..93dc0ed 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -26,7 +26,7 @@
 
 	int ht_support;
 
-	os_time_t last_beacon;
+	struct os_reltime last_beacon;
 };
 
 struct ieee802_11_elems;
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index a959694..13604ed 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -16,6 +16,7 @@
 #include "wpa_auth.h"
 #include "sta_info.h"
 #include "ap_mlme.h"
+#include "hostapd.h"
 
 
 #ifndef CONFIG_NO_HOSTAPD_LOGGER
@@ -80,7 +81,8 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
 		       MAC2STR(sta->addr), reason_code);
-	mlme_deletekeys_request(hapd, sta);
+	if (!hapd->iface->driver_ap_teardown)
+		mlme_deletekeys_request(hapd, sta);
 }
 
 
@@ -118,8 +120,6 @@
  * reassociation procedure that was initiated by that specific peer MAC entity.
  *
  * PeerSTAAddress = sta->addr
- *
- * sta->previous_ap contains the "Current AP" information from ReassocReq.
  */
 void mlme_reassociate_indication(struct hostapd_data *hapd,
 				 struct sta_info *sta)
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 68ad4dc..86f1cbe 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -79,7 +79,10 @@
 		user->password_hash = eap_user->password_hash;
 	}
 	user->force_version = eap_user->force_version;
+	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
+	user->remediation = eap_user->remediation;
+	user->accept_attr = eap_user->accept_attr;
 
 	return 0;
 }
@@ -92,6 +95,7 @@
 	os_memset(&srv, 0, sizeof(srv));
 	srv.client_file = conf->radius_server_clients;
 	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;
@@ -112,9 +116,14 @@
 	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;
 #endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+	srv.subscr_remediation_url = conf->subscr_remediation_url;
+	srv.subscr_remediation_method = conf->subscr_remediation_method;
+#endif /* CONFIG_HS20 */
 
 	hapd->radius_srv = radius_server_init(&srv);
 	if (hapd->radius_srv == NULL) {
@@ -133,7 +142,7 @@
 #ifdef EAP_TLS_FUNCS
 	if (hapd->conf->eap_server &&
 	    (hapd->conf->ca_cert || hapd->conf->server_cert ||
-	     hapd->conf->dh_file)) {
+	     hapd->conf->private_key || hapd->conf->dh_file)) {
 		struct tls_connection_params params;
 
 		hapd->ssl_ctx = tls_init(NULL);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 2f4ba23..b3b6149 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -4,14 +4,8 @@
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -21,7 +15,6 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
 #include "wps/wps_defs.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
@@ -34,10 +27,27 @@
 #include "ap_drv_ops.h"
 #include "beacon.h"
 #include "hs20.h"
+#include "dfs.h"
 
 
 #ifdef NEED_AP_MLME
 
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->bss_load_test_set) {
+		if (2 + 5 > len)
+			return eid;
+		*eid++ = WLAN_EID_BSS_LOAD;
+		*eid++ = 5;
+		os_memcpy(eid, hapd->conf->bss_load_test, 5);
+		eid += 5;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	return eid;
+}
+
+
 static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
 {
 	u8 erp = 0;
@@ -93,6 +103,70 @@
 }
 
 
+static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 local_pwr_constraint = 0;
+	int dfs;
+
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	/*
+	 * There is no DFS support and power constraint was not directly
+	 * requested by config option.
+	 */
+	if (!hapd->iconf->ieee80211h &&
+	    hapd->iconf->local_pwr_constraint == -1)
+		return eid;
+
+	/* Check if DFS is required by regulatory. */
+	dfs = hostapd_is_dfs_required(hapd->iface);
+	if (dfs < 0) {
+		wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+			   dfs);
+		dfs = 0;
+	}
+
+	if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
+		return eid;
+
+	/*
+	 * ieee80211h (DFS) is enabled so Power Constraint element shall
+	 * be added when running on DFS channel whenever local_pwr_constraint
+	 * is configured or not. In order to meet regulations when TPC is not
+	 * implemented using a transmit power that is below the legal maximum
+	 * (including any mitigation factor) should help. In this case,
+	 * indicate 3 dB below maximum allowed transmit power.
+	 */
+	if (hapd->iconf->local_pwr_constraint == -1)
+		local_pwr_constraint = 3;
+
+	/*
+	 * A STA that is not an AP shall use a transmit power less than or
+	 * equal to the local maximum transmit power level for the channel.
+	 * The local maximum transmit power can be calculated from the formula:
+	 * local max TX pwr = max TX pwr - local pwr constraint
+	 * Where max TX pwr is maximum transmit power level specified for
+	 * channel in Country element and local pwr constraint is specified
+	 * for channel in this Power Constraint element.
+	 */
+
+	/* Element ID */
+	*pos++ = WLAN_EID_PWR_CONSTRAINT;
+	/* Length */
+	*pos++ = 1;
+	/* Local Power Constraint */
+	if (local_pwr_constraint)
+		*pos++ = local_pwr_constraint;
+	else
+		*pos++ = hapd->iconf->local_pwr_constraint;
+
+	return pos;
+}
+
+
 static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
 				    struct hostapd_channel_data *start,
 				    struct hostapd_channel_data *prev)
@@ -146,7 +220,7 @@
 			continue; /* can use same entry */
 		}
 
-		if (start) {
+		if (start && prev) {
 			pos = hostapd_eid_country_add(pos, end, chan_spacing,
 						      start, prev);
 			start = NULL;
@@ -187,6 +261,70 @@
 }
 
 
+static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 chan;
+
+	if (!hapd->cs_freq_params.freq)
+		return eid;
+
+	if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
+	    NUM_HOSTAPD_MODES)
+		return eid;
+
+	*eid++ = WLAN_EID_CHANNEL_SWITCH;
+	*eid++ = 3;
+	*eid++ = hapd->cs_block_tx;
+	*eid++ = chan;
+	*eid++ = hapd->cs_count;
+
+	return eid;
+}
+
+
+static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 sec_ch;
+
+	if (!hapd->cs_freq_params.sec_channel_offset)
+		return eid;
+
+	if (hapd->cs_freq_params.sec_channel_offset == -1)
+		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
+	else if (hapd->cs_freq_params.sec_channel_offset == 1)
+		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
+	else
+		return eid;
+
+	*eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+	*eid++ = 1;
+	*eid++ = sec_ch;
+
+	return eid;
+}
+
+
+static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos,
+				  u8 *start, unsigned int *csa_counter_off)
+{
+	u8 *old_pos = pos;
+
+	if (!csa_counter_off)
+		return pos;
+
+	*csa_counter_off = 0;
+	pos = hostapd_eid_csa(hapd, pos);
+
+	if (pos != old_pos) {
+		/* save an offset to the counter - should be last byte */
+		*csa_counter_off = pos - start - 1;
+		pos = hostapd_eid_secondary_channel(hapd, pos);
+	}
+
+	return pos;
+}
+
+
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   struct sta_info *sta,
 				   const struct ieee80211_mgmt *req,
@@ -242,6 +380,9 @@
 
 	pos = hostapd_eid_country(hapd, pos, epos - pos);
 
+	/* Power Constraint element */
+	pos = hostapd_eid_pwr_constraint(hapd, pos);
+
 	/* ERP Information element */
 	pos = hostapd_eid_erp_info(hapd, pos);
 
@@ -251,6 +392,8 @@
 	/* RSN, MDIE, WPA */
 	pos = hostapd_eid_wpa(hapd, pos, epos - pos);
 
+	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+
 #ifdef CONFIG_IEEE80211N
 	pos = hostapd_eid_ht_capabilities(hapd, pos);
 	pos = hostapd_eid_ht_operation(hapd, pos);
@@ -265,6 +408,8 @@
 	pos = hostapd_eid_adv_proto(hapd, pos);
 	pos = hostapd_eid_roaming_consortium(hapd, pos);
 
+	pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
+				    &hapd->cs_c_off_proberesp);
 #ifdef CONFIG_IEEE80211AC
 	pos = hostapd_eid_vht_capabilities(hapd, pos);
 	pos = hostapd_eid_vht_operation(hapd, pos);
@@ -297,6 +442,7 @@
 
 #ifdef CONFIG_HS20
 	pos = hostapd_eid_hs20_indication(hapd, pos);
+	pos = hostapd_eid_osen(hapd, pos);
 #endif /* CONFIG_HS20 */
 
 	if (hapd->conf->vendor_elements) {
@@ -443,12 +589,10 @@
 			sta->ssid_probe = &hapd->conf->ssid;
 	} else {
 		if (!(mgmt->da[0] & 0x01)) {
-			char ssid_txt[33];
-			ieee802_11_print_ssid(ssid_txt, elems.ssid,
-					      elems.ssid_len);
 			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
 				   " for foreign SSID '%s' (DA " MACSTR ")%s",
-				   MAC2STR(mgmt->sa), ssid_txt,
+				   MAC2STR(mgmt->sa),
+				   wpa_ssid_txt(elems.ssid, elems.ssid_len),
 				   MAC2STR(mgmt->da),
 				   elems.ssid_list ? " (SSID list)" : "");
 		}
@@ -456,7 +600,8 @@
 	}
 
 #ifdef CONFIG_INTERWORKING
-	if (elems.interworking && elems.interworking_len >= 1) {
+	if (hapd->conf->interworking &&
+	    elems.interworking && elems.interworking_len >= 1) {
 		u8 ant = elems.interworking[0] & 0x0f;
 		if (ant != INTERWORKING_ANT_WILDCARD &&
 		    ant != hapd->conf->access_network_type) {
@@ -467,7 +612,7 @@
 		}
 	}
 
-	if (elems.interworking &&
+	if (hapd->conf->interworking && elems.interworking &&
 	    (elems.interworking_len == 7 || elems.interworking_len == 9)) {
 		const u8 *hessid;
 		if (elems.interworking_len == 7)
@@ -500,7 +645,7 @@
 	 * with AP configuration */
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (hapd->iconf->ignore_probe_probability > 0.0d &&
+	if (hapd->iconf->ignore_probe_probability > 0.0 &&
 	    drand48() < hapd->iconf->ignore_probe_probability) {
 		wpa_printf(MSG_INFO,
 			   "TESTING: ignoring probe request from " MACSTR,
@@ -522,7 +667,7 @@
 		   is_broadcast_ether_addr(mgmt->da));
 
 	if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
-		perror("handle_probe_req: send");
+		wpa_printf(MSG_INFO, "handle_probe_req: send failed");
 
 	os_free(resp);
 
@@ -570,23 +715,17 @@
 #endif /* NEED_AP_MLME */
 
 
-void ieee802_11_set_beacon(struct hostapd_data *hapd)
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+			       struct wpa_driver_ap_params *params)
 {
 	struct ieee80211_mgmt *head = NULL;
 	u8 *tail = NULL;
 	size_t head_len = 0, tail_len = 0;
 	u8 *resp = NULL;
 	size_t resp_len = 0;
-	struct wpa_driver_ap_params params;
-	struct wpabuf *beacon, *proberesp, *assocresp;
 #ifdef NEED_AP_MLME
 	u16 capab_info;
 	u8 *pos, *tailpos;
-#endif /* NEED_AP_MLME */
-
-	hapd->beacon_set_done = 1;
-
-#ifdef NEED_AP_MLME
 
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
@@ -607,7 +746,7 @@
 		wpa_printf(MSG_ERROR, "Failed to set beacon data");
 		os_free(head);
 		os_free(tail);
-		return;
+		return -1;
 	}
 
 	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -652,6 +791,9 @@
 	tailpos = hostapd_eid_country(hapd, tailpos,
 				      tail + BEACON_TAIL_BUF_SIZE - tailpos);
 
+	/* Power Constraint element */
+	tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+
 	/* ERP Information element */
 	tailpos = hostapd_eid_erp_info(hapd, tailpos);
 
@@ -662,6 +804,9 @@
 	tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
 				  tailpos);
 
+	tailpos = hostapd_eid_bss_load(hapd, tailpos,
+				       tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
 #ifdef CONFIG_IEEE80211N
 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
@@ -678,7 +823,8 @@
 	tailpos = hostapd_eid_interworking(hapd, tailpos);
 	tailpos = hostapd_eid_adv_proto(hapd, tailpos);
 	tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
-
+	tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
+					&hapd->cs_c_off_beacon);
 #ifdef CONFIG_IEEE80211AC
 	tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_vht_operation(hapd, tailpos);
@@ -710,6 +856,7 @@
 
 #ifdef CONFIG_HS20
 	tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+	tailpos = hostapd_eid_osen(hapd, tailpos);
 #endif /* CONFIG_HS20 */
 
 	if (hapd->conf->vendor_elements) {
@@ -723,94 +870,162 @@
 	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
 #endif /* NEED_AP_MLME */
 
-	os_memset(&params, 0, sizeof(params));
-	params.head = (u8 *) head;
-	params.head_len = head_len;
-	params.tail = tail;
-	params.tail_len = tail_len;
-	params.proberesp = resp;
-	params.proberesp_len = resp_len;
-	params.dtim_period = hapd->conf->dtim_period;
-	params.beacon_int = hapd->iconf->beacon_int;
-	params.basic_rates = hapd->iface->basic_rates;
-	params.ssid = hapd->conf->ssid.ssid;
-	params.ssid_len = hapd->conf->ssid.ssid_len;
-	params.pairwise_ciphers = hapd->conf->rsn_pairwise ?
-		hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise;
-	params.group_cipher = hapd->conf->wpa_group;
-	params.key_mgmt_suites = hapd->conf->wpa_key_mgmt;
-	params.auth_algs = hapd->conf->auth_algs;
-	params.wpa_version = hapd->conf->wpa;
-	params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
+	os_memset(params, 0, sizeof(*params));
+	params->head = (u8 *) head;
+	params->head_len = head_len;
+	params->tail = tail;
+	params->tail_len = tail_len;
+	params->proberesp = resp;
+	params->proberesp_len = resp_len;
+	params->dtim_period = hapd->conf->dtim_period;
+	params->beacon_int = hapd->iconf->beacon_int;
+	params->basic_rates = hapd->iface->basic_rates;
+	params->ssid = hapd->conf->ssid.ssid;
+	params->ssid_len = hapd->conf->ssid.ssid_len;
+	params->pairwise_ciphers = hapd->conf->wpa_pairwise |
+		hapd->conf->rsn_pairwise;
+	params->group_cipher = hapd->conf->wpa_group;
+	params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+	params->auth_algs = hapd->conf->auth_algs;
+	params->wpa_version = hapd->conf->wpa;
+	params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
 		(hapd->conf->ieee802_1x &&
 		 (hapd->conf->default_wep_key_len ||
 		  hapd->conf->individual_wep_key_len));
 	switch (hapd->conf->ignore_broadcast_ssid) {
 	case 0:
-		params.hide_ssid = NO_SSID_HIDING;
+		params->hide_ssid = NO_SSID_HIDING;
 		break;
 	case 1:
-		params.hide_ssid = HIDDEN_SSID_ZERO_LEN;
+		params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
 		break;
 	case 2:
-		params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+		params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
 		break;
 	}
-	hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp);
-	params.beacon_ies = beacon;
-	params.proberesp_ies = proberesp;
-	params.assocresp_ies = assocresp;
-	params.isolate = hapd->conf->isolate;
+	params->isolate = hapd->conf->isolate;
 #ifdef NEED_AP_MLME
-	params.cts_protect = !!(ieee802_11_erp_info(hapd) &
+	params->cts_protect = !!(ieee802_11_erp_info(hapd) &
 				ERP_INFO_USE_PROTECTION);
-	params.preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+	params->preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
 		hapd->iconf->preamble == SHORT_PREAMBLE;
 	if (hapd->iface->current_mode &&
 	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
-		params.short_slot_time =
+		params->short_slot_time =
 			hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
 	else
-		params.short_slot_time = -1;
+		params->short_slot_time = -1;
 	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
-		params.ht_opmode = -1;
+		params->ht_opmode = -1;
 	else
-		params.ht_opmode = hapd->iface->ht_op_mode;
+		params->ht_opmode = hapd->iface->ht_op_mode;
 #endif /* NEED_AP_MLME */
-	params.interworking = hapd->conf->interworking;
+	params->interworking = hapd->conf->interworking;
 	if (hapd->conf->interworking &&
 	    !is_zero_ether_addr(hapd->conf->hessid))
-		params.hessid = hapd->conf->hessid;
-	params.access_network_type = hapd->conf->access_network_type;
-	params.ap_max_inactivity = hapd->conf->ap_max_inactivity;
+		params->hessid = hapd->conf->hessid;
+	params->access_network_type = hapd->conf->access_network_type;
+	params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
 #ifdef CONFIG_HS20
-	params.disable_dgaf = hapd->conf->disable_dgaf;
+	params->disable_dgaf = hapd->conf->disable_dgaf;
+	if (hapd->conf->osen) {
+		params->privacy = 1;
+		params->osen = 1;
+	}
 #endif /* CONFIG_HS20 */
-	if (hostapd_drv_set_ap(hapd, &params))
-		wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
-	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
-
-	os_free(tail);
-	os_free(head);
-	os_free(resp);
+	return 0;
 }
 
 
-void ieee802_11_set_beacons(struct hostapd_iface *iface)
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
+{
+	os_free(params->tail);
+	params->tail = NULL;
+	os_free(params->head);
+	params->head = NULL;
+	os_free(params->proberesp);
+	params->proberesp = NULL;
+}
+
+
+int ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+	struct wpa_driver_ap_params params;
+	struct hostapd_freq_params freq;
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_config *iconf = iface->conf;
+	struct wpabuf *beacon, *proberesp, *assocresp;
+	int res, ret = -1;
+
+	if (hapd->csa_in_progress) {
+		wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
+		return -1;
+	}
+
+	hapd->beacon_set_done = 1;
+
+	if (ieee802_11_build_ap_params(hapd, &params) < 0)
+		return -1;
+
+	if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+	    0)
+		goto fail;
+
+	params.beacon_ies = beacon;
+	params.proberesp_ies = proberesp;
+	params.assocresp_ies = assocresp;
+
+	if (iface->current_mode &&
+	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
+				    iconf->channel, iconf->ieee80211n,
+				    iconf->ieee80211ac,
+				    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)
+		params.freq = &freq;
+
+	res = hostapd_drv_set_ap(hapd, &params);
+	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+	if (res)
+		wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+	else
+		ret = 0;
+fail:
+	ieee802_11_free_ap_params(&params);
+	return ret;
+}
+
+
+int ieee802_11_set_beacons(struct hostapd_iface *iface)
 {
 	size_t i;
-	for (i = 0; i < iface->num_bss; i++)
-		ieee802_11_set_beacon(iface->bss[i]);
+	int ret = 0;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		if (iface->bss[i]->started &&
+		    ieee802_11_set_beacon(iface->bss[i]) < 0)
+			ret = -1;
+	}
+
+	return ret;
 }
 
 
 /* only update beacons if started */
-void ieee802_11_update_beacons(struct hostapd_iface *iface)
+int ieee802_11_update_beacons(struct hostapd_iface *iface)
 {
 	size_t i;
-	for (i = 0; i < iface->num_bss; i++)
-		if (iface->bss[i]->beacon_set_done)
-			ieee802_11_set_beacon(iface->bss[i]);
+	int ret = 0;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		if (iface->bss[i]->beacon_set_done && iface->bss[i]->started &&
+		    ieee802_11_set_beacon(iface->bss[i]) < 0)
+			ret = -1;
+	}
+
+	return ret;
 }
 
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index 37f10d2..722159a 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -3,14 +3,8 @@
  * Copyright (c) 2002-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef BEACON_H
@@ -21,8 +15,11 @@
 void handle_probe_req(struct hostapd_data *hapd,
 		      const struct ieee80211_mgmt *mgmt, size_t len,
 		      int ssi_signal);
-void ieee802_11_set_beacon(struct hostapd_data *hapd);
-void ieee802_11_set_beacons(struct hostapd_iface *iface);
-void ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_set_beacon(struct hostapd_data *hapd);
+int ieee802_11_set_beacons(struct hostapd_iface *iface);
+int ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+			       struct wpa_driver_ap_params *params);
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
 
 #endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 1cb7e73..39edbd7 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1,6 +1,6 @@
 /*
  * Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
@@ -21,25 +22,61 @@
 #include "ap_drv_ops.h"
 
 
+static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 char *buf, size_t buflen)
+{
+	struct hostap_sta_driver_data data;
+	int ret;
+
+	if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+		return 0;
+
+	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
+			  "rx_bytes=%lu\ntx_bytes=%lu\n",
+			  data.rx_packets, data.tx_packets,
+			  data.rx_bytes, data.tx_bytes);
+	if (ret < 0 || (size_t) ret >= buflen)
+		return 0;
+	return ret;
+}
+
+
 static int hostapd_get_sta_conn_time(struct sta_info *sta,
 				     char *buf, size_t buflen)
 {
-	struct os_time now, age;
-	int len = 0, ret;
+	struct os_reltime age;
+	int ret;
 
 	if (!sta->connected_time.sec)
 		return 0;
 
-	os_get_time(&now);
-	os_time_sub(&now, &sta->connected_time, &age);
+	os_reltime_age(&sta->connected_time, &age);
 
-	ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n",
+	ret = os_snprintf(buf, buflen, "connected_time=%u\n",
 			  (unsigned int) age.sec);
-	if (ret < 0 || (size_t) ret >= buflen - len)
-		return len;
-	len += ret;
+	if (ret < 0 || (size_t) ret >= buflen)
+		return 0;
+	return ret;
+}
 
-	return len;
+
+static const char * timeout_next_str(int val)
+{
+	switch (val) {
+	case STA_NULLFUNC:
+		return "NULLFUNC POLL";
+	case STA_DISASSOC:
+		return "DISASSOC";
+	case STA_DEAUTH:
+		return "DEAUTH";
+	case STA_REMOVE:
+		return "REMOVE";
+	case STA_DISASSOC_FROM_CLI:
+		return "DISASSOC_FROM_CLI";
+	}
+
+	return "?";
 }
 
 
@@ -47,22 +84,45 @@
 				      struct sta_info *sta,
 				      char *buf, size_t buflen)
 {
-	int len, res, ret;
+	int len, res, ret, i;
 
-	if (sta == NULL) {
-		ret = os_snprintf(buf, buflen, "FAIL\n");
-		if (ret < 0 || (size_t) ret >= buflen)
-			return 0;
-		return ret;
-	}
+	if (!sta)
+		return 0;
 
 	len = 0;
-	ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
+	ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
 			  MAC2STR(sta->addr));
 	if (ret < 0 || (size_t) ret >= buflen - len)
 		return len;
 	len += ret;
 
+	ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
+	if (ret < 0)
+		return len;
+	len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
+			  "listen_interval=%d\nsupported_rates=",
+			  sta->aid, sta->capability, sta->listen_interval);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	for (i = 0; i < sta->supported_rates_len; i++) {
+		ret = os_snprintf(buf + len, buflen - len, "%02x%s",
+				  sta->supported_rates[i],
+				  i + 1 < sta->supported_rates_len ? " " : "");
+		if (ret < 0 || (size_t) ret >= buflen - len)
+			return len;
+		len += ret;
+	}
+
+	ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
+			  timeout_next_str(sta->timeout_next));
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
 	res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
 	if (res >= 0)
 		len += res;
@@ -80,9 +140,8 @@
 	if (res >= 0)
 		len += res;
 
-	res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
-	if (res >= 0)
-		len += res;
+	len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
+	len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
 
 	return len;
 }
@@ -100,6 +159,8 @@
 {
 	u8 addr[ETH_ALEN];
 	int ret;
+	const char *pos;
+	struct sta_info *sta;
 
 	if (hwaddr_aton(txtaddr, addr)) {
 		ret = os_snprintf(buf, buflen, "FAIL\n");
@@ -107,8 +168,28 @@
 			return 0;
 		return ret;
 	}
-	return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
-					  buf, buflen);
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL)
+		return -1;
+
+	pos = os_strchr(txtaddr, ' ');
+	if (pos) {
+		pos++;
+
+#ifdef HOSTAPD_DUMP_STATE
+		if (os_strcmp(pos, "eapol") == 0) {
+			if (sta->eapol_sm == NULL)
+				return -1;
+			return eapol_auth_dump_state(sta->eapol_sm, buf,
+						     buflen);
+		}
+#endif /* HOSTAPD_DUMP_STATE */
+
+		return -1;
+	}
+
+	return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
 }
 
 
@@ -125,7 +206,11 @@
 		if (ret < 0 || (size_t) ret >= buflen)
 			return 0;
 		return ret;
-	}		
+	}
+
+	if (!sta->next)
+		return 0;
+
 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
 }
 
@@ -145,11 +230,12 @@
 	if (mgmt == NULL)
 		return -1;
 
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
-		" with minor reason code %u (stype=%u)",
-		MAC2STR(addr), minor_reason_code, stype);
-
 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+		" with minor reason code %u (stype=%u (%s))",
+		MAC2STR(addr), minor_reason_code, stype,
+		fc2str(mgmt->frame_control));
+
 	os_memcpy(mgmt->da, addr, ETH_ALEN);
 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
@@ -165,9 +251,8 @@
 
 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
 	*pos++ = 4 + 3 + 1;
-	WPA_PUT_BE24(pos, OUI_WFA);
-	pos += 3;
-	*pos++ = P2P_OUI_TYPE;
+	WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+	pos += 4;
 
 	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
 	WPA_PUT_LE16(pos, 1);
@@ -197,6 +282,10 @@
 	if (hwaddr_aton(txtaddr, addr))
 		return -1;
 
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
 	pos = os_strstr(txtaddr, " test=");
 	if (pos) {
 		struct ieee80211_mgmt mgmt;
@@ -211,8 +300,7 @@
 		os_memcpy(mgmt.da, addr, ETH_ALEN);
 		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
 		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
-		mgmt.u.deauth.reason_code =
-			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		mgmt.u.deauth.reason_code = host_to_le16(reason);
 		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
 					     IEEE80211_HDRLEN +
 					     sizeof(mgmt.u.deauth),
@@ -229,10 +317,6 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	pos = os_strstr(txtaddr, " reason=");
-	if (pos)
-		reason = atoi(pos + 8);
-
 	hostapd_drv_sta_deauth(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
@@ -258,6 +342,10 @@
 	if (hwaddr_aton(txtaddr, addr))
 		return -1;
 
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
 	pos = os_strstr(txtaddr, " test=");
 	if (pos) {
 		struct ieee80211_mgmt mgmt;
@@ -272,8 +360,7 @@
 		os_memcpy(mgmt.da, addr, ETH_ALEN);
 		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
 		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
-		mgmt.u.disassoc.reason_code =
-			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		mgmt.u.disassoc.reason_code = host_to_le16(reason);
 		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
 					     IEEE80211_HDRLEN +
 					     sizeof(mgmt.u.deauth),
@@ -290,10 +377,6 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	pos = os_strstr(txtaddr, " reason=");
-	if (pos)
-		reason = atoi(pos + 8);
-
 	hostapd_drv_sta_disassoc(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
@@ -303,3 +386,144 @@
 
 	return 0;
 }
+
+
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+			      size_t buflen)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	int len = 0, ret;
+	size_t i;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "state=%s\n"
+			  "phy=%s\n"
+			  "freq=%d\n"
+			  "num_sta_non_erp=%d\n"
+			  "num_sta_no_short_slot_time=%d\n"
+			  "num_sta_no_short_preamble=%d\n"
+			  "olbc=%d\n"
+			  "num_sta_ht_no_gf=%d\n"
+			  "num_sta_no_ht=%d\n"
+			  "num_sta_ht_20_mhz=%d\n"
+			  "num_sta_ht40_intolerant=%d\n"
+			  "olbc_ht=%d\n"
+			  "ht_op_mode=0x%x\n",
+			  hostapd_state_text(iface->state),
+			  iface->phy,
+			  iface->freq,
+			  iface->num_sta_non_erp,
+			  iface->num_sta_no_short_slot_time,
+			  iface->num_sta_no_short_preamble,
+			  iface->olbc,
+			  iface->num_sta_ht_no_gf,
+			  iface->num_sta_no_ht,
+			  iface->num_sta_ht_20mhz,
+			  iface->num_sta_ht40_intolerant,
+			  iface->olbc_ht,
+			  iface->ht_op_mode);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	if (!iface->cac_started || !iface->dfs_cac_ms) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "cac_time_seconds=%d\n"
+				  "cac_time_left_seconds=N/A\n",
+				  iface->dfs_cac_ms / 1000);
+	} else {
+		/* CAC started and CAC time set - calculate remaining time */
+		struct os_reltime now;
+		unsigned int left_time;
+
+		os_reltime_age(&iface->dfs_cac_start, &now);
+		left_time = iface->dfs_cac_ms / 1000 - now.sec;
+		ret = os_snprintf(buf + len, buflen - len,
+				  "cac_time_seconds=%u\n"
+				  "cac_time_left_seconds=%u\n",
+				  iface->dfs_cac_ms / 1000,
+				  left_time);
+	}
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "channel=%u\n"
+			  "secondary_channel=%d\n"
+			  "ieee80211n=%d\n"
+			  "ieee80211ac=%d\n"
+			  "vht_oper_chwidth=%d\n"
+			  "vht_oper_centr_freq_seg0_idx=%d\n"
+			  "vht_oper_centr_freq_seg1_idx=%d\n",
+			  iface->conf->channel,
+			  iface->conf->secondary_channel,
+			  iface->conf->ieee80211n,
+			  iface->conf->ieee80211ac,
+			  iface->conf->vht_oper_chwidth,
+			  iface->conf->vht_oper_centr_freq_seg0_idx,
+			  iface->conf->vht_oper_centr_freq_seg1_idx);
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		ret = os_snprintf(buf + len, buflen - len,
+				  "bss[%d]=%s\n"
+				  "bssid[%d]=" MACSTR "\n"
+				  "ssid[%d]=%s\n"
+				  "num_sta[%d]=%d\n",
+				  (int) i, bss->conf->iface,
+				  (int) i, MAC2STR(bss->own_addr),
+				  (int) i,
+				  wpa_ssid_txt(bss->conf->ssid.ssid,
+					       bss->conf->ssid.ssid_len),
+				  (int) i, bss->num_sta);
+		if (ret < 0 || (size_t) ret >= buflen - len)
+			return len;
+		len += ret;
+	}
+
+	return len;
+}
+
+
+int hostapd_parse_csa_settings(const char *pos,
+			       struct csa_settings *settings)
+{
+	char *end;
+
+	os_memset(settings, 0, sizeof(*settings));
+	settings->cs_count = strtol(pos, &end, 10);
+	if (pos == end) {
+		wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
+		return -1;
+	}
+
+	settings->freq_params.freq = atoi(end);
+	if (settings->freq_params.freq == 0) {
+		wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+		return -1;
+	}
+
+#define SET_CSA_SETTING(str) \
+	do { \
+		const char *pos2 = os_strstr(pos, " " #str "="); \
+		if (pos2) { \
+			pos2 += sizeof(" " #str "=") - 1; \
+			settings->freq_params.str = atoi(pos2); \
+		} \
+	} while (0)
+
+	SET_CSA_SETTING(center_freq1);
+	SET_CSA_SETTING(center_freq2);
+	SET_CSA_SETTING(bandwidth);
+	SET_CSA_SETTING(sec_channel_offset);
+	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
+	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+	settings->block_tx = !!os_strstr(pos, " blocktx");
+#undef SET_CSA_SETTING
+
+	return 0;
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index e83f894..ee58b4c 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -1,6 +1,6 @@
 /*
  * Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,5 +19,10 @@
 				      const char *txtaddr);
 int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
 				    const char *txtaddr);
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+			      size_t buflen);
+int hostapd_parse_csa_settings(const char *pos,
+			       struct csa_settings *settings);
+
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
new file mode 100644
index 0000000..20419f3
--- /dev/null
+++ b/src/ap/dfs.c
@@ -0,0 +1,947 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "drivers/driver.h"
+#include "dfs.h"
+
+
+static int dfs_get_used_n_chans(struct hostapd_iface *iface)
+{
+	int n_chans = 1;
+
+	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:
+			break;
+		case VHT_CHANWIDTH_80MHZ:
+			n_chans = 4;
+			break;
+		case VHT_CHANWIDTH_160MHZ:
+			n_chans = 8;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return n_chans;
+}
+
+
+static int dfs_channel_available(struct hostapd_channel_data *chan,
+				 int skip_radar)
+{
+	/*
+	 * When radar detection happens, CSA is performed. However, there's no
+	 * time for CAC, so radar channels must be skipped when finding a new
+	 * channel for CSA, unless they are available for immediate use.
+	 */
+	if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+	     HOSTAPD_CHAN_DFS_AVAILABLE))
+		return 0;
+
+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+		return 0;
+	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
+		return 0;
+	return 1;
+}
+
+
+static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
+{
+	/*
+	 * The tables contain first valid channel number based on channel width.
+	 * We will also choose this first channel as the control one.
+	 */
+	int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+			     184, 192 };
+	/*
+	 * VHT80, valid channels based on center frequency:
+	 * 42, 58, 106, 122, 138, 155
+	 */
+	int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
+	/*
+	 * VHT160 valid channels based on center frequency:
+	 * 50, 114
+	 */
+	int allowed_160[] = { 36, 100 };
+	int *allowed = allowed_40;
+	unsigned int i, allowed_no = 0;
+
+	switch (n_chans) {
+	case 2:
+		allowed = allowed_40;
+		allowed_no = ARRAY_SIZE(allowed_40);
+		break;
+	case 4:
+		allowed = allowed_80;
+		allowed_no = ARRAY_SIZE(allowed_80);
+		break;
+	case 8:
+		allowed = allowed_160;
+		allowed_no = ARRAY_SIZE(allowed_160);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
+		break;
+	}
+
+	for (i = 0; i < allowed_no; i++) {
+		if (chan->chan == allowed[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+				    int first_chan_idx, int num_chans,
+				    int skip_radar)
+{
+	struct hostapd_channel_data *first_chan, *chan;
+	int i;
+
+	if (first_chan_idx + num_chans >= mode->num_channels)
+		return 0;
+
+	first_chan = &mode->channels[first_chan_idx];
+
+	for (i = 0; i < num_chans; i++) {
+		chan = &mode->channels[first_chan_idx + i];
+
+		if (first_chan->freq + i * 20 != chan->freq)
+			return 0;
+
+		if (!dfs_channel_available(chan, skip_radar))
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+			  struct hostapd_channel_data *chan)
+{
+	int *entry;
+
+	if (!iface->conf->chanlist)
+		return 1;
+
+	for (entry = iface->conf->chanlist; *entry != -1; entry++) {
+		if (*entry == chan->chan)
+			return 1;
+	}
+	return 0;
+}
+
+
+/*
+ * 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
+ */
+static int dfs_find_channel(struct hostapd_iface *iface,
+			    struct hostapd_channel_data **ret_chan,
+			    int idx, int skip_radar)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int i, channel_idx = 0, n_chans;
+
+	mode = iface->current_mode;
+	n_chans = dfs_get_used_n_chans(iface);
+
+	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		/* Skip HT40/VHT incompatible channels */
+		if (iface->conf->ieee80211n &&
+		    iface->conf->secondary_channel &&
+		    !dfs_is_chan_allowed(chan, n_chans))
+			continue;
+
+		/* Skip incompatible chandefs */
+		if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
+		if (ret_chan && idx == channel_idx) {
+			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+			*ret_chan = chan;
+			return idx;
+		}
+		wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
+		channel_idx++;
+	}
+	return channel_idx;
+}
+
+
+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)
+{
+	if (!iface->conf->ieee80211ac)
+		return;
+
+	if (!chan)
+		return;
+
+	*vht_oper_centr_freq_seg1_idx = 0;
+
+	switch (iface->conf->vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		if (secondary_channel == 1)
+			*vht_oper_centr_freq_seg0_idx = chan->chan + 2;
+		else if (secondary_channel == -1)
+			*vht_oper_centr_freq_seg0_idx = chan->chan - 2;
+		else
+			*vht_oper_centr_freq_seg0_idx = chan->chan;
+		break;
+	case VHT_CHANWIDTH_80MHZ:
+		*vht_oper_centr_freq_seg0_idx = chan->chan + 6;
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		*vht_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;
+		break;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
+		   *vht_oper_centr_freq_seg0_idx,
+		   *vht_oper_centr_freq_seg1_idx);
+}
+
+
+/* Return start channel idx we will use for mode->channels[idx] */
+static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int channel_no = iface->conf->channel;
+	int res = -1, i;
+
+	/* HT40- */
+	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:
+			break;
+		case VHT_CHANWIDTH_80MHZ:
+			channel_no =
+				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+			break;
+		case VHT_CHANWIDTH_160MHZ:
+			channel_no =
+				iface->conf->vht_oper_centr_freq_seg0_idx - 14;
+			break;
+		default:
+			wpa_printf(MSG_INFO,
+				   "DFS only VHT20/40/80/160 is supported now");
+			channel_no = -1;
+			break;
+		}
+	}
+
+	/* Get idx */
+	mode = iface->current_mode;
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+		if (chan->chan == channel_no) {
+			res = i;
+			break;
+		}
+	}
+
+	if (res == -1) {
+		wpa_printf(MSG_DEBUG,
+			   "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
+			   mode->num_channels, channel_no, iface->conf->channel,
+			   iface->conf->ieee80211n,
+			   iface->conf->secondary_channel,
+			   iface->conf->vht_oper_chwidth);
+
+		for (i = 0; i < mode->num_channels; i++) {
+			wpa_printf(MSG_DEBUG, "Available channel: %d",
+				   mode->channels[i].chan);
+		}
+	}
+
+	return res;
+}
+
+
+/* At least one channel have radar flag */
+static int dfs_check_chans_radar(struct hostapd_iface *iface,
+				 int start_chan_idx, int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i, res = 0;
+
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+		if (channel->flag & HOSTAPD_CHAN_RADAR)
+			res++;
+	}
+
+	return res;
+}
+
+
+/* All channels available */
+static int dfs_check_chans_available(struct hostapd_iface *iface,
+				     int start_chan_idx, int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i;
+
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+
+		if (channel->flag & HOSTAPD_CHAN_DISABLED)
+			break;
+
+		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+			continue;
+
+		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
+		    HOSTAPD_CHAN_DFS_AVAILABLE)
+			break;
+	}
+
+	return i == n_chans;
+}
+
+
+/* At least one channel unavailable */
+static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
+				       int start_chan_idx,
+				       int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i, res = 0;
+
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+		if (channel->flag & HOSTAPD_CHAN_DISABLED)
+			res++;
+		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
+		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
+			res++;
+	}
+
+	return res;
+}
+
+
+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,
+		      int skip_radar)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	int num_available_chandefs;
+	int chan_idx;
+	u32 _rand;
+
+	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;
+
+	if (iface->current_mode == NULL)
+		return NULL;
+
+	mode = iface->current_mode;
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return NULL;
+
+	/* Get the count first */
+	num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+	if (num_available_chandefs == 0)
+		return NULL;
+
+	os_get_random((u8 *) &_rand, sizeof(_rand));
+	chan_idx = _rand % num_available_chandefs;
+	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+
+	/* dfs_find_channel() calculations assume HT40+ */
+	if (iface->conf->secondary_channel)
+		*secondary_channel = 1;
+	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);
+
+	return chan;
+}
+
+
+static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	int i;
+
+	mode = iface->current_mode;
+	if (mode == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+		if (chan->freq == freq) {
+			if (chan->flag & HOSTAPD_CHAN_RADAR) {
+				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+				chan->flag |= state;
+				return 1; /* Channel found */
+			}
+		}
+	}
+	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+	return 0;
+}
+
+
+static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
+			 int chan_offset, int chan_width, int cf1,
+			 int cf2, u32 state)
+{
+	int n_chans = 1, i;
+	struct hostapd_hw_modes *mode;
+	int frequency = freq;
+	int ret = 0;
+
+	mode = iface->current_mode;
+	if (mode == NULL)
+		return 0;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+		return 0;
+	}
+
+	/* Seems cf1 and chan_width is enough here */
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		n_chans = 1;
+		if (frequency == 0)
+			frequency = cf1;
+		break;
+	case CHAN_WIDTH_40:
+		n_chans = 2;
+		frequency = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		n_chans = 4;
+		frequency = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		n_chans = 8;
+		frequency = cf1 - 70;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+			   chan_width);
+		break;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
+		   n_chans);
+	for (i = 0; i < n_chans; i++) {
+		ret += set_dfs_state_freq(iface, frequency, state);
+		frequency = frequency + 20;
+	}
+
+	return ret;
+}
+
+
+static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+				       int chan_width, int cf1, int cf2)
+{
+	int start_chan_idx;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int n_chans, i, j, frequency = freq, radar_n_chans = 1;
+	u8 radar_chan;
+	int res = 0;
+
+	/* Our configuration */
+	mode = iface->current_mode;
+	start_chan_idx = dfs_get_start_chan_idx(iface);
+	n_chans = dfs_get_used_n_chans(iface);
+
+	/* Check we are on DFS channel(s) */
+	if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
+		return 0;
+
+	/* Reported via radar event */
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		radar_n_chans = 1;
+		if (frequency == 0)
+			frequency = cf1;
+		break;
+	case CHAN_WIDTH_40:
+		radar_n_chans = 2;
+		frequency = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		radar_n_chans = 4;
+		frequency = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		radar_n_chans = 8;
+		frequency = cf1 - 70;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+			   chan_width);
+		break;
+	}
+
+	ieee80211_freq_to_chan(frequency, &radar_chan);
+
+	for (i = 0; i < n_chans; i++) {
+		chan = &mode->channels[start_chan_idx + i];
+		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+			continue;
+		for (j = 0; j < radar_n_chans; j++) {
+			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
+				   chan->chan, radar_chan + j * 4);
+			if (chan->chan == radar_chan + j * 4)
+				res++;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
+
+	return res;
+}
+
+
+static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+				     int start_chan_idx, int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i;
+	unsigned int cac_time_ms = 0;
+
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+			continue;
+		if (channel->dfs_cac_ms > cac_time_ms)
+			cac_time_ms = channel->dfs_cac_ms;
+	}
+
+	return cac_time_ms;
+}
+
+
+/*
+ * Main DFS handler
+ * 1 - continue channel/ap setup
+ * 0 - channel/ap setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *channel;
+	int res, n_chans, start_chan_idx;
+	int skip_radar = 0;
+
+	iface->cac_started = 0;
+
+	do {
+		/* Get start (first) channel for current configuration */
+		start_chan_idx = dfs_get_start_chan_idx(iface);
+		if (start_chan_idx == -1)
+			return -1;
+
+		/* Get number of used channels, depend on width */
+		n_chans = dfs_get_used_n_chans(iface);
+
+		/* Setup CAC time */
+		iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
+						     n_chans);
+
+		/* Check if any of configured channels require DFS */
+		res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+		wpa_printf(MSG_DEBUG,
+			   "DFS %d channels required radar detection",
+			   res);
+		if (!res)
+			return 1;
+
+		/* Check if all channels are DFS available */
+		res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
+		wpa_printf(MSG_DEBUG,
+			   "DFS all channels available, (SKIP CAC): %s",
+			   res ? "yes" : "no");
+		if (res)
+			return 1;
+
+		/* Check if any of configured channels is unavailable */
+		res = dfs_check_chans_unavailable(iface, start_chan_idx,
+						  n_chans);
+		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
+			   res, res ? "yes": "no");
+		if (res) {
+			int sec = 0;
+			u8 cf1 = 0, cf2 = 0;
+
+			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+							skip_radar);
+			if (!channel) {
+				wpa_printf(MSG_ERROR, "could not get valid channel");
+				return -1;
+			}
+
+			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;
+		}
+	} while (res);
+
+	/* Finally start CAC */
+	hostapd_set_state(iface, HAPD_IFACE_DFS);
+	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+		iface->freq,
+		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,
+		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);
+
+	if (res) {
+		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
+		"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	if (success) {
+		/* Complete iface/ap configuration */
+		set_dfs_state(iface, freq, ht_enabled, chan_offset,
+			      chan_width, cf1, cf2,
+			      HOSTAPD_CHAN_DFS_AVAILABLE);
+		iface->cac_started = 0;
+		hostapd_setup_interface_complete(iface, 0);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *channel;
+	int secondary_channel;
+	u8 vht_oper_centr_freq_seg0_idx = 0;
+	u8 vht_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,
+					skip_radar);
+
+	if (!channel) {
+		wpa_printf(MSG_ERROR, "No valid channel available");
+		hostapd_setup_interface_complete(iface, err);
+		return err;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+		   channel->chan);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+		"freq=%d chan=%d sec_chan=%d", channel->freq,
+		channel->chan, secondary_channel);
+
+	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;
+	err = 0;
+
+	hostapd_setup_interface_complete(iface, err);
+	return err;
+}
+
+
+static int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+	unsigned int i;
+	for (i = 0; i < iface->num_bss; i++)
+		if (iface->bss[i]->csa_in_progress)
+			return 1;
+	return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *channel;
+	int secondary_channel;
+	u8 vht_oper_centr_freq_seg0_idx;
+	u8 vht_oper_centr_freq_seg1_idx;
+	int skip_radar = 1;
+	struct csa_settings csa_settings;
+	unsigned int i;
+	int err = 1;
+
+	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
+		   __func__, iface->cac_started ? "yes" : "no",
+		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+	/* Check if CSA in progress */
+	if (hostapd_csa_in_progress(iface))
+		return 0;
+
+	/* Check if active CAC */
+	if (iface->cac_started)
+		return hostapd_dfs_start_channel_switch_cac(iface);
+
+	/* Perform channel switch/CSA */
+	channel = dfs_get_valid_channel(iface, &secondary_channel,
+					&vht_oper_centr_freq_seg0_idx,
+					&vht_oper_centr_freq_seg1_idx,
+					skip_radar);
+
+	if (!channel) {
+		/*
+		 * If there is no channel to switch immediately to, check if
+		 * there is another channel where we can switch even if it
+		 * requires to perform a CAC first.
+		 */
+		skip_radar = 0;
+		channel = dfs_get_valid_channel(iface, &secondary_channel,
+						&vht_oper_centr_freq_seg0_idx,
+						&vht_oper_centr_freq_seg1_idx,
+						skip_radar);
+		if (!channel) {
+			/* FIXME: Wait for channel(s) to become available */
+			hostapd_disable_iface(iface);
+			return err;
+		}
+
+		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_disable_iface(iface);
+		hostapd_enable_iface(iface);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+		   channel->chan);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+		"freq=%d chan=%d sec_chan=%d", channel->freq,
+		channel->chan, secondary_channel);
+
+	/* Setup CSA request */
+	os_memset(&csa_settings, 0, sizeof(csa_settings));
+	csa_settings.cs_count = 5;
+	csa_settings.block_tx = 1;
+	err = hostapd_set_freq_params(&csa_settings.freq_params,
+				      iface->conf->hw_mode,
+				      channel->freq,
+				      channel->chan,
+				      iface->conf->ieee80211n,
+				      iface->conf->ieee80211ac,
+				      secondary_channel,
+				      iface->conf->vht_oper_chwidth,
+				      vht_oper_centr_freq_seg0_idx,
+				      vht_oper_centr_freq_seg1_idx,
+				      iface->current_mode->vht_capab);
+
+	if (err) {
+		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
+		hostapd_disable_iface(iface);
+		return err;
+	}
+
+	for (i = 0; i < iface->num_bss; i++) {
+		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+		if (err)
+			break;
+	}
+
+	if (err) {
+		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
+			   err);
+		iface->freq = channel->freq;
+		iface->conf->channel = channel->chan;
+		iface->conf->secondary_channel = secondary_channel;
+		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_disable_iface(iface);
+		hostapd_enable_iface(iface);
+		return 0;
+	}
+
+	/* Channel configuration will be updated once CSA completes and
+	 * ch_switch_notify event is received */
+
+	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+	return 0;
+}
+
+
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+			       int ht_enabled, int chan_offset, int chan_width,
+			       int cf1, int cf2)
+{
+	int res;
+
+	if (!iface->conf->ieee80211h)
+		return 0;
+
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	/* mark radar frequency as invalid */
+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+		      cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
+
+	/* Skip if reported radar event not overlapped our channels */
+	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
+	if (!res)
+		return 0;
+
+	/* radar detected while operating, switch the channel. */
+	res = hostapd_dfs_start_channel_switch(iface);
+
+	return res;
+}
+
+
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+	/* TODO add correct implementation here */
+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+	return 0;
+}
+
+
+int hostapd_is_dfs_required(struct hostapd_iface *iface)
+{
+	int n_chans, start_chan_idx;
+
+	if (!iface->conf->ieee80211h || !iface->current_mode ||
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	/* Get start (first) channel for current configuration */
+	start_chan_idx = dfs_get_start_chan_idx(iface);
+	if (start_chan_idx == -1)
+		return -1;
+
+	/* Get number of used channels, depend on width */
+	n_chans = dfs_get_used_n_chans(iface);
+
+	/* Check if any of configured channels require DFS */
+	return dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+}
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
new file mode 100644
index 0000000..a619c55
--- /dev/null
+++ b/src/ap/dfs.h
@@ -0,0 +1,26 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef DFS_H
+#define DFS_H
+
+int hostapd_handle_dfs(struct hostapd_iface *iface);
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2);
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+			       int ht_enabled,
+			       int chan_offset, int chan_width,
+			       int cf1, int cf2);
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+			     int ht_enabled,
+			     int chan_offset, int chan_width, int cf1, int cf2);
+int hostapd_is_dfs_required(struct hostapd_iface *iface);
+
+#endif /* DFS_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index d6bc98d..93804de 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "radius/radius.h"
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
@@ -29,6 +30,8 @@
 #include "ap_drv_ops.h"
 #include "ap_config.h"
 #include "hw_features.h"
+#include "dfs.h"
+#include "beacon.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -77,6 +80,12 @@
 		ie = elems.wpa_ie - 2;
 		ielen = elems.wpa_ie_len + 2;
 		wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+#ifdef CONFIG_HS20
+	} else if (elems.osen) {
+		ie = elems.osen - 2;
+		ielen = elems.osen_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
+#endif /* CONFIG_HS20 */
 	} else {
 		ie = NULL;
 		ielen = 0;
@@ -114,6 +123,31 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+	if (elems.ht_capabilities &&
+	    elems.ht_capabilities_len >=
+	    sizeof(struct ieee80211_ht_capabilities) &&
+	    (hapd->iface->conf->ht_capab &
+	     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+		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)
+			ht40_intolerant_add(hapd->iface, sta);
+	}
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_INTERWORKING
+	if (elems.ext_capab && elems.ext_capab_len > 4) {
+		if (elems.ext_capab[4] & 0x01)
+			sta->qos_map_enabled = 1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
 #ifdef CONFIG_HS20
 	wpabuf_free(sta->hs20_ie);
 	if (elems.hs20 && elems.hs20_len > 4) {
@@ -273,6 +307,29 @@
 			sta->flags |= WLAN_STA_MAYBE_WPS;
 		wpabuf_free(wps);
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_HS20
+	} else if (hapd->conf->osen) {
+		if (elems.osen == NULL) {
+			hostapd_logger(
+				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+				HOSTAPD_LEVEL_INFO,
+				"No HS 2.0 OSEN element in association request");
+			return WLAN_STATUS_INVALID_IE;
+		}
+
+		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+				   "state machine");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+				      elems.osen - 2, elems.osen_len + 2) < 0)
+			return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
 	}
 #ifdef CONFIG_WPS
 skip_wpa_check:
@@ -291,6 +348,7 @@
 
 	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
 
 	if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
 		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -373,14 +431,15 @@
 
 
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
-			     int offset)
+			     int offset, int width, int cf1, int cf2)
 {
 #ifdef NEED_AP_MLME
-	int channel;
+	int channel, chwidth, seg0_idx = 0, seg1_idx = 0;
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO, "driver had channel switch: "
-		       "freq=%d, ht=%d, offset=%d", freq, ht, offset);
+		       "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d",
+		       freq, ht, offset, width, cf1, cf2);
 
 	hapd->iface->freq = freq;
 
@@ -392,9 +451,52 @@
 		return;
 	}
 
+	switch (width) {
+	case CHAN_WIDTH_80:
+		chwidth = VHT_CHANWIDTH_80MHZ;
+		break;
+	case CHAN_WIDTH_80P80:
+		chwidth = VHT_CHANWIDTH_80P80MHZ;
+		break;
+	case CHAN_WIDTH_160:
+		chwidth = VHT_CHANWIDTH_160MHZ;
+		break;
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+	case CHAN_WIDTH_40:
+	default:
+		chwidth = VHT_CHANWIDTH_USE_HT;
+		break;
+	}
+
+	switch (hapd->iface->current_mode->mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		if (cf1 > 5000)
+			seg0_idx = (cf1 - 5000) / 5;
+		if (cf2 > 5000)
+			seg1_idx = (cf2 - 5000) / 5;
+		break;
+	default:
+		seg0_idx = hostapd_hw_get_channel(hapd, cf1);
+		seg1_idx = hostapd_hw_get_channel(hapd, cf2);
+		break;
+	}
+
 	hapd->iconf->channel = channel;
 	hapd->iconf->ieee80211n = ht;
 	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;
+
+	if (hapd->csa_in_progress &&
+	    freq == hapd->cs_freq_params.freq) {
+		hostapd_cleanup_cs_params(hapd);
+		ieee802_11_set_beacon(hapd);
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d",
+			freq);
+	}
 #endif /* NEED_AP_MLME */
 }
 
@@ -506,39 +608,48 @@
 
 
 static void hostapd_action_rx(struct hostapd_data *hapd,
-			      struct rx_action *action)
+			      struct rx_mgmt *drv_mgmt)
 {
+	struct ieee80211_mgmt *mgmt;
 	struct sta_info *sta;
+	size_t plen __maybe_unused;
+	u16 fc;
+
+	if (drv_mgmt->frame_len < 24 + 1)
+		return;
+
+	plen = drv_mgmt->frame_len - 24 - 1;
+
+	mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
+	fc = le_to_host16(mgmt->frame_control);
+	if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
+		return; /* handled by the driver */
 
         wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
-		   action->category, (int) action->len);
+		   mgmt->u.action.category, (int) plen);
 
-	sta = ap_get_sta(hapd, action->sa);
+	sta = ap_get_sta(hapd, mgmt->sa);
 	if (sta == NULL) {
 		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
 		return;
 	}
 #ifdef CONFIG_IEEE80211R
-	if (action->category == WLAN_ACTION_FT) {
-		wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d",
-			   __func__, (int) action->len);
-		wpa_ft_action_rx(sta->wpa_sm, action->data, action->len);
+	if (mgmt->u.action.category == WLAN_ACTION_FT) {
+		const u8 *payload = drv_mgmt->frame + 24 + 1;
+		wpa_ft_action_rx(sta->wpa_sm, payload, plen);
 	}
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
-	if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) {
-		wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d",
-			   __func__, (int) action->len);
-		ieee802_11_sa_query_action(hapd, action->sa,
-					   *(action->data + 1),
-					   action->data + 2);
+	if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
+		ieee802_11_sa_query_action(
+			hapd, mgmt->sa,
+			mgmt->u.action.u.sa_query_resp.action,
+			mgmt->u.action.u.sa_query_resp.trans_id);
 	}
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM
-	if (action->category == WLAN_ACTION_WNM) {
-		wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
-			   __func__, (int) action->len);
-		ieee802_11_rx_wnm_action_ap(hapd, action);
+	if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
 	}
 #endif /* CONFIG_WNM */
 }
@@ -580,17 +691,32 @@
 }
 
 
-static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
 {
 	struct hostapd_iface *iface = hapd->iface;
 	const struct ieee80211_hdr *hdr;
 	const u8 *bssid;
 	struct hostapd_frame_info fi;
+	int ret;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_mgmt_frame_handling) {
+		size_t hex_len = 2 * rx_mgmt->frame_len + 1;
+		char *hex = os_malloc(hex_len);
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
+					 rx_mgmt->frame_len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex);
+			os_free(hex);
+		}
+		return 1;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
 	bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
 	if (bssid == NULL)
-		return;
+		return 0;
 
 	hapd = get_hapd_bssid(iface, bssid);
 	if (hapd == NULL) {
@@ -605,7 +731,7 @@
 		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
 			hapd = iface->bss[0];
 		else
-			return;
+			return 0;
 	}
 
 	os_memset(&fi, 0, sizeof(fi));
@@ -614,53 +740,25 @@
 
 	if (hapd == HAPD_BROADCAST) {
 		size_t i;
-		for (i = 0; i < iface->num_bss; i++)
-			ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
-					rx_mgmt->frame_len, &fi);
+		ret = 0;
+		for (i = 0; i < iface->num_bss; i++) {
+			/* if bss is set, driver will call this function for
+			 * each bss individually. */
+			if (rx_mgmt->drv_priv &&
+			    (iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
+				continue;
+
+			if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+					    rx_mgmt->frame_len, &fi) > 0)
+				ret = 1;
+		}
 	} else
-		ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+		ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
+				      &fi);
 
 	random_add_randomness(&fi, sizeof(fi));
-}
 
-
-static void hostapd_rx_action(struct hostapd_data *hapd,
-			      struct rx_action *rx_action)
-{
-	struct rx_mgmt rx_mgmt;
-	u8 *buf;
-	struct ieee80211_hdr *hdr;
-
-	wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR
-		   " BSSID=" MACSTR " category=%u",
-		   MAC2STR(rx_action->da), MAC2STR(rx_action->sa),
-		   MAC2STR(rx_action->bssid), rx_action->category);
-	wpa_hexdump(MSG_MSGDUMP, "Received action frame contents",
-		    rx_action->data, rx_action->len);
-
-	buf = os_zalloc(24 + 1 + rx_action->len);
-	if (buf == NULL)
-		return;
-	hdr = (struct ieee80211_hdr *) buf;
-	hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					  WLAN_FC_STYPE_ACTION);
-	if (rx_action->category == WLAN_ACTION_SA_QUERY) {
-		/*
-		 * Assume frame was protected; it would have been dropped if
-		 * not.
-		 */
-		hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-	}
-	os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN);
-	os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN);
-	os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN);
-	buf[24] = rx_action->category;
-	os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len);
-	os_memset(&rx_mgmt, 0, sizeof(rx_mgmt));
-	rx_mgmt.frame = buf;
-	rx_mgmt.frame_len = 24 + 1 + rx_action->len;
-	hostapd_mgmt_rx(hapd, &rx_mgmt);
-	os_free(buf);
+	return ret;
 }
 
 
@@ -785,6 +883,64 @@
 }
 
 
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
+		   hapd->conf->iface);
+
+	if (hapd->csa_in_progress) {
+		wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
+			   hapd->conf->iface);
+		hostapd_switch_channel_fallback(hapd->iface,
+						&hapd->cs_freq_params);
+	}
+}
+
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+					     struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+	hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled,
+				   radar->chan_offset, radar->chan_width,
+				   radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+					   struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+					  struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+					   struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+	hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -858,10 +1014,16 @@
 					    data->rx_from_unknown.addr,
 					    data->rx_from_unknown.wds);
 		break;
-	case EVENT_RX_MGMT:
-		hostapd_mgmt_rx(hapd, &data->rx_mgmt);
-		break;
 #endif /* NEED_AP_MLME */
+	case EVENT_RX_MGMT:
+		if (!data->rx_mgmt.frame)
+			break;
+#ifdef NEED_AP_MLME
+		if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0)
+			break;
+#endif /* NEED_AP_MLME */
+		hostapd_action_rx(hapd, &data->rx_mgmt);
+		break;
 	case EVENT_RX_PROBE_REQ:
 		if (data->rx_probe_req.sa == NULL ||
 		    data->rx_probe_req.ie == NULL)
@@ -900,15 +1062,6 @@
 			break;
 		hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
 		break;
-	case EVENT_RX_ACTION:
-		if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
-		    data->rx_action.bssid == NULL)
-			break;
-#ifdef NEED_AP_MLME
-		hostapd_rx_action(hapd, &data->rx_action);
-#endif /* NEED_AP_MLME */
-		hostapd_action_rx(hapd, &data->rx_action);
-		break;
 	case EVENT_AUTH:
 		hostapd_notif_auth(hapd, &data->auth);
 		break;
@@ -917,7 +1070,10 @@
 			break;
 		hostapd_event_ch_switch(hapd, data->ch_switch.freq,
 					data->ch_switch.ht_enabled,
-					data->ch_switch.ch_offset);
+					data->ch_switch.ch_offset,
+					data->ch_switch.ch_width,
+					data->ch_switch.cf1,
+					data->ch_switch.cf2);
 		break;
 	case EVENT_CONNECT_FAILED_REASON:
 		if (!data)
@@ -929,6 +1085,39 @@
 	case EVENT_SURVEY:
 		hostapd_event_get_survey(hapd, &data->survey_results);
 		break;
+#ifdef NEED_AP_MLME
+	case EVENT_INTERFACE_UNAVAILABLE:
+		hostapd_event_iface_unavailable(hapd);
+		break;
+	case EVENT_DFS_RADAR_DETECTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_FINISHED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_ABORTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_NOP_FINISHED:
+		if (!data)
+			break;
+		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+		break;
+	case EVENT_CHANNEL_LIST_CHANGED:
+		/* channel list changed (regulatory?), update channel list */
+		/* TODO: check this. hostapd_get_hw_features() initializes
+		 * too much stuff. */
+		/* hostapd_get_hw_features(hapd->iface); */
+		hostapd_channel_list_updated(
+			hapd->iface, data->channel_list_changed.initiator);
+		break;
+#endif /* NEED_AP_MLME */
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 79d50e5..559d77f 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -83,12 +83,14 @@
 
 	for (i = 0; i < argc; i++) {
 		if (os_strcmp(col[i], "password") == 0 && argv[i]) {
-			os_free(user->password);
+			bin_clear_free(user->password, user->password_len);
 			user->password_len = os_strlen(argv[i]);
 			user->password = (u8 *) os_strdup(argv[i]);
 			user->next = (void *) 1;
 		} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
 			set_user_methods(user, argv[i]);
+		} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
+			user->remediation = strlen(argv[i]) > 0;
 		}
 	}
 
@@ -116,7 +118,7 @@
 	if (len <= user->identity_len &&
 	    os_memcmp(argv[id], user->identity, len) == 0 &&
 	    (user->password == NULL || len > user->password_len)) {
-		os_free(user->password);
+		bin_clear_free(user->password, user->password_len);
 		user->password_len = os_strlen(argv[id]);
 		user->password = (u8 *) os_strdup(argv[id]);
 		user->next = (void *) 1;
@@ -156,8 +158,10 @@
 		return NULL;
 	}
 
-	os_free(hapd->tmp_eap_user.identity);
-	os_free(hapd->tmp_eap_user.password);
+	bin_clear_free(hapd->tmp_eap_user.identity,
+		       hapd->tmp_eap_user.identity_len);
+	bin_clear_free(hapd->tmp_eap_user.password,
+		       hapd->tmp_eap_user.password_len);
 	os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
 	hapd->tmp_eap_user.phase2 = phase2;
 	hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
@@ -173,8 +177,8 @@
 	}
 
 	os_snprintf(cmd, sizeof(cmd),
-		    "SELECT password,methods FROM users WHERE "
-		    "identity='%s' AND phase2=%d;", id_str, phase2);
+		    "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+		    id_str, phase2);
 	wpa_printf(MSG_DEBUG, "DB: %s", cmd);
 	if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
 	    SQLITE_OK) {
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index b3574ba..ad07107 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1,6 +1,6 @@
 /*
  * Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,6 +19,13 @@
 #include "gas_serv.h"
 
 
+static void convert_to_protected_dual(struct wpabuf *msg)
+{
+	u8 *categ = wpabuf_mhead_u8(msg);
+	*categ = WLAN_ACTION_PROTECTED_DUAL;
+}
+
+
 static struct gas_dialog_info *
 gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
 {
@@ -46,6 +53,8 @@
 		 * it to be that long.
 		 */
 		ap_sta_session_timeout(hapd, sta, 5);
+	} else {
+		ap_sta_replenish_timeout(hapd, sta, 5);
 	}
 
 	if (sta->gas_dialog == NULL) {
@@ -62,7 +71,6 @@
 			continue;
 		dia = &sta->gas_dialog[i];
 		dia->valid = 1;
-		dia->index = i;
 		dia->dialog_token = dialog_token;
 		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
 		return dia;
@@ -150,6 +158,10 @@
 		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
 	if (hapd->conf->hs20_operating_class)
 		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+	if (hapd->conf->hs20_osu_providers_count)
+		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+	if (hapd->conf->hs20_icons_count)
+		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
 	gas_anqp_set_element_len(buf, len);
 }
 #endif /* CONFIG_HS20 */
@@ -402,7 +414,7 @@
 			gas_anqp_set_element_len(buf, realm_data_len);
 		}
 		gas_anqp_set_element_len(buf, len);
-	} else if (nai_home_realm && hapd->conf->nai_realm_data) {
+	} else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
 		hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
 						home_realm_len);
 	}
@@ -505,18 +517,188 @@
 	}
 }
 
+
+static void anqp_add_osu_provider(struct wpabuf *buf,
+				  struct hostapd_bss_config *bss,
+				  struct hs20_osu_provider *p)
+{
+	u8 *len, *len2, *count;
+	unsigned int i;
+
+	len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
+
+	/* OSU Friendly Name Duples */
+	len2 = wpabuf_put(buf, 2);
+	for (i = 0; i < p->friendly_name_count; i++) {
+		struct hostapd_lang_string *s = &p->friendly_name[i];
+		wpabuf_put_u8(buf, 3 + s->name_len);
+		wpabuf_put_data(buf, s->lang, 3);
+		wpabuf_put_data(buf, s->name, s->name_len);
+	}
+	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+	/* OSU Server URI */
+	if (p->server_uri) {
+		wpabuf_put_u8(buf, os_strlen(p->server_uri));
+		wpabuf_put_str(buf, p->server_uri);
+	} else
+		wpabuf_put_u8(buf, 0);
+
+	/* OSU Method List */
+	count = wpabuf_put(buf, 1);
+	for (i = 0; p->method_list[i] >= 0; i++)
+		wpabuf_put_u8(buf, p->method_list[i]);
+	*count = i;
+
+	/* Icons Available */
+	len2 = wpabuf_put(buf, 2);
+	for (i = 0; i < p->icons_count; i++) {
+		size_t j;
+		struct hs20_icon *icon = NULL;
+
+		for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+			if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
+			    0)
+				icon = &bss->hs20_icons[j];
+		}
+		if (!icon)
+			continue; /* icon info not found */
+
+		wpabuf_put_le16(buf, icon->width);
+		wpabuf_put_le16(buf, icon->height);
+		wpabuf_put_data(buf, icon->language, 3);
+		wpabuf_put_u8(buf, os_strlen(icon->type));
+		wpabuf_put_str(buf, icon->type);
+		wpabuf_put_u8(buf, os_strlen(icon->name));
+		wpabuf_put_str(buf, icon->name);
+	}
+	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+	/* OSU_NAI */
+	if (p->osu_nai) {
+		wpabuf_put_u8(buf, os_strlen(p->osu_nai));
+		wpabuf_put_str(buf, p->osu_nai);
+	} else
+		wpabuf_put_u8(buf, 0);
+
+	/* OSU Service Description Duples */
+	len2 = wpabuf_put(buf, 2);
+	for (i = 0; i < p->service_desc_count; i++) {
+		struct hostapd_lang_string *s = &p->service_desc[i];
+		wpabuf_put_u8(buf, 3 + s->name_len);
+		wpabuf_put_data(buf, s->lang, 3);
+		wpabuf_put_data(buf, s->name, s->name_len);
+	}
+	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
+					struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_osu_providers_count) {
+		size_t i;
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+
+		/* OSU SSID */
+		wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
+		wpabuf_put_data(buf, hapd->conf->osu_ssid,
+				hapd->conf->osu_ssid_len);
+
+		/* Number of OSU Providers */
+		wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
+
+		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+			anqp_add_osu_provider(
+				buf, hapd->conf,
+				&hapd->conf->hs20_osu_providers[i]);
+		}
+
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+				      struct wpabuf *buf,
+				      const u8 *name, size_t name_len)
+{
+	struct hs20_icon *icon;
+	size_t i;
+	u8 *len;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
+			  name, name_len);
+	for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
+		icon = &hapd->conf->hs20_icons[i];
+		if (name_len == os_strlen(icon->name) &&
+		    os_memcmp(name, icon->name, name_len) == 0)
+			break;
+	}
+
+	if (i < hapd->conf->hs20_icons_count)
+		icon = &hapd->conf->hs20_icons[i];
+	else
+		icon = NULL;
+
+	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
+	wpabuf_put_u8(buf, 0); /* Reserved */
+
+	if (icon) {
+		char *data;
+		size_t data_len;
+
+		data = os_readfile(icon->file, &data_len);
+		if (data == NULL || data_len > 65535) {
+			wpabuf_put_u8(buf, 2); /* Download Status:
+						* Unspecified file error */
+			wpabuf_put_u8(buf, 0);
+			wpabuf_put_le16(buf, 0);
+		} else {
+			wpabuf_put_u8(buf, 0); /* Download Status: Success */
+			wpabuf_put_u8(buf, os_strlen(icon->type));
+			wpabuf_put_str(buf, icon->type);
+			wpabuf_put_le16(buf, data_len);
+			wpabuf_put_data(buf, data, data_len);
+		}
+		os_free(data);
+	} else {
+		wpabuf_put_u8(buf, 1); /* Download Status: File not found */
+		wpabuf_put_u8(buf, 0);
+		wpabuf_put_le16(buf, 0);
+	}
+
+	gas_anqp_set_element_len(buf, len);
+}
+
 #endif /* CONFIG_HS20 */
 
 
 static struct wpabuf *
 gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
 				unsigned int request,
-				struct gas_dialog_info *di,
-				const u8 *home_realm, size_t home_realm_len)
+				const u8 *home_realm, size_t home_realm_len,
+				const u8 *icon_name, size_t icon_name_len)
 {
 	struct wpabuf *buf;
+	size_t len;
 
-	buf = wpabuf_alloc(1400);
+	len = 1400;
+	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+		len += 1000;
+	if (request & ANQP_REQ_ICON_REQUEST)
+		len += 65536;
+
+	buf = wpabuf_alloc(len);
 	if (buf == NULL)
 		return NULL;
 
@@ -550,44 +732,31 @@
 		anqp_add_connection_capability(hapd, buf);
 	if (request & ANQP_REQ_OPERATING_CLASS)
 		anqp_add_operating_class(hapd, buf);
+	if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
+		anqp_add_osu_providers_list(hapd, buf);
+	if (request & ANQP_REQ_ICON_REQUEST)
+		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
 #endif /* CONFIG_HS20 */
 
 	return buf;
 }
 
 
-static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
-{
-	struct gas_dialog_info *dia = eloop_data;
-
-	wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
-		   "dialog token %d", dia->dialog_token);
-
-	gas_serv_dialog_clear(dia);
-}
-
-
 struct anqp_query_info {
 	unsigned int request;
-	unsigned int remote_request;
 	const u8 *home_realm_query;
 	size_t home_realm_query_len;
-	u16 remote_delay;
+	const u8 *icon_name;
+	size_t icon_name_len;
 };
 
 
 static void set_anqp_req(unsigned int bit, const char *name, int local,
-			 unsigned int remote, u16 remote_delay,
 			 struct anqp_query_info *qi)
 {
 	qi->request |= bit;
 	if (local) {
 		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
-	} else if (bit & remote) {
-		wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
-		qi->remote_request |= bit;
-		if (remote_delay > qi->remote_delay)
-			qi->remote_delay = remote_delay;
 	} else {
 		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
 	}
@@ -599,43 +768,38 @@
 {
 	switch (info_id) {
 	case ANQP_CAPABILITY_LIST:
-		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
-			     0, qi);
+		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
+			     qi);
 		break;
 	case ANQP_VENUE_NAME:
 		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
-			     hapd->conf->venue_name != NULL, 0, 0, qi);
+			     hapd->conf->venue_name != NULL, qi);
 		break;
 	case ANQP_NETWORK_AUTH_TYPE:
 		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
-			     hapd->conf->network_auth_type != NULL,
-			     0, 0, qi);
+			     hapd->conf->network_auth_type != NULL, qi);
 		break;
 	case ANQP_ROAMING_CONSORTIUM:
 		set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
-			     hapd->conf->roaming_consortium != NULL, 0, 0, qi);
+			     hapd->conf->roaming_consortium != NULL, qi);
 		break;
 	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
 		set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
 			     "IP Addr Type Availability",
-			     hapd->conf->ipaddr_type_configured,
-			     0, 0, qi);
+			     hapd->conf->ipaddr_type_configured, qi);
 		break;
 	case ANQP_NAI_REALM:
 		set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
-			     hapd->conf->nai_realm_data != NULL,
-			     0, 0, qi);
+			     hapd->conf->nai_realm_data != NULL, qi);
 		break;
 	case ANQP_3GPP_CELLULAR_NETWORK:
 		set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
 			     "3GPP Cellular Network",
-			     hapd->conf->anqp_3gpp_cell_net != NULL,
-			     0, 0, qi);
+			     hapd->conf->anqp_3gpp_cell_net != NULL, qi);
 		break;
 	case ANQP_DOMAIN_NAME:
 		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
-			     hapd->conf->domain_name != NULL,
-			     0, 0, qi);
+			     hapd->conf->domain_name != NULL, qi);
 		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
@@ -667,29 +831,30 @@
 	switch (subtype) {
 	case HS20_STYPE_CAPABILITY_LIST:
 		set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
-			     1, 0, 0, qi);
+			     1, qi);
 		break;
 	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
 		set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
 			     "Operator Friendly Name",
-			     hapd->conf->hs20_oper_friendly_name != NULL,
-			     0, 0, qi);
+			     hapd->conf->hs20_oper_friendly_name != NULL, qi);
 		break;
 	case HS20_STYPE_WAN_METRICS:
 		set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
-			     hapd->conf->hs20_wan_metrics != NULL,
-			     0, 0, qi);
+			     hapd->conf->hs20_wan_metrics != NULL, qi);
 		break;
 	case HS20_STYPE_CONNECTION_CAPABILITY:
 		set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
 			     "Connection Capability",
 			     hapd->conf->hs20_connection_capability != NULL,
-			     0, 0, qi);
+			     qi);
 		break;
 	case HS20_STYPE_OPERATING_CLASS:
 		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
-			     hapd->conf->hs20_operating_class != NULL,
-			     0, 0, qi);
+			     hapd->conf->hs20_operating_class != NULL, qi);
+		break;
+	case HS20_STYPE_OSU_PROVIDERS_LIST:
+		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+			     hapd->conf->hs20_osu_providers_count, qi);
 		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
@@ -716,6 +881,23 @@
 }
 
 
+static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
+				    const u8 *pos, const u8 *end,
+				    struct anqp_query_info *qi)
+{
+	qi->request |= ANQP_REQ_ICON_REQUEST;
+	qi->icon_name = pos;
+	qi->icon_name_len = end - pos;
+	if (hapd->conf->hs20_icons_count) {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
+			   "(local)");
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
+			   "available");
+	}
+}
+
+
 static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
 				    const u8 *pos, const u8 *end,
 				    struct anqp_query_info *qi)
@@ -760,6 +942,9 @@
 	case HS20_STYPE_NAI_HOME_REALM_QUERY:
 		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
 		break;
+	case HS20_STYPE_ICON_REQUEST:
+		rx_anqp_hs_icon_request(hapd, pos, end, qi);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
 			   "%u", subtype);
@@ -772,13 +957,14 @@
 
 static void gas_serv_req_local_processing(struct hostapd_data *hapd,
 					  const u8 *sa, u8 dialog_token,
-					  struct anqp_query_info *qi)
+					  struct anqp_query_info *qi, int prot)
 {
 	struct wpabuf *buf, *tx_buf;
 
-	buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
+	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
 					      qi->home_realm_query,
-					      qi->home_realm_query_len);
+					      qi->home_realm_query_len,
+					      qi->icon_name, qi->icon_name_len);
 	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
 			buf);
 	if (!buf)
@@ -802,13 +988,17 @@
 				   "for " MACSTR " (dialog token %u)",
 				   MAC2STR(sa), dialog_token);
 			wpabuf_free(buf);
-			return;
+			tx_buf = gas_anqp_build_initial_resp_buf(
+				dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+				0, NULL);
+		} else {
+			di->prot = prot;
+			di->sd_resp = buf;
+			di->sd_resp_pos = 0;
+			tx_buf = gas_anqp_build_initial_resp_buf(
+				dialog_token, WLAN_STATUS_SUCCESS,
+				comeback_delay, NULL);
 		}
-		di->sd_resp = buf;
-		di->sd_resp_pos = 0;
-		tx_buf = gas_anqp_build_initial_resp_buf(
-			dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
-			NULL);
 	} else {
 		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
 		tx_buf = gas_anqp_build_initial_resp_buf(
@@ -817,7 +1007,8 @@
 	}
 	if (!tx_buf)
 		return;
-
+	if (prot)
+		convert_to_protected_dual(tx_buf);
 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
 				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
 	wpabuf_free(tx_buf);
@@ -826,7 +1017,7 @@
 
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
 					const u8 *sa,
-					const u8 *data, size_t len)
+					const u8 *data, size_t len, int prot)
 {
 	const u8 *pos = data;
 	const u8 *end = data + len;
@@ -876,6 +1067,8 @@
 			return;
 		wpabuf_put_data(buf, adv_proto, 2 + slen);
 		wpabuf_put_le16(buf, 0); /* Query Response Length */
+		if (prot)
+			convert_to_protected_dual(buf);
 		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
 					wpabuf_head(buf), wpabuf_len(buf));
 		wpabuf_free(buf);
@@ -927,100 +1120,13 @@
 		pos += elen;
 	}
 
-	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
-}
-
-
-void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
-			      struct gas_dialog_info *dialog)
-{
-	struct wpabuf *buf, *tx_buf;
-	u8 dialog_token = dialog->dialog_token;
-	size_t frag_len;
-
-	if (dialog->sd_resp == NULL) {
-		buf = gas_serv_build_gas_resp_payload(hapd,
-						      dialog->all_requested,
-						      dialog, NULL, 0);
-		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
-			buf);
-		if (!buf)
-			goto tx_gas_response_done;
-		dialog->sd_resp = buf;
-		dialog->sd_resp_pos = 0;
-	}
-	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
-	if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
-	    hapd->conf->gas_comeback_delay) {
-		u16 comeback_delay_tus = dialog->comeback_delay +
-			GAS_SERV_COMEBACK_DELAY_FUDGE;
-		u32 comeback_delay_secs, comeback_delay_usecs;
-
-		if (hapd->conf->gas_comeback_delay) {
-			/* Testing - allow overriding of the delay value */
-			comeback_delay_tus = hapd->conf->gas_comeback_delay;
-		}
-
-		wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
-			   "%u) and comeback delay %u, "
-			   "requesting comebacks", (unsigned int) frag_len,
-			   (unsigned int) hapd->gas_frag_limit,
-			   dialog->comeback_delay);
-		tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
-							 WLAN_STATUS_SUCCESS,
-							 comeback_delay_tus,
-							 NULL);
-		if (tx_buf) {
-			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
-				"GAS: Tx GAS Initial Resp (comeback = 10TU)");
-			hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
-						dst,
-						wpabuf_head(tx_buf),
-						wpabuf_len(tx_buf));
-		}
-		wpabuf_free(tx_buf);
-
-		/* start a timer of 1.5 * comeback-delay */
-		comeback_delay_tus = comeback_delay_tus +
-			(comeback_delay_tus / 2);
-		comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
-		comeback_delay_usecs = (comeback_delay_tus * 1024) -
-			(comeback_delay_secs * 1000000);
-		eloop_register_timeout(comeback_delay_secs,
-				       comeback_delay_usecs,
-				       gas_serv_clear_cached_ies, dialog,
-				       NULL);
-		goto tx_gas_response_done;
-	}
-
-	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
-				dialog->sd_resp_pos, frag_len);
-	if (buf == NULL) {
-		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
-			"failed");
-		goto tx_gas_response_done;
-	}
-	tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
-						 WLAN_STATUS_SUCCESS, 0, buf);
-	wpabuf_free(buf);
-	if (tx_buf == NULL)
-		goto tx_gas_response_done;
-	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
-		"Response (frag_id %d frag_len %d)",
-		dialog->sd_frag_id, (int) frag_len);
-	dialog->sd_frag_id++;
-
-	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
-				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
-	wpabuf_free(tx_buf);
-tx_gas_response_done:
-	gas_serv_clear_cached_ies(dialog, NULL);
+	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
 }
 
 
 static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
 					 const u8 *sa,
-					 const u8 *data, size_t len)
+					 const u8 *data, size_t len, int prot)
 {
 	struct gas_dialog_info *dialog;
 	struct wpabuf *buf, *tx_buf;
@@ -1051,33 +1157,6 @@
 		goto send_resp;
 	}
 
-	if (dialog->sd_resp == NULL) {
-		wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
-			   dialog->requested, dialog->received);
-		if ((dialog->requested & dialog->received) !=
-		    dialog->requested) {
-			wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
-				   "from remote processing");
-			gas_serv_dialog_clear(dialog);
-			tx_buf = gas_anqp_build_comeback_resp_buf(
-				dialog_token,
-				WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
-				NULL);
-			if (tx_buf == NULL)
-				return;
-			goto send_resp;
-		}
-
-		buf = gas_serv_build_gas_resp_payload(hapd,
-						      dialog->all_requested,
-						      dialog, NULL, 0);
-		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
-			buf);
-		if (!buf)
-			goto rx_gas_comeback_req_done;
-		dialog->sd_resp = buf;
-		dialog->sd_resp_pos = 0;
-	}
 	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
 	if (frag_len > hapd->gas_frag_limit) {
 		frag_len = hapd->gas_frag_limit;
@@ -1090,15 +1169,18 @@
 	if (buf == NULL) {
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
 			"buffer");
-		goto rx_gas_comeback_req_done;
+		gas_serv_dialog_clear(dialog);
+		return;
 	}
 	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
 						  WLAN_STATUS_SUCCESS,
 						  dialog->sd_frag_id,
 						  more, 0, buf);
 	wpabuf_free(buf);
-	if (tx_buf == NULL)
-		goto rx_gas_comeback_req_done;
+	if (tx_buf == NULL) {
+		gas_serv_dialog_clear(dialog);
+		return;
+	}
 	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
 		"(frag_id %d more=%d frag_len=%d)",
 		dialog->sd_frag_id, more, (int) frag_len);
@@ -1118,13 +1200,11 @@
 	}
 
 send_resp:
+	if (prot)
+		convert_to_protected_dual(tx_buf);
 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
 				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
 	wpabuf_free(tx_buf);
-	return;
-
-rx_gas_comeback_req_done:
-	gas_serv_clear_cached_ies(dialog, NULL);
 }
 
 
@@ -1133,24 +1213,30 @@
 {
 	struct hostapd_data *hapd = ctx;
 	const struct ieee80211_mgmt *mgmt;
-	size_t hdr_len;
 	const u8 *sa, *data;
+	int prot;
 
 	mgmt = (const struct ieee80211_mgmt *) buf;
-	hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
-	if (hdr_len > len)
+	if (len < IEEE80211_HDRLEN + 2)
 		return;
-	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
 		return;
+	/*
+	 * Note: Public Action and Protected Dual of Public Action frames share
+	 * the same payload structure, so it is fine to use definitions of
+	 * Public Action frames to process both.
+	 */
+	prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
 	sa = mgmt->sa;
-	len -= hdr_len;
-	data = &mgmt->u.action.u.public_action.action;
+	len -= IEEE80211_HDRLEN + 1;
+	data = buf + IEEE80211_HDRLEN + 1;
 	switch (data[0]) {
 	case WLAN_PA_GAS_INITIAL_REQ:
-		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
+		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
 		break;
 	case WLAN_PA_GAS_COMEBACK_REQ:
-		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
+		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
 		break;
 	}
 }
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 4213cf6..4ec3201 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -1,6 +1,6 @@
 /*
  * Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -37,29 +37,22 @@
 	(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
 #define ANQP_REQ_OPERATING_CLASS \
 	(0x10000 << HS20_STYPE_OPERATING_CLASS)
-
-/* To account for latencies between hostapd and external ANQP processor */
-#define GAS_SERV_COMEBACK_DELAY_FUDGE 10
-#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */
+#define ANQP_REQ_OSU_PROVIDERS_LIST \
+	(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
+#define ANQP_REQ_ICON_REQUEST \
+	(0x10000 << HS20_STYPE_ICON_REQUEST)
 
 struct gas_dialog_info {
 	u8 valid;
-	u8 index;
 	struct wpabuf *sd_resp; /* Fragmented response */
 	u8 dialog_token;
 	size_t sd_resp_pos; /* Offset in sd_resp */
 	u8 sd_frag_id;
-	u16 comeback_delay;
-
-	unsigned int requested;
-	unsigned int received;
-	unsigned int all_requested;
+	int prot; /* whether Protected Dual of Public Action frame is used */
 };
 
 struct hostapd_data;
 
-void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
-			      struct gas_dialog_info *dialog);
 struct gas_dialog_info *
 gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
 		     u8 dialog_token);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index fd1ca2b..26aca2b 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,9 +11,10 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "radius/radius_client.h"
 #include "radius/radius_das.h"
-#include "drivers/driver.h"
+#include "eap_server/tncs.h"
 #include "hostapd.h"
 #include "authsrv.h"
 #include "sta_info.h"
@@ -32,14 +33,15 @@
 #include "ap_config.h"
 #include "p2p_hostapd.h"
 #include "gas_serv.h"
+#include "dfs.h"
+#include "ieee802_11.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
 static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
 static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
-
-extern int wpa_debug_level;
-extern struct wpa_driver_ops *wpa_drivers[];
+static int setup_interface2(struct hostapd_iface *iface);
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
 
 
 int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -61,10 +63,22 @@
 
 static void hostapd_reload_bss(struct hostapd_data *hapd)
 {
+	struct hostapd_ssid *ssid;
+
 #ifndef CONFIG_NO_RADIUS
 	radius_client_reconfig(hapd->radius, hapd->conf->radius);
 #endif /* CONFIG_NO_RADIUS */
 
+	ssid = &hapd->conf->ssid;
+	if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
+	    ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
+		/*
+		 * Force PSK to be derived again since SSID or passphrase may
+		 * have changed.
+		 */
+		os_free(ssid->wpa_psk);
+		ssid->wpa_psk = NULL;
+	}
 	if (hostapd_setup_wpa_psk(hapd->conf)) {
 		wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
 			   "after reloading configuration");
@@ -75,7 +89,7 @@
 	else
 		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
-	if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
+	if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
 		hostapd_setup_wpa(hapd);
 		if (hapd->wpa_auth)
 			wpa_init_keys(hapd->wpa_auth);
@@ -159,7 +173,18 @@
 	for (j = 0; j < iface->num_bss; j++) {
 		hapd = iface->bss[j];
 		hapd->iconf = newconf;
-		hapd->conf = &newconf->bss[j];
+		hapd->iconf->channel = oldconf->channel;
+		hapd->iconf->secondary_channel = oldconf->secondary_channel;
+		hapd->iconf->ieee80211n = oldconf->ieee80211n;
+		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;
+		hapd->conf = newconf->bss[j];
 		hostapd_reload_bss(hapd);
 	}
 
@@ -227,6 +252,14 @@
 
 static void hostapd_free_hapd_data(struct hostapd_data *hapd)
 {
+	if (!hapd->started) {
+		wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
+			   __func__, hapd->conf->iface);
+		return;
+	}
+	hapd->started = 0;
+
+	wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
 	iapp_deinit(hapd->iapp);
 	hapd->iapp = NULL;
 	accounting_deinit(hapd);
@@ -244,10 +277,21 @@
 
 	authsrv_deinit(hapd);
 
-	if (hapd->interface_added &&
-	    hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
-		wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
-			   hapd->conf->iface);
+	if (hapd->interface_added) {
+		hapd->interface_added = 0;
+		if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to remove BSS interface %s",
+				   hapd->conf->iface);
+			hapd->interface_added = 1;
+		} else {
+			/*
+			 * Since this was a dynamically added interface, the
+			 * driver wrapper may have removed its internal instance
+			 * and hapd->drv_priv is not valid anymore.
+			 */
+			hapd->drv_priv = NULL;
+		}
 	}
 
 	os_free(hapd->probereq_cb);
@@ -267,8 +311,10 @@
 #endif /* CONFIG_INTERWORKING */
 
 #ifdef CONFIG_SQLITE
-	os_free(hapd->tmp_eap_user.identity);
-	os_free(hapd->tmp_eap_user.password);
+	bin_clear_free(hapd->tmp_eap_user.identity,
+		       hapd->tmp_eap_user.identity_len);
+	bin_clear_free(hapd->tmp_eap_user.password,
+		       hapd->tmp_eap_user.password_len);
 #endif /* CONFIG_SQLITE */
 }
 
@@ -278,13 +324,13 @@
  * @hapd: Pointer to BSS data
  *
  * This function is used to free all per-BSS data structures and resources.
- * This gets called in a loop for each BSS between calls to
- * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
- * is deinitialized. Most of the modules that are initialized in
- * hostapd_setup_bss() are deinitialized here.
+ * Most of the modules that are initialized in hostapd_setup_bss() are
+ * deinitialized here.
  */
 static void hostapd_cleanup(struct hostapd_data *hapd)
 {
+	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
+		   hapd->conf->iface);
 	if (hapd->iface->interfaces &&
 	    hapd->iface->interfaces->ctrl_iface_deinit)
 		hapd->iface->interfaces->ctrl_iface_deinit(hapd);
@@ -292,20 +338,9 @@
 }
 
 
-/**
- * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup
- * @iface: Pointer to interface data
- *
- * This function is called before per-BSS data structures are deinitialized
- * with hostapd_cleanup().
- */
-static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
-{
-}
-
-
 static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
 {
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = NULL;
 	os_free(iface->current_rates);
@@ -325,19 +360,23 @@
  */
 static void hostapd_cleanup_iface(struct hostapd_iface *iface)
 {
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+
 	hostapd_cleanup_iface_partial(iface);
 	hostapd_config_free(iface->conf);
 	iface->conf = NULL;
 
 	os_free(iface->config_fname);
 	os_free(iface->bss);
+	wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface);
 	os_free(iface);
 }
 
 
 static void hostapd_clear_wep(struct hostapd_data *hapd)
 {
-	if (hapd->drv_priv) {
+	if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
 		hostapd_set_privacy(hapd, 0);
 		hostapd_broadcast_wep_clear(hapd);
 	}
@@ -388,11 +427,15 @@
 	if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
 		return 0;
 
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries");
-	if (hostapd_flush(hapd)) {
-		wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to "
-			"kernel driver");
-		ret = -1;
+	if (!hapd->iface->driver_ap_teardown) {
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Flushing old station entries");
+
+		if (hostapd_flush(hapd)) {
+			wpa_msg(hapd->msg_ctx, MSG_WARNING,
+				"Could not connect to kernel driver");
+			ret = -1;
+		}
 	}
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
 	os_memset(addr, 0xff, ETH_ALEN);
@@ -403,6 +446,14 @@
 }
 
 
+static void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+{
+	hostapd_free_stas(hapd);
+	hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+	hostapd_clear_wep(hapd);
+}
+
+
 /**
  * hostapd_validate_bssid_configuration - Validate BSSID configuration
  * @iface: Pointer to interface data
@@ -429,7 +480,7 @@
 	/* Determine the bits necessary to any configured BSSIDs,
 	   if they are higher than the number of BSSIDs. */
 	for (j = 0; j < iface->conf->num_bss; j++) {
-		if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) {
+		if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) {
 			if (j)
 				auto_addr++;
 			continue;
@@ -437,7 +488,7 @@
 
 		for (i = 0; i < ETH_ALEN; i++) {
 			mask[i] |=
-				iface->conf->bss[j].bssid[i] ^
+				iface->conf->bss[j]->bssid[i] ^
 				hapd->own_addr[i];
 		}
 	}
@@ -502,7 +553,7 @@
 	size_t i;
 
 	for (i = 0; i < conf->num_bss; i++) {
-		if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) {
+		if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
 			return 1;
 		}
 	}
@@ -516,7 +567,34 @@
 static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
 				    struct radius_das_attrs *attr)
 {
-	/* TODO */
+	if (attr->nas_identifier &&
+	    (!hapd->conf->nas_identifier ||
+	     os_strlen(hapd->conf->nas_identifier) !=
+	     attr->nas_identifier_len ||
+	     os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier,
+		       attr->nas_identifier_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch");
+		return 1;
+	}
+
+	if (attr->nas_ip_addr &&
+	    (hapd->conf->own_ip_addr.af != AF_INET ||
+	     os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) !=
+	     0)) {
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch");
+		return 1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (attr->nas_ipv6_addr &&
+	    (hapd->conf->own_ip_addr.af != AF_INET6 ||
+	     os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16)
+	     != 0)) {
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch");
+		return 1;
+	}
+#endif /* CONFIG_IPV6 */
+
 	return 0;
 }
 
@@ -583,6 +661,8 @@
 	if (sta == NULL)
 		return RADIUS_DAS_SESSION_NOT_FOUND;
 
+	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
 	hostapd_drv_sta_deauth(hapd, sta->addr,
 			       WLAN_REASON_PREV_AUTH_NOT_VALID);
 	ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -596,7 +676,8 @@
 /**
  * hostapd_setup_bss - Per-BSS setup (initialization)
  * @hapd: Pointer to BSS data
- * @first: Whether this BSS is the first BSS of an interface
+ * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+ *	but interface may exist
  *
  * This function is used to initialize all per-BSS data structures and
  * resources. This gets called in a loop for each BSS when an interface is
@@ -611,7 +692,24 @@
 	char force_ifname[IFNAMSIZ];
 	u8 if_addr[ETH_ALEN];
 
-	if (!first) {
+	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+		   __func__, hapd, hapd->conf->iface, first);
+
+#ifdef EAP_SERVER_TNC
+	if (hapd->conf->tnc && tncs_global_init() < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
+		return -1;
+	}
+#endif /* EAP_SERVER_TNC */
+
+	if (hapd->started) {
+		wpa_printf(MSG_ERROR, "%s: Interface %s was already started",
+			   __func__, hapd->conf->iface);
+		return -1;
+	}
+	hapd->started = 1;
+
+	if (!first || first == -1) {
 		if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) {
 			/* Allocate the next available BSSID. */
 			do {
@@ -636,9 +734,10 @@
 				   hapd->conf->iface, hapd->own_addr, hapd,
 				   &hapd->drv_priv, force_ifname, if_addr,
 				   hapd->conf->bridge[0] ? hapd->conf->bridge :
-				   NULL)) {
+				   NULL, first == -1)) {
 			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
 				   MACSTR ")", MAC2STR(hapd->own_addr));
+			hapd->interface_added = 0;
 			return -1;
 		}
 	}
@@ -702,7 +801,7 @@
 		return -1;
 	}
 
-	if (wpa_debug_level == MSG_MSGDUMP)
+	if (wpa_debug_level <= MSG_MSGDUMP)
 		conf->radius->msg_dumps = 1;
 #ifndef CONFIG_NO_RADIUS
 	hapd->radius = radius_client_init(hapd, conf->radius);
@@ -748,7 +847,7 @@
 		return -1;
 	}
 
-	if (hapd->conf->wpa && hostapd_setup_wpa(hapd))
+	if ((hapd->conf->wpa || hapd->conf->osen) && hostapd_setup_wpa(hapd))
 		return -1;
 
 	if (accounting_init(hapd)) {
@@ -768,22 +867,22 @@
 		wpa_printf(MSG_ERROR, "GAS server initialization failed");
 		return -1;
 	}
-#endif /* CONFIG_INTERWORKING */
 
-	if (hapd->iface->interfaces &&
-	    hapd->iface->interfaces->ctrl_iface_init &&
-	    hapd->iface->interfaces->ctrl_iface_init(hapd)) {
-		wpa_printf(MSG_ERROR, "Failed to setup control interface");
+	if (conf->qos_map_set_len &&
+	    hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
+				    conf->qos_map_set_len)) {
+		wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
 		return -1;
 	}
+#endif /* CONFIG_INTERWORKING */
 
 	if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
 		wpa_printf(MSG_ERROR, "VLAN initialization failed.");
 		return -1;
 	}
 
-	if (!hapd->conf->start_disabled)
-		ieee802_11_set_beacon(hapd);
+	if (!hapd->conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+		return -1;
 
 	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
 		return -1;
@@ -849,44 +948,117 @@
 
 	if (hapd->iface->drv_max_acl_mac_addrs == 0)
 		return;
-	if (!(conf->bss->num_accept_mac || conf->bss->num_deny_mac))
-		return;
 
-	if (conf->bss->macaddr_acl == DENY_UNLESS_ACCEPTED) {
-		if (conf->bss->num_accept_mac) {
-			accept_acl = 1;
-			err = hostapd_set_acl_list(hapd, conf->bss->accept_mac,
-						   conf->bss->num_accept_mac,
-						   accept_acl);
-			if (err) {
-				wpa_printf(MSG_DEBUG, "Failed to set accept acl");
-				return;
-			}
-		} else {
-			wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file");
+	if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+		accept_acl = 1;
+		err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
+					   conf->bss[0]->num_accept_mac,
+					   accept_acl);
+		if (err) {
+			wpa_printf(MSG_DEBUG, "Failed to set accept acl");
+			return;
 		}
-	} else if (conf->bss->macaddr_acl == ACCEPT_UNLESS_DENIED) {
-		if (conf->bss->num_deny_mac) {
-			accept_acl = 0;
-			err = hostapd_set_acl_list(hapd, conf->bss->deny_mac,
-						   conf->bss->num_deny_mac,
-						   accept_acl);
-			if (err) {
-				wpa_printf(MSG_DEBUG, "Failed to set deny acl");
-				return;
-			}
-		} else {
-			wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file");
+	} else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+		accept_acl = 0;
+		err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
+					   conf->bss[0]->num_deny_mac,
+					   accept_acl);
+		if (err) {
+			wpa_printf(MSG_DEBUG, "Failed to set deny acl");
+			return;
 		}
 	}
 }
 
 
+static int start_ctrl_iface_bss(struct hostapd_data *hapd)
+{
+	if (!hapd->iface->interfaces ||
+	    !hapd->iface->interfaces->ctrl_iface_init)
+		return 0;
+
+	if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to setup control interface for %s",
+			   hapd->conf->iface);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int start_ctrl_iface(struct hostapd_iface *iface)
+{
+	size_t i;
+
+	if (!iface->interfaces || !iface->interfaces->ctrl_iface_init)
+		return 0;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+		if (iface->interfaces->ctrl_iface_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to setup control interface for %s",
+				   hapd->conf->iface);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_iface *iface = eloop_ctx;
+
+	if (!iface->wait_channel_update) {
+		wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it");
+		return;
+	}
+
+	/*
+	 * It is possible that the existing channel list is acceptable, so try
+	 * to proceed.
+	 */
+	wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway");
+	setup_interface2(iface);
+}
+
+
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator)
+{
+	if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Channel list updated - continue setup");
+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+	setup_interface2(iface);
+}
+
+
 static int setup_interface(struct hostapd_iface *iface)
 {
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t i;
-	char country[4];
+
+	/*
+	 * It is possible that setup_interface() is called after the interface
+	 * was disabled etc., in which case driver_ap_teardown is possibly set
+	 * to 1. Clear it here so any other key/station deletion, which is not
+	 * part of a teardown flow, would also call the relevant driver
+	 * callbacks.
+	 */
+	iface->driver_ap_teardown = 0;
+
+	if (!iface->phy[0]) {
+		const char *phy = hostapd_drv_get_radio_name(hapd);
+		if (phy) {
+			wpa_printf(MSG_DEBUG, "phy: %s", phy);
+			os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+		}
+	}
 
 	/*
 	 * Make sure that all BSSes get configured with a pointer to the same
@@ -900,15 +1072,49 @@
 	if (hostapd_validate_bssid_configuration(iface))
 		return -1;
 
+	/*
+	 * Initialize control interfaces early to allow external monitoring of
+	 * channel setup operations that may take considerable amount of time
+	 * especially for DFS cases.
+	 */
+	if (start_ctrl_iface(iface))
+		return -1;
+
 	if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
+		char country[4], previous_country[4];
+
+		hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE);
+		if (hostapd_get_country(hapd, previous_country) < 0)
+			previous_country[0] = '\0';
+
 		os_memcpy(country, hapd->iconf->country, 3);
 		country[3] = '\0';
 		if (hostapd_set_country(hapd, country) < 0) {
 			wpa_printf(MSG_ERROR, "Failed to set country code");
 			return -1;
 		}
+
+		wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s",
+			   previous_country, country);
+
+		if (os_strncmp(previous_country, country, 2) != 0) {
+			wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update");
+			iface->wait_channel_update = 1;
+			eloop_register_timeout(5, 0,
+					       channel_list_update_timeout,
+					       iface, NULL);
+			return 0;
+		}
 	}
 
+	return setup_interface2(iface);
+}
+
+
+static int setup_interface2(struct hostapd_iface *iface)
+{
+	iface->wait_channel_update = 0;
+
 	if (hostapd_get_hw_features(iface)) {
 		/* Not all drivers support this yet, so continue without hw
 		 * feature data. */
@@ -917,7 +1123,7 @@
 		if (ret < 0) {
 			wpa_printf(MSG_ERROR, "Could not select hw_mode and "
 				   "channel. (%d)", ret);
-			return -1;
+			goto fail;
 		}
 		if (ret == 1) {
 			wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
@@ -925,36 +1131,65 @@
 		}
 		ret = hostapd_check_ht_capab(iface);
 		if (ret < 0)
-			return -1;
+			goto fail;
 		if (ret == 1) {
 			wpa_printf(MSG_DEBUG, "Interface initialization will "
 				   "be completed in a callback");
 			return 0;
 		}
+
+		if (iface->conf->ieee80211h)
+			wpa_printf(MSG_DEBUG, "DFS support is enabled");
 	}
 	return hostapd_setup_interface_complete(iface, 0);
+
+fail:
+	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+	if (iface->interfaces && iface->interfaces->terminate_on_error)
+		eloop_terminate();
+	return -1;
 }
 
 
+/**
+ * hostapd_setup_interface_complete - Complete interface setup
+ *
+ * This function is called when previous steps in the interface setup has been
+ * completed. This can also start operations, e.g., DFS, that will require
+ * additional processing before interface is ready to be enabled. Such
+ * operations will call this function from eloop callbacks when finished.
+ */
 int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
 {
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t j;
 	u8 *prev_addr;
 
-	if (err) {
-		wpa_printf(MSG_ERROR, "Interface initialization failed");
-		eloop_terminate();
-		return -1;
-	}
+	if (err)
+		goto fail;
 
 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
-	if (hapd->iconf->channel) {
-		iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
+	if (iface->conf->channel) {
+#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(hapd->iconf->hw_mode),
-			   hapd->iconf->channel, iface->freq);
+			   hostapd_hw_mode_txt(iface->conf->hw_mode),
+			   iface->conf->channel, iface->freq);
+
+#ifdef NEED_AP_MLME
+		/* Check DFS */
+		res = hostapd_handle_dfs(iface);
+		if (res <= 0) {
+			if (res < 0)
+				goto fail;
+			return res;
+		}
+#endif /* NEED_AP_MLME */
 
 		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
@@ -966,7 +1201,7 @@
 				     hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
 			wpa_printf(MSG_ERROR, "Could not set channel for "
 				   "kernel driver");
-			return -1;
+			goto fail;
 		}
 	}
 
@@ -977,7 +1212,7 @@
 			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_WARNING,
 				       "Failed to prepare rates table.");
-			return -1;
+			goto fail;
 		}
 	}
 
@@ -985,14 +1220,14 @@
 	    hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
 		wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
 			   "kernel driver");
-		return -1;
+		goto fail;
 	}
 
 	if (hapd->iconf->fragm_threshold > -1 &&
 	    hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) {
 		wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
 			   "for kernel driver");
-		return -1;
+		goto fail;
 	}
 
 	prev_addr = hapd->own_addr;
@@ -1001,11 +1236,18 @@
 		hapd = iface->bss[j];
 		if (j)
 			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
-		if (hostapd_setup_bss(hapd, j == 0))
-			return -1;
+		if (hostapd_setup_bss(hapd, j == 0)) {
+			do {
+				hapd = iface->bss[j];
+				hostapd_bss_deinit_no_free(hapd);
+				hostapd_free_hapd_data(hapd);
+			} while (j-- > 0);
+			goto fail;
+		}
 		if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
 			prev_addr = hapd->own_addr;
 	}
+	hapd = iface->bss[0];
 
 	hostapd_tx_queue_params(iface);
 
@@ -1016,7 +1258,7 @@
 	if (hostapd_driver_commit(hapd) < 0) {
 		wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
 			   "configuration", __func__);
-		return -1;
+		goto fail;
 	}
 
 	/*
@@ -1027,16 +1269,28 @@
 	 */
 	for (j = 0; j < iface->num_bss; j++) {
 		if (hostapd_init_wps_complete(iface->bss[j]))
-			return -1;
+			goto fail;
 	}
 
+	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+	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);
 
 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
 		   iface->bss[0]->conf->iface);
+	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+		iface->interfaces->terminate_on_error--;
 
 	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Interface initialization failed");
+	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+	if (iface->interfaces && iface->interfaces->terminate_on_error)
+		eloop_terminate();
+	return -1;
 }
 
 
@@ -1049,6 +1303,12 @@
  * and sets driver parameters based on the configuration.
  * Flushes old stations, sets the channel, encryption,
  * beacons, and WDS links based on the configuration.
+ *
+ * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS,
+ * or DFS operations, this function returns 0 before such operations have been
+ * completed. The pending operations are registered into eloop and will be
+ * completed from eloop callbacks. Those callbacks end up calling
+ * hostapd_setup_interface_complete() once setup has been completed.
  */
 int hostapd_setup_interface(struct hostapd_iface *iface)
 {
@@ -1098,68 +1358,324 @@
 }
 
 
+static void hostapd_bss_deinit(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
+		   hapd->conf->iface);
+	hostapd_bss_deinit_no_free(hapd);
+	hostapd_cleanup(hapd);
+}
+
+
 void hostapd_interface_deinit(struct hostapd_iface *iface)
 {
-	size_t j;
+	int j;
 
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
 	if (iface == NULL)
 		return;
 
-	hostapd_cleanup_iface_pre(iface);
-	for (j = 0; j < iface->num_bss; j++) {
-		struct hostapd_data *hapd = iface->bss[j];
-		hostapd_free_stas(hapd);
-		hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
-		hostapd_clear_wep(hapd);
-		hostapd_cleanup(hapd);
-	}
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+	hostapd_stop_setup_timers(iface);
+	eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+	iface->wait_channel_update = 0;
+
+	for (j = iface->num_bss - 1; j >= 0; j--)
+		hostapd_bss_deinit(iface->bss[j]);
 }
 
 
 void hostapd_interface_free(struct hostapd_iface *iface)
 {
 	size_t j;
-	for (j = 0; j < iface->num_bss; j++)
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+	for (j = 0; j < iface->num_bss; j++) {
+		wpa_printf(MSG_DEBUG, "%s: free hapd %p",
+			   __func__, iface->bss[j]);
 		os_free(iface->bss[j]);
+	}
 	hostapd_cleanup_iface(iface);
 }
 
 
-#ifdef HOSTAPD
+/**
+ * hostapd_init - Allocate and initialize per-interface data
+ * @config_file: Path to the configuration file
+ * Returns: Pointer to the allocated interface data or %NULL on failure
+ *
+ * This function is used to allocate main data structures for per-interface
+ * data. The allocated data buffer will be freed by calling
+ * hostapd_cleanup_iface().
+ */
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+				    const char *config_file)
+{
+	struct hostapd_iface *hapd_iface = NULL;
+	struct hostapd_config *conf = NULL;
+	struct hostapd_data *hapd;
+	size_t i;
+
+	hapd_iface = os_zalloc(sizeof(*hapd_iface));
+	if (hapd_iface == NULL)
+		goto fail;
+
+	hapd_iface->config_fname = os_strdup(config_file);
+	if (hapd_iface->config_fname == NULL)
+		goto fail;
+
+	conf = interfaces->config_read_cb(hapd_iface->config_fname);
+	if (conf == NULL)
+		goto fail;
+	hapd_iface->conf = conf;
+
+	hapd_iface->num_bss = conf->num_bss;
+	hapd_iface->bss = os_calloc(conf->num_bss,
+				    sizeof(struct hostapd_data *));
+	if (hapd_iface->bss == NULL)
+		goto fail;
+
+	for (i = 0; i < conf->num_bss; i++) {
+		hapd = hapd_iface->bss[i] =
+			hostapd_alloc_bss_data(hapd_iface, conf,
+					       conf->bss[i]);
+		if (hapd == NULL)
+			goto fail;
+		hapd->msg_ctx = hapd;
+	}
+
+	return hapd_iface;
+
+fail:
+	wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
+		   config_file);
+	if (conf)
+		hostapd_config_free(conf);
+	if (hapd_iface) {
+		os_free(hapd_iface->config_fname);
+		os_free(hapd_iface->bss);
+		wpa_printf(MSG_DEBUG, "%s: free iface %p",
+			   __func__, hapd_iface);
+		os_free(hapd_iface);
+	}
+	return NULL;
+}
+
+
+static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname)
+{
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_iface *iface = interfaces->iface[i];
+		for (j = 0; j < iface->num_bss; j++) {
+			struct hostapd_data *hapd = iface->bss[j];
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * hostapd_interface_init_bss - Read configuration file and init BSS data
+ *
+ * This function is used to parse configuration file for a BSS. This BSS is
+ * added to an existing interface sharing the same radio (if any) or a new
+ * interface is created if this is the first interface on a radio. This
+ * allocate memory for the BSS. No actual driver operations are started.
+ *
+ * This is similar to hostapd_interface_init(), but for a case where the
+ * configuration is used to add a single BSS instead of all BSSes for a radio.
+ */
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+			   const char *config_fname, int debug)
+{
+	struct hostapd_iface *new_iface = NULL, *iface = NULL;
+	struct hostapd_data *hapd;
+	int k;
+	size_t i, bss_idx;
+
+	if (!phy || !*phy)
+		return NULL;
+
+	for (i = 0; i < interfaces->count; i++) {
+		if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
+			iface = interfaces->iface[i];
+			break;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
+		   config_fname, phy, iface ? "" : " --> new PHY");
+	if (iface) {
+		struct hostapd_config *conf;
+		struct hostapd_bss_config **tmp_conf;
+		struct hostapd_data **tmp_bss;
+		struct hostapd_bss_config *bss;
+		const char *ifname;
+
+		/* Add new BSS to existing iface */
+		conf = interfaces->config_read_cb(config_fname);
+		if (conf == NULL)
+			return NULL;
+		if (conf->num_bss > 1) {
+			wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
+			hostapd_config_free(conf);
+			return NULL;
+		}
+
+		ifname = conf->bss[0]->iface;
+		if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
+			wpa_printf(MSG_ERROR,
+				   "Interface name %s already in use", ifname);
+			hostapd_config_free(conf);
+			return NULL;
+		}
+
+		tmp_conf = os_realloc_array(
+			iface->conf->bss, iface->conf->num_bss + 1,
+			sizeof(struct hostapd_bss_config *));
+		tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+					   sizeof(struct hostapd_data *));
+		if (tmp_bss)
+			iface->bss = tmp_bss;
+		if (tmp_conf) {
+			iface->conf->bss = tmp_conf;
+			iface->conf->last_bss = tmp_conf[0];
+		}
+		if (tmp_bss == NULL || tmp_conf == NULL) {
+			hostapd_config_free(conf);
+			return NULL;
+		}
+		bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0];
+		iface->conf->num_bss++;
+
+		hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+		if (hapd == NULL) {
+			iface->conf->num_bss--;
+			hostapd_config_free(conf);
+			return NULL;
+		}
+		iface->conf->last_bss = bss;
+		iface->bss[iface->num_bss] = hapd;
+		hapd->msg_ctx = hapd;
+
+		bss_idx = iface->num_bss++;
+		conf->num_bss--;
+		conf->bss[0] = NULL;
+		hostapd_config_free(conf);
+	} else {
+		/* Add a new iface with the first BSS */
+		new_iface = iface = hostapd_init(interfaces, config_fname);
+		if (!iface)
+			return NULL;
+		os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+		iface->interfaces = interfaces;
+		bss_idx = 0;
+	}
+
+	for (k = 0; k < debug; k++) {
+		if (iface->bss[bss_idx]->conf->logger_stdout_level > 0)
+			iface->bss[bss_idx]->conf->logger_stdout_level--;
+	}
+
+	if (iface->conf->bss[bss_idx]->iface[0] == '\0' &&
+	    !hostapd_drv_none(iface->bss[bss_idx])) {
+		wpa_printf(MSG_ERROR, "Interface name not specified in %s",
+			   config_fname);
+		if (new_iface)
+			hostapd_interface_deinit_free(new_iface);
+		return NULL;
+	}
+
+	return iface;
+}
+
 
 void hostapd_interface_deinit_free(struct hostapd_iface *iface)
 {
 	const struct wpa_driver_ops *driver;
 	void *drv_priv;
+
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
 	if (iface == NULL)
 		return;
+	wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u",
+		   __func__, (unsigned int) iface->num_bss,
+		   (unsigned int) iface->conf->num_bss);
 	driver = iface->bss[0]->driver;
 	drv_priv = iface->bss[0]->drv_priv;
 	hostapd_interface_deinit(iface);
-	if (driver && driver->hapd_deinit && drv_priv)
+	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+		   __func__, driver, drv_priv);
+	if (driver && driver->hapd_deinit && drv_priv) {
 		driver->hapd_deinit(drv_priv);
+		iface->bss[0]->drv_priv = NULL;
+	}
 	hostapd_interface_free(iface);
 }
 
 
+static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
+				  void *drv_priv,
+				  struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+
+	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+		   __func__, driver, drv_priv);
+	if (driver && driver->hapd_deinit && drv_priv) {
+		driver->hapd_deinit(drv_priv);
+		for (j = 0; j < hapd_iface->num_bss; j++) {
+			wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
+				   __func__, (int) j,
+				   hapd_iface->bss[j]->drv_priv);
+			if (hapd_iface->bss[j]->drv_priv == drv_priv)
+				hapd_iface->bss[j]->drv_priv = NULL;
+		}
+	}
+}
+
+
 int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
 {
+	size_t j;
+
 	if (hapd_iface->bss[0]->drv_priv != NULL) {
 		wpa_printf(MSG_ERROR, "Interface %s already enabled",
-			   hapd_iface->conf->bss[0].iface);
+			   hapd_iface->conf->bss[0]->iface);
 		return -1;
 	}
 
 	wpa_printf(MSG_DEBUG, "Enable interface %s",
-		   hapd_iface->conf->bss[0].iface);
+		   hapd_iface->conf->bss[0]->iface);
+
+	for (j = 0; j < hapd_iface->num_bss; j++)
+		hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+	if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+		wpa_printf(MSG_INFO, "Invalid configuration - cannot enable");
+		return -1;
+	}
 
 	if (hapd_iface->interfaces == NULL ||
 	    hapd_iface->interfaces->driver_init == NULL ||
-	    hapd_iface->interfaces->driver_init(hapd_iface) ||
-	    hostapd_setup_interface(hapd_iface)) {
-		hostapd_interface_deinit_free(hapd_iface);
+	    hapd_iface->interfaces->driver_init(hapd_iface))
+		return -1;
+
+	if (hostapd_setup_interface(hapd_iface)) {
+		hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+				      hapd_iface->bss[0]->drv_priv,
+				      hapd_iface);
 		return -1;
 	}
+
 	return 0;
 }
 
@@ -1169,19 +1685,17 @@
 	size_t j;
 
 	wpa_printf(MSG_DEBUG, "Reload interface %s",
-		   hapd_iface->conf->bss[0].iface);
-	for (j = 0; j < hapd_iface->num_bss; j++) {
-		hostapd_flush_old_stations(hapd_iface->bss[j],
-					   WLAN_REASON_PREV_AUTH_NOT_VALID);
-
-#ifndef CONFIG_NO_RADIUS
-		/* TODO: update dynamic data based on changed configuration
-		 * items (e.g., open/close sockets, etc.) */
-		radius_client_flush(hapd_iface->bss[j]->radius, 0);
-#endif  /* CONFIG_NO_RADIUS */
-
-		hostapd_reload_bss(hapd_iface->bss[j]);
+		   hapd_iface->conf->bss[0]->iface);
+	for (j = 0; j < hapd_iface->num_bss; j++)
+		hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+	if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+		wpa_printf(MSG_ERROR, "Updated configuration is invalid");
+		return -1;
 	}
+	hostapd_clear_old(hapd_iface);
+	for (j = 0; j < hapd_iface->num_bss; j++)
+		hostapd_reload_bss(hapd_iface->bss[j]);
+
 	return 0;
 }
 
@@ -1189,39 +1703,43 @@
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
 {
 	size_t j;
-	struct hostapd_bss_config *bss;
 	const struct wpa_driver_ops *driver;
 	void *drv_priv;
 
 	if (hapd_iface == NULL)
 		return -1;
-	bss = hapd_iface->bss[0]->conf;
+
+	if (hapd_iface->bss[0]->drv_priv == NULL) {
+		wpa_printf(MSG_INFO, "Interface %s already disabled",
+			   hapd_iface->conf->bss[0]->iface);
+		return -1;
+	}
+
+	wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
 	driver = hapd_iface->bss[0]->driver;
 	drv_priv = hapd_iface->bss[0]->drv_priv;
 
-	/* whatever hostapd_interface_deinit does */
+	hapd_iface->driver_ap_teardown =
+		!!(hapd_iface->drv_flags &
+		   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+	/* same as hostapd_interface_deinit without deinitializing ctrl-iface */
 	for (j = 0; j < hapd_iface->num_bss; j++) {
 		struct hostapd_data *hapd = hapd_iface->bss[j];
-		hostapd_free_stas(hapd);
-		hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
-		hostapd_clear_wep(hapd);
+		hostapd_bss_deinit_no_free(hapd);
 		hostapd_free_hapd_data(hapd);
 	}
 
-	if (driver && driver->hapd_deinit && drv_priv) {
-		driver->hapd_deinit(drv_priv);
-		hapd_iface->bss[0]->drv_priv = NULL;
-	}
+	hostapd_deinit_driver(driver, drv_priv, hapd_iface);
 
 	/* From hostapd_cleanup_iface: These were initialized in
 	 * hostapd_setup_interface and hostapd_setup_interface_complete
 	 */
 	hostapd_cleanup_iface_partial(hapd_iface);
-	bss->wpa = 0;
-	bss->wpa_key_mgmt = -1;
-	bss->wpa_pairwise = -1;
 
-	wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface);
+	wpa_printf(MSG_DEBUG, "Interface %s disabled",
+		   hapd_iface->bss[0]->conf->iface);
+	hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
 	return 0;
 }
 
@@ -1272,7 +1790,7 @@
 		return NULL;
 	}
 
-	bss = conf->last_bss = conf->bss;
+	bss = conf->last_bss = conf->bss[0];
 
 	os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
 	bss->ctrl_interface = os_strdup(ctrl_iface);
@@ -1307,8 +1825,7 @@
 
 	for (i = 0; i < conf->num_bss; i++) {
 		hapd = hapd_iface->bss[i] =
-			hostapd_alloc_bss_data(hapd_iface, conf,
-					       &conf->bss[i]);
+			hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
 		if (hapd == NULL)
 			return NULL;
 		hapd->msg_ctx = hapd;
@@ -1323,17 +1840,85 @@
 int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
 {
 	struct hostapd_config *conf = NULL;
-	struct hostapd_iface *hapd_iface = NULL;
+	struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
+	struct hostapd_data *hapd;
 	char *ptr;
-	size_t i;
+	size_t i, j;
+	const char *conf_file = NULL, *phy_name = NULL;
+
+	if (os_strncmp(buf, "bss_config=", 11) == 0) {
+		char *pos;
+		phy_name = buf + 11;
+		pos = os_strchr(phy_name, ':');
+		if (!pos)
+			return -1;
+		*pos++ = '\0';
+		conf_file = pos;
+		if (!os_strlen(conf_file))
+			return -1;
+
+		hapd_iface = hostapd_interface_init_bss(interfaces, phy_name,
+							conf_file, 0);
+		if (!hapd_iface)
+			return -1;
+		for (j = 0; j < interfaces->count; j++) {
+			if (interfaces->iface[j] == hapd_iface)
+				break;
+		}
+		if (j == interfaces->count) {
+			struct hostapd_iface **tmp;
+			tmp = os_realloc_array(interfaces->iface,
+					       interfaces->count + 1,
+					       sizeof(struct hostapd_iface *));
+			if (!tmp) {
+				hostapd_interface_deinit_free(hapd_iface);
+				return -1;
+			}
+			interfaces->iface = tmp;
+			interfaces->iface[interfaces->count++] = hapd_iface;
+			new_iface = hapd_iface;
+		}
+
+		if (new_iface) {
+			if (interfaces->driver_init(hapd_iface) ||
+			    hostapd_setup_interface(hapd_iface)) {
+				interfaces->count--;
+				goto fail;
+			}
+		} else {
+			/* Assign new BSS with bss[0]'s driver info */
+			hapd = hapd_iface->bss[hapd_iface->num_bss - 1];
+			hapd->driver = hapd_iface->bss[0]->driver;
+			hapd->drv_priv = hapd_iface->bss[0]->drv_priv;
+			os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr,
+				  ETH_ALEN);
+
+			if (start_ctrl_iface_bss(hapd) < 0 ||
+			    (hapd_iface->state == HAPD_IFACE_ENABLED &&
+			     hostapd_setup_bss(hapd, -1))) {
+				hostapd_cleanup(hapd);
+				hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
+				hapd_iface->conf->num_bss--;
+				hapd_iface->num_bss--;
+				wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
+					   __func__, hapd, hapd->conf->iface);
+				os_free(hapd);
+				return -1;
+			}
+		}
+		return 0;
+	}
 
 	ptr = os_strchr(buf, ' ');
 	if (ptr == NULL)
 		return -1;
 	*ptr++ = '\0';
 
+	if (os_strncmp(ptr, "config=", 7) == 0)
+		conf_file = ptr + 7;
+
 	for (i = 0; i < interfaces->count; i++) {
-		if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface,
+		if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
 			       buf)) {
 			wpa_printf(MSG_INFO, "Cannot add interface - it "
 				   "already exists");
@@ -1348,8 +1933,14 @@
 		goto fail;
 	}
 
-	conf = hostapd_config_alloc(interfaces, buf, ptr);
-	if (conf == NULL) {
+	if (conf_file && interfaces->config_read_cb) {
+		conf = interfaces->config_read_cb(conf_file);
+		if (conf && conf->bss)
+			os_strlcpy(conf->bss[0]->iface, buf,
+				   sizeof(conf->bss[0]->iface));
+	} else
+		conf = hostapd_config_alloc(interfaces, buf, ptr);
+	if (conf == NULL || conf->bss == NULL) {
 		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
 			   "for configuration", __func__);
 		goto fail;
@@ -1362,14 +1953,10 @@
 		goto fail;
 	}
 
-	if (hapd_iface->interfaces &&
-	    hapd_iface->interfaces->ctrl_iface_init &&
-	    hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) {
-		wpa_printf(MSG_ERROR, "%s: Failed to setup control "
-			   "interface", __func__);
+	if (start_ctrl_iface(hapd_iface) < 0)
 		goto fail;
-	}
-	wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface);
+
+	wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0]->iface);
 
 	return 0;
 
@@ -1377,24 +1964,79 @@
 	if (conf)
 		hostapd_config_free(conf);
 	if (hapd_iface) {
-		os_free(hapd_iface->bss[interfaces->count]);
+		if (hapd_iface->bss) {
+			for (i = 0; i < hapd_iface->num_bss; i++) {
+				hapd = hapd_iface->bss[i];
+				if (!hapd)
+					continue;
+				if (hapd_iface->interfaces &&
+				    hapd_iface->interfaces->ctrl_iface_deinit)
+					hapd_iface->interfaces->
+						ctrl_iface_deinit(hapd);
+				wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+					   __func__, hapd_iface->bss[i],
+					   hapd->conf->iface);
+				os_free(hapd);
+				hapd_iface->bss[i] = NULL;
+			}
+			os_free(hapd_iface->bss);
+		}
+		wpa_printf(MSG_DEBUG, "%s: free iface %p",
+			   __func__, hapd_iface);
 		os_free(hapd_iface);
 	}
 	return -1;
 }
 
 
+static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
+{
+	size_t i;
+
+	wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface);
+
+	/* Remove hostapd_data only if it has already been initialized */
+	if (idx < iface->num_bss) {
+		struct hostapd_data *hapd = iface->bss[idx];
+
+		hostapd_bss_deinit(hapd);
+		wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+			   __func__, hapd, hapd->conf->iface);
+		hostapd_config_free_bss(hapd->conf);
+		os_free(hapd);
+
+		iface->num_bss--;
+
+		for (i = idx; i < iface->num_bss; i++)
+			iface->bss[i] = iface->bss[i + 1];
+	} else {
+		hostapd_config_free_bss(iface->conf->bss[idx]);
+		iface->conf->bss[idx] = NULL;
+	}
+
+	iface->conf->num_bss--;
+	for (i = idx; i < iface->conf->num_bss; i++)
+		iface->conf->bss[i] = iface->conf->bss[i + 1];
+
+	return 0;
+}
+
+
 int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
 {
 	struct hostapd_iface *hapd_iface;
-	size_t i, k = 0;
+	size_t i, j, k = 0;
 
 	for (i = 0; i < interfaces->count; i++) {
 		hapd_iface = interfaces->iface[i];
 		if (hapd_iface == NULL)
 			return -1;
-		if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) {
+		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
 			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+			hapd_iface->driver_ap_teardown =
+				!!(hapd_iface->drv_flags &
+				   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
 			hostapd_interface_deinit_free(hapd_iface);
 			k = i;
 			while (k < (interfaces->count - 1)) {
@@ -1405,12 +2047,19 @@
 			interfaces->count--;
 			return 0;
 		}
+
+		for (j = 0; j < hapd_iface->conf->num_bss; j++) {
+			if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
+				hapd_iface->driver_ap_teardown =
+					!(hapd_iface->drv_flags &
+					  WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+				return hostapd_remove_bss(hapd_iface, j);
+			}
+		}
 	}
 	return -1;
 }
 
-#endif /* HOSTAPD */
-
 
 /**
  * hostapd_new_assoc_sta - Notify that a new station associated with the AP
@@ -1450,8 +2099,9 @@
 	/* Start accounting here, if IEEE 802.1X and WPA are not used.
 	 * IEEE 802.1X/WPA code will start accounting after the station has
 	 * been authorized. */
-	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) {
-		os_get_time(&sta->connected_time);
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
+		ap_sta_set_authorized(hapd, sta, 1);
+		os_get_reltime(&sta->connected_time);
 		accounting_sta_start(hapd, sta);
 	}
 
@@ -1464,11 +2114,329 @@
 	} else
 		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
 
-	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
-		   "for " MACSTR " (%d seconds - ap_max_inactivity)",
-		   __func__, MAC2STR(sta->addr),
-		   hapd->conf->ap_max_inactivity);
-	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
-			       ap_handle_timer, hapd, sta);
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+		wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->conf->ap_max_inactivity);
+		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+		eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+				       ap_handle_timer, hapd, sta);
+	}
 }
+
+
+const char * hostapd_state_text(enum hostapd_iface_state s)
+{
+	switch (s) {
+	case HAPD_IFACE_UNINITIALIZED:
+		return "UNINITIALIZED";
+	case HAPD_IFACE_DISABLED:
+		return "DISABLED";
+	case HAPD_IFACE_COUNTRY_UPDATE:
+		return "COUNTRY_UPDATE";
+	case HAPD_IFACE_ACS:
+		return "ACS";
+	case HAPD_IFACE_HT_SCAN:
+		return "HT_SCAN";
+	case HAPD_IFACE_DFS:
+		return "DFS";
+	case HAPD_IFACE_ENABLED:
+		return "ENABLED";
+	}
+
+	return "UNKNOWN";
+}
+
+
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
+{
+	wpa_printf(MSG_INFO, "%s: interface state %s->%s",
+		   iface->conf->bss[0]->iface, hostapd_state_text(iface->state),
+		   hostapd_state_text(s));
+	iface->state = s;
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void free_beacon_data(struct beacon_data *beacon)
+{
+	os_free(beacon->head);
+	beacon->head = NULL;
+	os_free(beacon->tail);
+	beacon->tail = NULL;
+	os_free(beacon->probe_resp);
+	beacon->probe_resp = NULL;
+	os_free(beacon->beacon_ies);
+	beacon->beacon_ies = NULL;
+	os_free(beacon->proberesp_ies);
+	beacon->proberesp_ies = NULL;
+	os_free(beacon->assocresp_ies);
+	beacon->assocresp_ies = NULL;
+}
+
+
+static int hostapd_build_beacon_data(struct hostapd_data *hapd,
+				     struct beacon_data *beacon)
+{
+	struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
+	struct wpa_driver_ap_params params;
+	int ret;
+
+	os_memset(beacon, 0, sizeof(*beacon));
+	ret = ieee802_11_build_ap_params(hapd, &params);
+	if (ret < 0)
+		return ret;
+
+	ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra,
+					 &proberesp_extra,
+					 &assocresp_extra);
+	if (ret)
+		goto free_ap_params;
+
+	ret = -1;
+	beacon->head = os_malloc(params.head_len);
+	if (!beacon->head)
+		goto free_ap_extra_ies;
+
+	os_memcpy(beacon->head, params.head, params.head_len);
+	beacon->head_len = params.head_len;
+
+	beacon->tail = os_malloc(params.tail_len);
+	if (!beacon->tail)
+		goto free_beacon;
+
+	os_memcpy(beacon->tail, params.tail, params.tail_len);
+	beacon->tail_len = params.tail_len;
+
+	if (params.proberesp != NULL) {
+		beacon->probe_resp = os_malloc(params.proberesp_len);
+		if (!beacon->probe_resp)
+			goto free_beacon;
+
+		os_memcpy(beacon->probe_resp, params.proberesp,
+			  params.proberesp_len);
+		beacon->probe_resp_len = params.proberesp_len;
+	}
+
+	/* copy the extra ies */
+	if (beacon_extra) {
+		beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
+		if (!beacon->beacon_ies)
+			goto free_beacon;
+
+		os_memcpy(beacon->beacon_ies,
+			  beacon_extra->buf, wpabuf_len(beacon_extra));
+		beacon->beacon_ies_len = wpabuf_len(beacon_extra);
+	}
+
+	if (proberesp_extra) {
+		beacon->proberesp_ies =
+			os_malloc(wpabuf_len(proberesp_extra));
+		if (!beacon->proberesp_ies)
+			goto free_beacon;
+
+		os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
+			  wpabuf_len(proberesp_extra));
+		beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
+	}
+
+	if (assocresp_extra) {
+		beacon->assocresp_ies =
+			os_malloc(wpabuf_len(assocresp_extra));
+		if (!beacon->assocresp_ies)
+			goto free_beacon;
+
+		os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
+			  wpabuf_len(assocresp_extra));
+		beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
+	}
+
+	ret = 0;
+free_beacon:
+	/* if the function fails, the caller should not free beacon data */
+	if (ret)
+		free_beacon_data(beacon);
+
+free_ap_extra_ies:
+	hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra,
+				  assocresp_extra);
+free_ap_params:
+	ieee802_11_free_ap_params(&params);
+	return ret;
+}
+
+
+/*
+ * TODO: This flow currently supports only changing frequency within the
+ * same hw_mode. Any other changes to MAC parameters or provided settings (even
+ * width) are not supported.
+ */
+static int hostapd_change_config_freq(struct hostapd_data *hapd,
+				      struct hostapd_config *conf,
+				      struct hostapd_freq_params *params,
+				      struct hostapd_freq_params *old_params)
+{
+	int channel;
+
+	if (!params->channel) {
+		/* check if the new channel is supported by hw */
+		params->channel = hostapd_hw_get_channel(hapd, params->freq);
+	}
+
+	channel = params->channel;
+	if (!channel)
+		return -1;
+
+	/* if a pointer to old_params is provided we save previous state */
+	if (old_params) {
+		old_params->channel = conf->channel;
+		old_params->ht_enabled = conf->ieee80211n;
+		old_params->sec_channel_offset = conf->secondary_channel;
+	}
+
+	conf->channel = channel;
+	conf->ieee80211n = params->ht_enabled;
+	conf->secondary_channel = params->sec_channel_offset;
+
+	/* TODO: maybe call here hostapd_config_check here? */
+
+	return 0;
+}
+
+
+static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+				     struct csa_settings *settings)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_freq_params old_freq;
+	int ret;
+
+	os_memset(&old_freq, 0, sizeof(old_freq));
+	if (!iface || !iface->freq || hapd->csa_in_progress)
+		return -1;
+
+	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+					 &settings->freq_params,
+					 &old_freq);
+	if (ret)
+		return ret;
+
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+
+	/* change back the configuration */
+	hostapd_change_config_freq(iface->bss[0], iface->conf,
+				   &old_freq, NULL);
+
+	if (ret)
+		return ret;
+
+	/* set channel switch parameters for csa ie */
+	hapd->cs_freq_params = settings->freq_params;
+	hapd->cs_count = settings->cs_count;
+	hapd->cs_block_tx = settings->block_tx;
+
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+	if (ret) {
+		free_beacon_data(&settings->beacon_after);
+		return ret;
+	}
+
+	settings->counter_offset_beacon = hapd->cs_c_off_beacon;
+	settings->counter_offset_presp = hapd->cs_c_off_proberesp;
+
+	return 0;
+}
+
+
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
+{
+	os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
+	hapd->cs_count = 0;
+	hapd->cs_block_tx = 0;
+	hapd->cs_c_off_beacon = 0;
+	hapd->cs_c_off_proberesp = 0;
+	hapd->csa_in_progress = 0;
+}
+
+
+int hostapd_switch_channel(struct hostapd_data *hapd,
+			   struct csa_settings *settings)
+{
+	int ret;
+	ret = hostapd_fill_csa_settings(hapd, settings);
+	if (ret)
+		return ret;
+
+	ret = hostapd_drv_switch_channel(hapd, settings);
+	free_beacon_data(&settings->beacon_csa);
+	free_beacon_data(&settings->beacon_after);
+
+	if (ret) {
+		/* if we failed, clean cs parameters */
+		hostapd_cleanup_cs_params(hapd);
+		return ret;
+	}
+
+	hapd->csa_in_progress = 1;
+	return 0;
+}
+
+
+void
+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;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+	if (freq_params->center_freq1)
+		vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+	if (freq_params->center_freq2)
+		vht_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;
+		break;
+	case 80:
+		if (freq_params->center_freq2)
+			vht_bw = VHT_CHANWIDTH_80P80MHZ;
+		else
+			vht_bw = VHT_CHANWIDTH_80MHZ;
+		break;
+	case 160:
+		vht_bw = VHT_CHANWIDTH_160MHZ;
+		break;
+	default:
+		wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
+			   freq_params->bandwidth);
+		break;
+	}
+
+	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;
+	iface->conf->ieee80211n = freq_params->ht_enabled;
+	iface->conf->ieee80211ac = freq_params->vht_enabled;
+
+	/*
+	 * cs_params must not be cleared earlier because the freq_params
+	 * argument may actually point to one of these.
+	 */
+	for (i = 0; i < iface->num_bss; i++)
+		hostapd_cleanup_cs_params(iface->bss[i]);
+
+	hostapd_disable_iface(iface);
+	hostapd_enable_iface(iface);
+}
+
+#endif /* NEED_AP_MLME */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dbf1b52..3c8727b 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,21 +11,19 @@
 
 #include "common/defs.h"
 #include "ap_config.h"
+#include "drivers/driver.h"
 
-struct wpa_driver_ops;
 struct wpa_ctrl_dst;
 struct radius_server_data;
 struct upnp_wps_device_sm;
 struct hostapd_data;
 struct sta_info;
-struct hostap_sta_driver_data;
 struct ieee80211_ht_capabilities;
 struct full_dynamic_vlan;
 enum wps_event;
 union wps_event_data;
 
 struct hostapd_iface;
-struct hostapd_dynamic_iface;
 
 struct hapd_interfaces {
 	int (*reload_config)(struct hostapd_iface *iface);
@@ -38,13 +36,15 @@
 	int (*driver_init)(struct hostapd_iface *iface);
 
 	size_t count;
-	size_t count_dynamic;
 	int global_ctrl_sock;
 	char *global_iface_path;
 	char *global_iface_name;
+#ifndef CONFIG_NATIVE_WINDOWS
 	gid_t ctrl_iface_group;
+#endif /* CONFIG_NATIVE_WINDOWS */
 	struct hostapd_iface **iface;
-	struct hostapd_dynamic_iface **dynamic_iface;
+
+	size_t terminate_on_error;
 };
 
 enum hostapd_chan_status {
@@ -100,6 +100,7 @@
 	struct hostapd_config *iconf;
 	struct hostapd_bss_config *conf;
 	int interface_added; /* virtual interface added for this BSS */
+	unsigned int started:1;
 
 	u8 own_addr[ETH_ALEN];
 
@@ -139,7 +140,7 @@
 	struct eapol_authenticator *eapol_auth;
 
 	struct rsn_preauth_interface *preauth_iface;
-	time_t michael_mic_failure;
+	struct os_reltime michael_mic_failure;
 	int michael_mic_failures;
 	int tkip_countermeasures;
 
@@ -209,6 +210,14 @@
 			   size_t psk_len);
 	void *new_psk_cb_ctx;
 
+	/* channel switch parameters */
+	struct hostapd_freq_params cs_freq_params;
+	u8 cs_count;
+	int cs_block_tx;
+	unsigned int cs_c_off_beacon;
+	unsigned int cs_c_off_proberesp;
+	int csa_in_progress;
+
 #ifdef CONFIG_P2P
 	struct p2p_data *p2p;
 	struct p2p_group *p2p_group;
@@ -234,8 +243,12 @@
 #ifdef CONFIG_SAE
 	/** Key used for generating SAE anti-clogging tokens */
 	u8 sae_token_key[8];
-	os_time_t last_sae_token_key_update;
+	struct os_reltime last_sae_token_key_update;
 #endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	int ext_mgmt_frame_handling;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -247,10 +260,30 @@
 	void *owner;
 	char *config_fname;
 	struct hostapd_config *conf;
+	char phy[16]; /* Name of the PHY (radio) */
+
+	enum hostapd_iface_state {
+		HAPD_IFACE_UNINITIALIZED,
+		HAPD_IFACE_DISABLED,
+		HAPD_IFACE_COUNTRY_UPDATE,
+		HAPD_IFACE_ACS,
+		HAPD_IFACE_HT_SCAN,
+		HAPD_IFACE_DFS,
+		HAPD_IFACE_ENABLED
+	} state;
 
 	size_t num_bss;
 	struct hostapd_data **bss;
 
+	unsigned int wait_channel_update:1;
+	unsigned int cac_started:1;
+
+	/*
+	 * When set, indicates that the driver will handle the AP
+	 * teardown: delete global keys, station keys, and stations.
+	 */
+	unsigned int driver_ap_teardown:1;
+
 	int num_ap; /* number of entries in ap_list */
 	struct ap_info *ap_list; /* AP info list head */
 	struct ap_info *ap_hash[STA_HASH_SIZE];
@@ -302,6 +335,9 @@
 	/* Number of HT associated stations 20 MHz */
 	int num_sta_ht_20mhz;
 
+	/* Number of HT40 intolerant stations */
+	int num_sta_ht40_intolerant;
+
 	/* Overlapping BSS information */
 	int olbc_ht;
 
@@ -315,21 +351,19 @@
 	/* lowest observed noise floor in dBm */
 	s8 lowest_nf;
 
+	unsigned int dfs_cac_ms;
+	struct os_reltime dfs_cac_start;
+
+	/* Latched with the actual secondary channel information and will be
+	 * used while juggling between HT20 and HT40 modes. */
+	int secondary_ch;
+
 #ifdef CONFIG_ACS
 	unsigned int acs_num_completed_scans;
 #endif /* CONFIG_ACS */
 
 	void (*scan_cb)(struct hostapd_iface *iface);
-};
-
-/**
- * struct hostapd_dynamic_iface - hostapd per dynamically allocated
- * or added interface data structure
- */
-struct hostapd_dynamic_iface {
-	char parent[IFNAMSIZ + 1];
-	char iface[IFNAMSIZ + 1];
-	unsigned int usage;
+	int num_ht40_scan_tries;
 };
 
 /* hostapd.c */
@@ -345,6 +379,11 @@
 int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
 void hostapd_interface_deinit(struct hostapd_iface *iface);
 void hostapd_interface_free(struct hostapd_iface *iface);
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+				    const char *config_file);
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+			   const char *config_fname, int debug);
 void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 			   int reassoc);
 void hostapd_interface_deinit_free(struct hostapd_iface *iface);
@@ -353,6 +392,15 @@
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
 int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
 int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
+const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_switch_channel(struct hostapd_data *hapd,
+			   struct csa_settings *settings);
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+				const struct hostapd_freq_params *freq_params);
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
 
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -374,7 +422,7 @@
 			 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 offset, int width, int cf1, int cf2);
 
 const struct hostapd_eap_user *
 hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 45d518b..d7909fa 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -1,7 +1,7 @@
 /*
  * Hotspot 2.0 AP ANQP processing
  * Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,19 +13,165 @@
 #include "common/ieee802_11_defs.h"
 #include "hostapd.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "hs20.h"
 
 
 u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
 {
+	u8 conf;
 	if (!hapd->conf->hs20)
 		return eid;
 	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
-	*eid++ = 5;
+	*eid++ = 7;
 	WPA_PUT_BE24(eid, OUI_WFA);
 	eid += 3;
 	*eid++ = HS20_INDICATION_OUI_TYPE;
-	/* Hotspot Configuration: DGAF Enabled */
-	*eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00;
+	conf = HS20_VERSION; /* Release Number */
+	conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+	if (hapd->conf->disable_dgaf)
+		conf |= HS20_DGAF_DISABLED;
+	*eid++ = conf;
+	WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+	eid += 2;
+
 	return eid;
 }
+
+
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *len;
+	u16 capab;
+
+	if (!hapd->conf->osen)
+		return eid;
+
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	len = eid++; /* to be filled */
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = HS20_OSEN_OUI_TYPE;
+
+	/* Group Data Cipher Suite */
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	eid += RSN_SELECTOR_LEN;
+
+	/* Pairwise Cipher Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+	eid += RSN_SELECTOR_LEN;
+
+	/* AKM Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+	eid += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities */
+	capab = 0;
+	if (hapd->conf->wmm_enabled) {
+		/* 4 PTKSA replay counters when using WMM */
+		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+	}
+#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 */
+	WPA_PUT_LE16(eid, capab);
+	eid += 2;
+
+	*len = eid - len - 1;
+
+	return eid;
+}
+
+
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+			       u8 osu_method, const char *url)
+{
+	struct wpabuf *buf;
+	size_t len = 0;
+	int ret;
+
+	/* TODO: should refuse to send notification if the STA is not associated
+	 * or if the STA did not indicate support for WNM-Notification */
+
+	if (url) {
+		len = 1 + os_strlen(url);
+		if (5 + len > 255) {
+			wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
+				   "WNM-Notification: '%s'", url);
+			return -1;
+		}
+	}
+
+	buf = wpabuf_alloc(4 + 7 + len);
+	if (buf == NULL)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpabuf_put_u8(buf, 1); /* Dialog token */
+	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+	/* Subscription Remediation subelement */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5 + len);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
+	if (url) {
+		wpabuf_put_u8(buf, len - 1);
+		wpabuf_put_data(buf, url, len - 1);
+		wpabuf_put_u8(buf, osu_method);
+	} else {
+		/* Server URL and Server Method fields not included */
+		wpabuf_put_u8(buf, 0);
+	}
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+					  const u8 *addr,
+					  const struct wpabuf *payload)
+{
+	struct wpabuf *buf;
+	int ret;
+
+	/* TODO: should refuse to send notification if the STA is not associated
+	 * or if the STA did not indicate support for WNM-Notification */
+
+	buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+	if (buf == NULL)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpabuf_put_u8(buf, 1); /* Dialog token */
+	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+	/* Deauthentication Imminent Notice subelement */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+	wpabuf_put_buf(buf, payload);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+
+	return ret;
+}
diff --git a/src/ap/hs20.h b/src/ap/hs20.h
index 98698ce..152439f 100644
--- a/src/ap/hs20.h
+++ b/src/ap/hs20.h
@@ -1,6 +1,6 @@
 /*
  * Hotspot 2.0 AP ANQP processing
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,5 +12,11 @@
 struct hostapd_data;
 
 u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+			       u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+					  const u8 *addr,
+					  const struct wpabuf *payload);
 
 #endif /* HS20_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8a239f4..4e66d1b 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -20,11 +14,13 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
+#include "common/wpa_ctrl.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
 #include "acs.h"
+#include "ieee802_11.h"
+#include "beacon.h"
 #include "hw_features.h"
 
 
@@ -45,6 +41,36 @@
 }
 
 
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+	static char info[256];
+	char *state;
+
+	switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
+	case HOSTAPD_CHAN_DFS_UNKNOWN:
+		state = "unknown";
+		break;
+	case HOSTAPD_CHAN_DFS_USABLE:
+		state = "usable";
+		break;
+	case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+		state = "unavailable";
+		break;
+	case HOSTAPD_CHAN_DFS_AVAILABLE:
+		state = "available";
+		break;
+	default:
+		return "";
+	}
+	os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
+	info[sizeof(info) - 1] = '\0';
+
+	return info;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
 int hostapd_get_hw_features(struct hostapd_iface *iface)
 {
 	struct hostapd_data *hapd = iface->bss[0];
@@ -71,30 +97,43 @@
 
 	for (i = 0; i < num_modes; i++) {
 		struct hostapd_hw_modes *feature = &modes[i];
+		int dfs_enabled = hapd->iconf->ieee80211h &&
+			(iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
 		/* set flag for channels we can use in current regulatory
 		 * domain */
 		for (j = 0; j < feature->num_channels; j++) {
+			int dfs = 0;
+
 			/*
 			 * Disable all channels that are marked not to allow
-			 * IBSS operation or active scanning. In addition,
-			 * disable all channels that require radar detection,
-			 * since that (in addition to full DFS) is not yet
-			 * supported.
+			 * IBSS operation or active scanning.
+			 * Use radar channels only if the driver supports DFS.
 			 */
-			if (feature->channels[j].flag &
-			    (HOSTAPD_CHAN_NO_IBSS |
-			     HOSTAPD_CHAN_PASSIVE_SCAN |
-			     HOSTAPD_CHAN_RADAR))
+			if ((feature->channels[j].flag &
+			     HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+				dfs = 1;
+			} else if (((feature->channels[j].flag &
+				     HOSTAPD_CHAN_RADAR) &&
+				    !(iface->drv_flags &
+				      WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+				   (feature->channels[j].flag &
+				    (HOSTAPD_CHAN_NO_IBSS |
+				     HOSTAPD_CHAN_PASSIVE_SCAN))) {
 				feature->channels[j].flag |=
 					HOSTAPD_CHAN_DISABLED;
+			}
+
 			if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
 				continue;
+
 			wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
-				   "chan=%d freq=%d MHz max_tx_power=%d dBm",
+				   "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
 				   feature->mode,
 				   feature->channels[j].chan,
 				   feature->channels[j].freq,
-				   feature->channels[j].max_tx_power);
+				   feature->channels[j].max_tx_power,
+				   dfs ? dfs_info(&feature->channels[j]) : "");
 		}
 	}
 
@@ -230,7 +269,7 @@
 		first = sec_chan;
 
 	ok = 0;
-	for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
+	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
 		if (first == allowed[k]) {
 			ok = 1;
 			break;
@@ -271,8 +310,8 @@
 	if (elems.ht_operation &&
 	    elems.ht_operation_len >= sizeof(*oper)) {
 		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-		*pri_chan = oper->control_chan;
-		if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
+		*pri_chan = oper->primary_chan;
+		if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
 			int sec = oper->ht_param &
 				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
 			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
@@ -293,7 +332,7 @@
 	int match;
 
 	pri_chan = iface->conf->channel;
-	sec_chan = iface->conf->secondary_channel * 4;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 	pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
 	if (iface->conf->secondary_channel > 0)
 		sec_freq = pri_freq + 20;
@@ -317,6 +356,7 @@
 			   "channel to get secondary channel with no Beacons "
 			   "from other BSSes");
 		ieee80211n_switch_pri_sec(iface);
+		return 1;
 	}
 
 	/*
@@ -355,6 +395,36 @@
 }
 
 
+static int ieee80211n_check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq,
+				      int start, int end)
+{
+	struct ieee802_11_elems elems;
+	struct ieee80211_ht_operation *oper;
+
+	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
+		return 0;
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (!elems.ht_capabilities) {
+		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
+			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+		return 1;
+	}
+
+	if (elems.ht_operation &&
+	    elems.ht_operation_len >= sizeof(*oper)) {
+		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
+			return 0;
+
+		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
+			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+		return 1;
+	}
+	return 0;
+}
+
+
 static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
 				      struct wpa_scan_results *scan_res)
 {
@@ -376,6 +446,15 @@
 		int pri = bss->freq;
 		int sec = pri;
 		int sec_chan, pri_chan;
+		struct ieee802_11_elems elems;
+
+		/* Check for overlapping 20 MHz BSS */
+		if (ieee80211n_check_20mhz_bss(bss, pri_freq, affected_start,
+					       affected_end)) {
+			wpa_printf(MSG_DEBUG,
+				   "Overlapping 20 MHz BSS is found");
+			return 0;
+		}
 
 		ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
 
@@ -407,7 +486,23 @@
 			}
 		}
 
-		/* TODO: 40 MHz intolerant */
+		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
+				       0);
+		if (elems.ht_capabilities &&
+		    elems.ht_capabilities_len >=
+		    sizeof(struct ieee80211_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;
@@ -437,6 +532,7 @@
 		oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
 	wpa_scan_results_free(scan_res);
 
+	iface->secondary_ch = iface->conf->secondary_channel;
 	if (!oper40) {
 		wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
 			   "channel pri=%d sec=%d based on overlapping BSSes",
@@ -444,9 +540,21 @@
 			   iface->conf->channel +
 			   iface->conf->secondary_channel * 4);
 		iface->conf->secondary_channel = 0;
+		if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
+			/*
+			 * TODO: Could consider scheduling another scan to check
+			 * if channel width can be changed if no coex reports
+			 * are received from associating stations.
+			 */
+		}
 	}
 
 	res = ieee80211n_allowed_ht40_channel_pair(iface);
+	if (!res) {
+		iface->conf->secondary_channel = 0;
+		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
+	}
+
 	hostapd_setup_interface_complete(iface, !res);
 }
 
@@ -491,25 +599,128 @@
 }
 
 
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+					struct wpa_driver_scan_params *params)
+{
+	/* Scan only the affected frequency range */
+	int pri_freq;
+	int affected_start, affected_end;
+	int i, pos;
+	struct hostapd_hw_modes *mode;
+
+	if (iface->current_mode == NULL)
+		return;
+
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	if (iface->conf->secondary_channel > 0) {
+		affected_start = pri_freq - 10;
+		affected_end = pri_freq + 30;
+	} else {
+		affected_start = pri_freq - 30;
+		affected_end = pri_freq + 10;
+	}
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+
+	mode = iface->current_mode;
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	pos = 0;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (chan->freq < affected_start ||
+		    chan->freq > affected_end)
+			continue;
+		params->freqs[pos++] = chan->freq;
+	}
+}
+
+
+static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
+{
+#define HT2040_COEX_SCAN_RETRY 15
+	struct hostapd_iface *iface = eloop_data;
+	struct wpa_driver_scan_params params;
+	int ret;
+
+	os_memset(&params, 0, sizeof(params));
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		ieee80211n_scan_channels_2g4(iface, &params);
+	else
+		ieee80211n_scan_channels_5g(iface, &params);
+
+	ret = hostapd_driver_scan(iface->bss[0], &params);
+	iface->num_ht40_scan_tries++;
+	os_free(params.freqs);
+
+	if (ret == -EBUSY &&
+	    iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
+			   ret, strerror(-ret), iface->num_ht40_scan_tries);
+		eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+		return;
+	}
+
+	if (ret == 0) {
+		iface->scan_cb = ieee80211n_check_scan;
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "Failed to request a scan in device, bringing up in HT20 mode");
+	iface->conf->secondary_channel = 0;
+	iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+	hostapd_setup_interface_complete(iface, 0);
+}
+
+
+void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+	eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+}
+
+
 static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
 {
 	struct wpa_driver_scan_params params;
+	int ret;
 
 	if (!iface->conf->secondary_channel)
 		return 0; /* HT40 not used */
 
+	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
 	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
 		   "40 MHz channel");
 	os_memset(&params, 0, sizeof(params));
 	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
 		ieee80211n_scan_channels_2g4(iface, &params);
-	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
-		wpa_printf(MSG_ERROR, "Failed to request a scan of "
-			   "neighboring BSSes");
-		os_free(params.freqs);
+	else
+		ieee80211n_scan_channels_5g(iface, &params);
+
+	ret = hostapd_driver_scan(iface->bss[0], &params);
+	os_free(params.freqs);
+
+	if (ret == -EBUSY) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
+			   ret, strerror(-ret));
+		iface->num_ht40_scan_tries = 1;
+		eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+		eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+		return 1;
+	}
+
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s)",
+			   ret, strerror(-ret));
 		return -1;
 	}
-	os_free(params.freqs);
 
 	iface->scan_cb = ieee80211n_check_scan;
 	return 1;
@@ -597,12 +808,6 @@
 		return 0;
 	}
 
-	if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
-		wpa_printf(MSG_ERROR, "Driver does not support configured "
-			   "HT capability [PSMP]");
-		return 0;
-	}
-
 	if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
 	    !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
 		wpa_printf(MSG_ERROR, "Driver does not support configured "
@@ -613,6 +818,92 @@
 	return 1;
 }
 
+
+#ifdef CONFIG_IEEE80211AC
+
+static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name)
+{
+	u32 req_cap = conf & cap;
+
+	/*
+	 * Make sure we support all requested capabilities.
+	 * NOTE: We assume that 'cap' represents a capability mask,
+	 * not a discrete value.
+	 */
+	if ((hw & req_cap) != req_cap) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]",
+			   name);
+		return 0;
+	}
+	return 1;
+}
+
+
+static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 cap,
+				     const char *name)
+{
+	u32 hw_max = hw & cap;
+	u32 conf_val = conf & cap;
+
+	if (conf_val > hw_max) {
+		int offset = find_first_bit(cap);
+		wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
+			   name, conf_val >> offset, hw_max >> offset);
+		return 0;
+	}
+	return 1;
+}
+
+
+static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+{
+	u32 hw = iface->current_mode->vht_capab;
+	u32 conf = iface->conf->vht_capab;
+
+	wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
+		   hw, conf);
+
+#define VHT_CAP_CHECK(cap) \
+	do { \
+		if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
+			return 0; \
+	} while (0)
+
+#define VHT_CAP_CHECK_MAX(cap) \
+	do { \
+		if (!ieee80211ac_cap_check_max(hw, conf, cap, #cap)) \
+			return 0; \
+	} while (0)
+
+	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
+	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
+	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
+	VHT_CAP_CHECK(VHT_CAP_RXLDPC);
+	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
+	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
+	VHT_CAP_CHECK(VHT_CAP_TXSTBC);
+	VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
+	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
+	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+	VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
+	VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
+	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
+	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+	VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
+	VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
+	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
+	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
+	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
+
+#undef VHT_CAP_CHECK
+#undef VHT_CAP_CHECK_MAX
+
+	return 1;
+}
+#endif /* CONFIG_IEEE80211AC */
+
 #endif /* CONFIG_IEEE80211N */
 
 
@@ -624,6 +915,10 @@
 		return 0;
 	if (!ieee80211n_supported_ht_capab(iface))
 		return -1;
+#ifdef CONFIG_IEEE80211AC
+	if (!ieee80211ac_supported_vht_capab(iface))
+		return -1;
+#endif /* CONFIG_IEEE80211AC */
 	ret = ieee80211n_check_40mhz(iface);
 	if (ret)
 		return ret;
@@ -718,33 +1013,45 @@
 }
 
 
-int hostapd_acs_completed(struct hostapd_iface *iface)
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
 {
-	int ret;
+	int ret = -1;
+
+	if (err)
+		goto out;
 
 	switch (hostapd_check_chans(iface)) {
 	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);
 		break;
 	case HOSTAPD_CHAN_ACS:
 		wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
+		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
 		hostapd_notify_bad_chans(iface);
-		return -1;
+		goto out;
 	case HOSTAPD_CHAN_INVALID:
 	default:
 		wpa_printf(MSG_ERROR, "ACS picked unusable channels");
+		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
 		hostapd_notify_bad_chans(iface);
-		return -1;
+		goto out;
 	}
 
 	ret = hostapd_check_ht_capab(iface);
 	if (ret < 0)
-		return -1;
+		goto out;
 	if (ret == 1) {
 		wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
 		return 0;
 	}
 
-	return hostapd_setup_interface_complete(iface, 0);
+	ret = 0;
+out:
+	return hostapd_setup_interface_complete(iface, ret);
 }
 
 
@@ -763,6 +1070,15 @@
 	if (iface->num_hw_features < 1)
 		return -1;
 
+	if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
+	     iface->conf->ieee80211n || iface->conf->ieee80211ac) &&
+	    iface->conf->channel == 14) {
+		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14");
+		iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+		iface->conf->ieee80211n = 0;
+		iface->conf->ieee80211ac = 0;
+	}
+
 	iface->current_mode = NULL;
 	for (i = 0; i < iface->num_hw_features; i++) {
 		struct hostapd_hw_modes *mode = &iface->hw_features[i];
@@ -793,8 +1109,6 @@
 		hostapd_notify_bad_chans(iface);
 		return -3;
 	}
-
-	return 0;
 }
 
 
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index abadcd1..0f67ab8 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef HW_FEATURES_H
@@ -21,6 +15,7 @@
 void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
 			      size_t num_hw_features);
 int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_acs_completed(struct hostapd_iface *iface, int err);
 int hostapd_select_hw_mode(struct hostapd_iface *iface);
 const char * hostapd_hw_mode_txt(int mode);
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
@@ -28,6 +23,7 @@
 int hostapd_check_ht_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);
 #else /* NEED_AP_MLME */
 static inline void
 hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -66,6 +62,10 @@
 	return 0;
 }
 
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
 #endif /* NEED_AP_MLME */
 
 #endif /* HW_FEATURES_H */
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index be55c69..9b2900f 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -204,7 +204,7 @@
 	addr.sin_port = htons(IAPP_UDP_PORT);
 	if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
 		   (struct sockaddr *) &addr, sizeof(addr)) < 0)
-		perror("sendto[IAPP-ADD]");
+		wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
 }
 
 
@@ -231,7 +231,7 @@
 				   * FIX: what is correct RW with 802.11? */
 
 	if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
-		perror("send[L2 Update]");
+		wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
 }
 
 
@@ -242,29 +242,22 @@
  */
 void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
 {
-	struct ieee80211_mgmt *assoc;
-	u16 seq;
+	u16 seq = 0; /* TODO */
 
 	if (iapp == NULL)
 		return;
 
-	assoc = sta->last_assoc_req;
-	seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0;
-
 	/* 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);
 
-	if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) ==
-	    WLAN_FC_STYPE_REASSOC_REQ) {
-		/* 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 */
-	}
+	/* 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 */
 }
 
 
@@ -276,8 +269,8 @@
 	struct sta_info *sta;
 
 	if (len != sizeof(*add)) {
-		printf("Invalid IAPP-ADD packet length %d (expected %lu)\n",
-		       len, (unsigned long) sizeof(*add));
+		wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
+			   len, (unsigned long) sizeof(*add));
 		return;
 	}
 
@@ -326,7 +319,8 @@
 	len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (len < 0) {
-		perror("recvfrom");
+		wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
+			   strerror(errno));
 		return;
 	}
 
@@ -350,17 +344,18 @@
 		       hdr->version, hdr->command,
 		       be_to_host16(hdr->identifier), hlen);
 	if (hdr->version != IAPP_VERSION) {
-		printf("Dropping IAPP frame with unknown version %d\n",
-		       hdr->version);
+		wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
+			   hdr->version);
 		return;
 	}
 	if (hlen > len) {
-		printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len);
+		wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
+			   hlen, len);
 		return;
 	}
 	if (hlen < len) {
-		printf("Ignoring %d extra bytes from IAPP frame\n",
-		       len - hlen);
+		wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
+			   len - hlen);
 		len = hlen;
 	}
 
@@ -376,7 +371,7 @@
 		/* TODO: process */
 		break;
 	default:
-		printf("Unknown IAPP command %d\n", hdr->command);
+		wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
 		break;
 	}
 }
@@ -403,7 +398,8 @@
 
 	iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (iapp->udp_sock < 0) {
-		perror("socket[PF_INET,SOCK_DGRAM]");
+		wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
 		iapp_deinit(iapp);
 		return NULL;
 	}
@@ -411,35 +407,38 @@
 	os_memset(&ifr, 0, sizeof(ifr));
 	os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
 	if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
-		perror("ioctl(SIOCGIFINDEX)");
+		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) {
-		perror("ioctl(SIOCGIFADDR)");
+		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) {
-		printf("Invalid address family %i (SIOCGIFADDR)\n",
-		       paddr->sin_family);
+		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) {
-		perror("ioctl(SIOCGIFBRDADDR)");
+		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) {
-		printf("Invalid address family %i (SIOCGIFBRDADDR)\n",
-		       paddr->sin_family);
+		wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
+			   paddr->sin_family);
 		iapp_deinit(iapp);
 		return NULL;
 	}
@@ -450,7 +449,8 @@
 	uaddr.sin_port = htons(IAPP_UDP_PORT);
 	if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
 		 sizeof(uaddr)) < 0) {
-		perror("bind[UDP]");
+		wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
+			   strerror(errno));
 		iapp_deinit(iapp);
 		return NULL;
 	}
@@ -461,14 +461,16 @@
 	mreq.imr_ifindex = 0;
 	if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
 		       sizeof(mreq)) < 0) {
-		perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]");
+		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) {
-		perror("socket[PF_PACKET,SOCK_RAW]");
+		wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
 		iapp_deinit(iapp);
 		return NULL;
 	}
@@ -478,19 +480,20 @@
 	addr.sll_ifindex = ifindex;
 	if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
 		 sizeof(addr)) < 0) {
-		perror("bind[PACKET]");
+		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)) {
-		printf("Could not register read socket for IAPP.\n");
+		wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
 		iapp_deinit(iapp);
 		return NULL;
 	}
 
-	printf("IEEE 802.11F (IAPP) using interface %s\n", iface);
+	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
@@ -515,7 +518,8 @@
 		mreq.imr_ifindex = 0;
 		if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
 			       &mreq, sizeof(mreq)) < 0) {
-			perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]");
+			wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
+				   strerror(errno));
 		}
 
 		eloop_unregister_read_sock(iapp->udp_sock);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 781f826..de1ee5e 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -15,7 +15,6 @@
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
 #include "crypto/random.h"
-#include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
@@ -39,6 +38,7 @@
 #include "ap_drv_ops.h"
 #include "wnm_ap.h"
 #include "ieee802_11.h"
+#include "dfs.h"
 
 
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -62,7 +62,6 @@
 	}
 
 	*pos++ = num;
-	count = 0;
 	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
 	     i++) {
 		count++;
@@ -105,7 +104,6 @@
 
 	*pos++ = WLAN_EID_EXT_SUPP_RATES;
 	*pos++ = num;
-	count = 0;
 	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
 	     i++) {
 		count++;
@@ -138,6 +136,15 @@
 {
 	int capab = WLAN_CAPABILITY_ESS;
 	int privacy;
+	int dfs;
+
+	/* Check if any of configured channels require DFS */
+	dfs = hostapd_is_dfs_required(hapd->iface);
+	if (dfs < 0) {
+		wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+			   dfs);
+		dfs = 0;
+	}
 
 	if (hapd->iface->num_sta_no_short_preamble == 0 &&
 	    hapd->iconf->preamble == SHORT_PREAMBLE)
@@ -153,6 +160,11 @@
 	if (hapd->conf->wpa)
 		privacy = 1;
 
+#ifdef CONFIG_HS20
+	if (hapd->conf->osen)
+		privacy = 1;
+#endif /* CONFIG_HS20 */
+
 	if (sta) {
 		int policy, def_klen;
 		if (probe && sta->ssid_probe) {
@@ -175,25 +187,21 @@
 	    hapd->iface->num_sta_no_short_slot_time == 0)
 		capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
 
+	/*
+	 * Currently, Spectrum Management capability bit is set when directly
+	 * requested in configuration by spectrum_mgmt_required or when AP is
+	 * running on DFS channel.
+	 * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
+	 */
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+	    (hapd->iconf->spectrum_mgmt_required || dfs))
+		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
 	return capab;
 }
 
 
-void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
-{
-	int i;
-	if (len > HOSTAPD_MAX_SSID_LEN)
-		len = HOSTAPD_MAX_SSID_LEN;
-	for (i = 0; i < len; i++) {
-		if (ssid[i] >= 32 && ssid[i] < 127)
-			buf[i] = ssid[i];
-		else
-			buf[i] = '.';
-	}
-	buf[len] = '\0';
-}
-
-
 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 auth_transaction, const u8 *challenge,
 			   int iswep)
@@ -228,7 +236,8 @@
 
 	/* Transaction 3 */
 	if (!iswep || !sta->challenge || !challenge ||
-	    os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) {
+	    os_memcmp_const(sta->challenge, challenge,
+			    WLAN_AUTH_CHALLENGE_LEN)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO,
 			       "shared key authentication - invalid "
@@ -239,13 +248,8 @@
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_DEBUG,
 		       "authentication OK (shared key)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
-	/* Station will be marked authenticated if it ACKs the
-	 * authentication reply. */
-#else
 	sta->flags |= WLAN_STA_AUTH;
 	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-#endif
 	os_free(sta->challenge);
 	sta->challenge = NULL;
 
@@ -286,7 +290,7 @@
 		   MAC2STR(dst), auth_alg, auth_transaction,
 		   resp, (unsigned long) ies_len);
 	if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
-		perror("send_auth_reply: send");
+		wpa_printf(MSG_INFO, "send_auth_reply: send");
 
 	os_free(buf);
 }
@@ -399,7 +403,7 @@
 		return -1;
 	if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
 			addr, ETH_ALEN, mac) < 0 ||
-	    os_memcmp(token, mac, SHA256_MAC_LEN) != 0)
+	    os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
 		return -1;
 
 	return 0;
@@ -411,17 +415,17 @@
 {
 	struct wpabuf *buf;
 	u8 *token;
-	struct os_time t;
+	struct os_reltime now;
 
-	os_get_time(&t);
-	if (hapd->last_sae_token_key_update == 0 ||
-	    t.sec > hapd->last_sae_token_key_update + 60) {
+	os_get_reltime(&now);
+	if (!os_reltime_initialized(&hapd->last_sae_token_key_update) ||
+	    os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) {
 		if (random_get_bytes(hapd->sae_token_key,
 				     sizeof(hapd->sae_token_key)) < 0)
 			return NULL;
 		wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
 			    hapd->sae_token_key, sizeof(hapd->sae_token_key));
-		hapd->last_sae_token_key_update = t.sec;
+		hapd->last_sae_token_key_update = now;
 	}
 
 	buf = wpabuf_alloc(SHA256_MAC_LEN);
@@ -492,6 +496,7 @@
 				       HOSTAPD_LEVEL_DEBUG,
 				       "SAE confirm before commit");
 			resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+			goto failed;
 		}
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -523,6 +528,7 @@
 		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
 	}
 
+failed:
 	sta->auth_alg = WLAN_AUTH_SAE;
 
 	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
@@ -552,13 +558,13 @@
 	char *radius_cui = NULL;
 
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
-		printf("handle_auth - too short payload (len=%lu)\n",
-		       (unsigned long) len);
+		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+			   (unsigned long) len);
 		return;
 	}
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (hapd->iconf->ignore_auth_probability > 0.0d &&
+	if (hapd->iconf->ignore_auth_probability > 0.0 &&
 	    drand48() < hapd->iconf->ignore_auth_probability) {
 		wpa_printf(MSG_INFO,
 			   "TESTING: ignoring auth frame from " MACSTR,
@@ -601,23 +607,23 @@
 #endif /* CONFIG_SAE */
 	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
 	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
-		printf("Unsupported authentication algorithm (%d)\n",
-		       auth_alg);
+		wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
+			   auth_alg);
 		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
 		goto fail;
 	}
 
 	if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
 	      (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
-		printf("Unknown authentication transaction number (%d)\n",
-		       auth_transaction);
+		wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
+			   auth_transaction);
 		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
 		goto fail;
 	}
 
 	if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
-		printf("Station " MACSTR " not allowed to authenticate.\n",
-		       MAC2STR(mgmt->sa));
+		wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+			   MAC2STR(mgmt->sa));
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
@@ -628,8 +634,8 @@
 				      &psk, &identity, &radius_cui);
 
 	if (res == HOSTAPD_ACL_REJECT) {
-		printf("Station " MACSTR " not allowed to authenticate.\n",
-		       MAC2STR(mgmt->sa));
+		wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+			   MAC2STR(mgmt->sa));
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
@@ -691,15 +697,10 @@
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "authentication OK (open system)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
-		/* Station will be marked authenticated if it ACKs the
-		 * authentication reply. */
-#else
 		sta->flags |= WLAN_STA_AUTH;
 		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
 		sta->auth_alg = WLAN_AUTH_OPEN;
 		mlme_authenticate_indication(hapd, sta);
-#endif
 		break;
 	case WLAN_AUTH_SHARED_KEY:
 		resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
@@ -792,12 +793,10 @@
 
 	if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
 	    os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
-		char ssid_txt[33];
-		ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len);
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO,
 			       "Station tried to associate with unknown SSID "
-			       "'%s'", ssid_txt);
+			       "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
@@ -859,6 +858,21 @@
 }
 
 
+static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+#ifdef CONFIG_INTERWORKING
+	/* check for QoS Map support */
+	if (ext_capab_ie_len >= 5) {
+		if (ext_capab_ie[4] & 0x01)
+			sta->qos_map_enabled = 1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
 static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			   const u8 *ies, size_t ies_len, int reassoc)
 {
@@ -881,6 +895,9 @@
 	resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
+	resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
 	resp = copy_supp_rates(hapd, sta, &elems);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
@@ -903,12 +920,17 @@
 				  elems.vht_capabilities_len);
 	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;
+
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
 	    !(sta->flags & WLAN_STA_VHT)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "Station does not support "
 			       "mandatory VHT PHY - reject association");
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
 	}
 #endif /* CONFIG_IEEE80211AC */
 
@@ -1050,7 +1072,9 @@
 
 #ifdef CONFIG_SAE
 		if (wpa_auth_uses_sae(sta->wpa_sm) &&
-		    sta->auth_alg != WLAN_AUTH_SAE) {
+		    sta->auth_alg != WLAN_AUTH_SAE &&
+		    !(sta->auth_alg == WLAN_AUTH_FT &&
+		      wpa_auth_uses_ft_sae(sta->wpa_sm))) {
 			wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
 				   "SAE AKM after non-SAE auth_alg %u",
 				   MAC2STR(sta->addr), sta->auth_alg);
@@ -1069,6 +1093,29 @@
 			return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
 		}
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_HS20
+	} else if (hapd->conf->osen) {
+		if (elems.osen == NULL) {
+			hostapd_logger(
+				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+				HOSTAPD_LEVEL_INFO,
+				"No HS 2.0 OSEN element in association request");
+			return WLAN_STATUS_INVALID_IE;
+		}
+
+		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+				   "state machine");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+				      elems.osen - 2, elems.osen_len + 2) < 0)
+			return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
 	} else
 		wpa_auth_sta_no_wpa(sta->wpa_sm);
 
@@ -1135,8 +1182,7 @@
 	reply->u.assoc_resp.capab_info =
 		host_to_le16(hostapd_own_capab_info(hapd, sta, 0));
 	reply->u.assoc_resp.status_code = host_to_le16(status_code);
-	reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0)
-					       | BIT(14) | BIT(15));
+	reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
 	/* Supported rates */
 	p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
 	/* Extended supported rates */
@@ -1169,6 +1215,8 @@
 
 	p = hostapd_eid_ext_capab(hapd, p);
 	p = hostapd_eid_bss_max_idle_period(hapd, p);
+	if (sta->qos_map_enabled)
+		p = hostapd_eid_qos_map_set(hapd, p);
 
 	if (sta->flags & WLAN_STA_WMM)
 		p = hostapd_eid_wmm(hapd, p);
@@ -1235,14 +1283,14 @@
 
 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
 				      sizeof(mgmt->u.assoc_req))) {
-		printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
-		       "\n", reassoc, (unsigned long) len);
+		wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
+			   reassoc, (unsigned long) len);
 		return;
 	}
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (reassoc) {
-		if (hapd->iconf->ignore_reassoc_probability > 0.0d &&
+		if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
 		    drand48() < hapd->iconf->ignore_reassoc_probability) {
 			wpa_printf(MSG_INFO,
 				   "TESTING: ignoring reassoc request from "
@@ -1250,7 +1298,7 @@
 			return;
 		}
 	} else {
-		if (hapd->iconf->ignore_assoc_probability > 0.0d &&
+		if (hapd->iconf->ignore_assoc_probability > 0.0 &&
 		    drand48() < hapd->iconf->ignore_assoc_probability) {
 			wpa_printf(MSG_INFO,
 				   "TESTING: ignoring assoc request from "
@@ -1396,17 +1444,6 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
-	if (reassoc) {
-		os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap,
-			  ETH_ALEN);
-	}
-
-	if (sta->last_assoc_req)
-		os_free(sta->last_assoc_req);
-	sta->last_assoc_req = os_malloc(len);
-	if (sta->last_assoc_req)
-		os_memcpy(sta->last_assoc_req, mgmt, len);
-
 	/* Make sure that the previously registered inactivity timer will not
 	 * remove the STA immediately. */
 	sta->timeout_next = STA_NULLFUNC;
@@ -1422,8 +1459,8 @@
 	struct sta_info *sta;
 
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
-		printf("handle_disassoc - too short payload (len=%lu)\n",
-		       (unsigned long) len);
+		wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
+			   (unsigned long) len);
 		return;
 	}
 
@@ -1433,8 +1470,8 @@
 
 	sta = ap_get_sta(hapd, mgmt->sa);
 	if (sta == NULL) {
-		printf("Station " MACSTR " trying to disassociate, but it "
-		       "is not associated.\n", MAC2STR(mgmt->sa));
+		wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated",
+			   MAC2STR(mgmt->sa));
 		return;
 	}
 
@@ -1508,8 +1545,8 @@
 	struct ieee802_11_elems elems;
 
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
-		printf("handle_beacon - too short payload (len=%lu)\n",
-		       (unsigned long) len);
+		wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
+			   (unsigned long) len);
 		return;
 	}
 
@@ -1524,9 +1561,9 @@
 
 #ifdef CONFIG_IEEE80211W
 
-static void hostapd_sa_query_action(struct hostapd_data *hapd,
-				    const struct ieee80211_mgmt *mgmt,
-				    size_t len)
+static int hostapd_sa_query_action(struct hostapd_data *hapd,
+				   const struct ieee80211_mgmt *mgmt,
+				   size_t len)
 {
 	const u8 *end;
 
@@ -1535,12 +1572,13 @@
 	if (((u8 *) mgmt) + len < end) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
 			   "frame (len=%lu)", (unsigned long) len);
-		return;
+		return 0;
 	}
 
 	ieee802_11_sa_query_action(hapd, mgmt->sa,
 				   mgmt->u.action.u.sa_query_resp.action,
 				   mgmt->u.action.u.sa_query_resp.trans_id);
+	return 1;
 }
 
 
@@ -1552,29 +1590,8 @@
 #endif /* CONFIG_IEEE80211W */
 
 
-#ifdef CONFIG_WNM
-static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta,
-			       const struct ieee80211_mgmt *mgmt,
-			       size_t len)
-{
-	struct rx_action action;
-	if (len < IEEE80211_HDRLEN + 2)
-		return;
-	os_memset(&action, 0, sizeof(action));
-	action.da = mgmt->da;
-	action.sa = mgmt->sa;
-	action.bssid = mgmt->bssid;
-	action.category = mgmt->u.action.category;
-	action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action;
-	action.len = len - IEEE80211_HDRLEN - 1;
-	action.freq = hapd->iface->freq;
-	ieee802_11_rx_wnm_action_ap(hapd, &action);
-}
-#endif /* CONFIG_WNM */
-
-
-static void handle_action(struct hostapd_data *hapd,
-			  const struct ieee80211_mgmt *mgmt, size_t len)
+static int handle_action(struct hostapd_data *hapd,
+			 const struct ieee80211_mgmt *mgmt, size_t len)
 {
 	struct sta_info *sta;
 	sta = ap_get_sta(hapd, mgmt->sa);
@@ -1584,7 +1601,7 @@
 			       HOSTAPD_LEVEL_DEBUG,
 			       "handle_action - too short payload (len=%lu)",
 			       (unsigned long) len);
-		return;
+		return 0;
 	}
 
 	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
@@ -1592,43 +1609,53 @@
 		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
 			   "frame (category=%u) from unassociated STA " MACSTR,
 			   MAC2STR(mgmt->sa), mgmt->u.action.category);
-		return;
+		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))) {
+	    !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
+	    robust_action_frame(mgmt->u.action.category)) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "Dropped unprotected Robust Action frame from "
 			       "an MFP STA");
-		return;
+		return 0;
 	}
 #endif /* CONFIG_IEEE80211W */
 
 	switch (mgmt->u.action.category) {
 #ifdef CONFIG_IEEE80211R
 	case WLAN_ACTION_FT:
-		if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+		if (!sta ||
+		    wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
 				     len - IEEE80211_HDRLEN))
 			break;
-		return;
+		return 1;
 #endif /* CONFIG_IEEE80211R */
 	case WLAN_ACTION_WMM:
 		hostapd_wmm_action(hapd, mgmt, len);
-		return;
+		return 1;
 #ifdef CONFIG_IEEE80211W
 	case WLAN_ACTION_SA_QUERY:
-		hostapd_sa_query_action(hapd, mgmt, len);
-		return;
+		return hostapd_sa_query_action(hapd, mgmt, len);
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM
 	case WLAN_ACTION_WNM:
-		hostapd_wnm_action(hapd, sta, mgmt, len);
-		return;
+		ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+		return 1;
 #endif /* CONFIG_WNM */
 	case WLAN_ACTION_PUBLIC:
+	case WLAN_ACTION_PROTECTED_DUAL:
+#ifdef CONFIG_IEEE80211N
+		if (mgmt->u.action.u.public_action.action ==
+		    WLAN_PA_20_40_BSS_COEX) {
+			wpa_printf(MSG_DEBUG,
+				   "HT20/40 coex mgmt frame received from STA "
+				   MACSTR, MAC2STR(mgmt->sa));
+			hostapd_2040_coex_action(hapd, mgmt, len);
+		}
+#endif /* CONFIG_IEEE80211N */
 		if (hapd->public_action_cb) {
 			hapd->public_action_cb(hapd->public_action_cb_ctx,
 					       (u8 *) mgmt, len,
@@ -1640,14 +1667,14 @@
 						hapd->iface->freq);
 		}
 		if (hapd->public_action_cb || hapd->public_action_cb2)
-			return;
+			return 1;
 		break;
 	case WLAN_ACTION_VENDOR_SPECIFIC:
 		if (hapd->vendor_action_cb) {
 			if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
 						   (u8 *) mgmt, len,
 						   hapd->iface->freq) == 0)
-				return;
+				return 1;
 		}
 		break;
 	}
@@ -1670,7 +1697,7 @@
 			   "frame back to sender");
 		resp = os_malloc(len);
 		if (resp == NULL)
-			return;
+			return 0;
 		os_memcpy(resp, mgmt, len);
 		os_memcpy(resp->da, resp->sa, ETH_ALEN);
 		os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
@@ -1683,6 +1710,8 @@
 		}
 		os_free(resp);
 	}
+
+	return 1;
 }
 
 
@@ -1699,15 +1728,16 @@
  * addition, it can be called to re-inserted pending frames (e.g., when using
  * external RADIUS server as an MAC ACL).
  */
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
-		     struct hostapd_frame_info *fi)
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+		    struct hostapd_frame_info *fi)
 {
 	struct ieee80211_mgmt *mgmt;
 	int broadcast;
 	u16 fc, stype;
+	int ret = 0;
 
 	if (len < 24)
-		return;
+		return 0;
 
 	mgmt = (struct ieee80211_mgmt *) buf;
 	fc = le_to_host16(mgmt->frame_control);
@@ -1715,7 +1745,7 @@
 
 	if (stype == WLAN_FC_STYPE_BEACON) {
 		handle_beacon(hapd, mgmt, len, fi);
-		return;
+		return 1;
 	}
 
 	broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
@@ -1729,15 +1759,15 @@
 	      stype == WLAN_FC_STYPE_ACTION) &&
 #endif /* CONFIG_P2P */
 	    os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
-		printf("MGMT: BSSID=" MACSTR " not our address\n",
-		       MAC2STR(mgmt->bssid));
-		return;
+		wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
+			   MAC2STR(mgmt->bssid));
+		return 0;
 	}
 
 
 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
 		handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
-		return;
+		return 1;
 	}
 
 	if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
@@ -1745,33 +1775,38 @@
 			       HOSTAPD_LEVEL_DEBUG,
 			       "MGMT: DA=" MACSTR " not our address",
 			       MAC2STR(mgmt->da));
-		return;
+		return 0;
 	}
 
 	switch (stype) {
 	case WLAN_FC_STYPE_AUTH:
 		wpa_printf(MSG_DEBUG, "mgmt::auth");
 		handle_auth(hapd, mgmt, len);
+		ret = 1;
 		break;
 	case WLAN_FC_STYPE_ASSOC_REQ:
 		wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
 		handle_assoc(hapd, mgmt, len, 0);
+		ret = 1;
 		break;
 	case WLAN_FC_STYPE_REASSOC_REQ:
 		wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
 		handle_assoc(hapd, mgmt, len, 1);
+		ret = 1;
 		break;
 	case WLAN_FC_STYPE_DISASSOC:
 		wpa_printf(MSG_DEBUG, "mgmt::disassoc");
 		handle_disassoc(hapd, mgmt, len);
+		ret = 1;
 		break;
 	case WLAN_FC_STYPE_DEAUTH:
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
 		handle_deauth(hapd, mgmt, len);
+		ret = 1;
 		break;
 	case WLAN_FC_STYPE_ACTION:
 		wpa_printf(MSG_DEBUG, "mgmt::action");
-		handle_action(hapd, mgmt, len);
+		ret = handle_action(hapd, mgmt, len);
 		break;
 	default:
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1779,6 +1814,8 @@
 			       "unknown mgmt frame subtype %d", stype);
 		break;
 	}
+
+	return ret;
 }
 
 
@@ -1797,8 +1834,8 @@
 	}
 
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
-		printf("handle_auth_cb - too short payload (len=%lu)\n",
-		       (unsigned long) len);
+		wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+			   (unsigned long) len);
 		return;
 	}
 
@@ -1808,8 +1845,8 @@
 
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
-		printf("handle_auth_cb: STA " MACSTR " not found\n",
-		       MAC2STR(mgmt->da));
+		wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+			   MAC2STR(mgmt->da));
 		return;
 	}
 
@@ -1859,15 +1896,15 @@
 
 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
 				      sizeof(mgmt->u.assoc_resp))) {
-		printf("handle_assoc_cb(reassoc=%d) - too short payload "
-		       "(len=%lu)\n", reassoc, (unsigned long) len);
+		wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
+			   reassoc, (unsigned long) len);
 		return;
 	}
 
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
-		printf("handle_assoc_cb: STA " MACSTR " not found\n",
-		       MAC2STR(mgmt->da));
+		wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+			   MAC2STR(mgmt->da));
 		return;
 	}
 
@@ -1885,7 +1922,7 @@
 		status = le_to_host16(mgmt->u.assoc_resp.status_code);
 
 	if (status != WLAN_STATUS_SUCCESS)
-		goto fail;
+		return;
 
 	/* Stop previous accounting session, if one is started, and allocate
 	 * new session id for the new session. */
@@ -1899,7 +1936,8 @@
 	if (sta->flags & WLAN_STA_ASSOC)
 		new_assoc = 0;
 	sta->flags |= WLAN_STA_ASSOC;
-	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
 	    sta->auth_alg == WLAN_AUTH_FT) {
 		/*
 		 * Open, static WEP, or FT protocol; no separate authorization
@@ -1938,7 +1976,7 @@
 			    sta->listen_interval,
 			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
 			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
-			    sta->flags, sta->qosinfo)) {
+			    sta->flags, sta->qosinfo, sta->vht_opmode)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_NOTICE,
 			       "Could not add STA to kernel driver");
@@ -1946,7 +1984,7 @@
 		ap_sta_disconnect(hapd, sta, sta->addr,
 				  WLAN_REASON_DISASSOC_AP_BUSY);
 
-		goto fail;
+		return;
 	}
 
 	if (sta->flags & WLAN_STA_WDS) {
@@ -1966,11 +2004,11 @@
 		 * interface selection is not going to change anymore.
 		 */
 		if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
-			goto fail;
+			return;
 	} else if (sta->vlan_id) {
 		/* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
 		if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
-			goto fail;
+			return;
 	}
 
 	hostapd_set_sta_flags(hapd, sta);
@@ -1982,13 +2020,6 @@
 	hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
 
 	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
-
- fail:
-	/* Copy of the association request is not needed anymore */
-	if (sta->last_assoc_req) {
-		os_free(sta->last_assoc_req);
-		sta->last_assoc_req = NULL;
-	}
 }
 
 
@@ -2055,6 +2086,14 @@
 	const struct ieee80211_mgmt *mgmt;
 	mgmt = (const struct ieee80211_mgmt *) buf;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_mgmt_frame_handling) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
+			stype, ok);
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	switch (stype) {
 	case WLAN_FC_STYPE_AUTH:
 		wpa_printf(MSG_DEBUG, "mgmt::auth cb");
@@ -2083,7 +2122,7 @@
 		wpa_printf(MSG_DEBUG, "mgmt::action cb");
 		break;
 	default:
-		printf("unknown mgmt cb frame subtype %d\n", stype);
+		wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
 		break;
 	}
 }
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2aab56d..cf0d3f2 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -14,12 +14,14 @@
 struct sta_info;
 struct hostapd_frame_info;
 struct ieee80211_ht_capabilities;
+struct ieee80211_mgmt;
 
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
-		     struct hostapd_frame_info *fi);
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+		    struct hostapd_frame_info *fi);
 void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
 			u16 stype, int ok);
-void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+			      const struct ieee80211_mgmt *mgmt, size_t len);
 #ifdef NEED_AP_MLME
 int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
 int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
@@ -40,7 +42,9 @@
 #endif /* NEED_AP_MLME */
 u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
 			   int probe);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
 u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
@@ -59,8 +63,12 @@
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab, size_t ht_capab_len);
 void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
 u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_capab, size_t vht_capab_len);
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_opmode);
 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,
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index c311e55..56c3ce0 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -29,7 +29,7 @@
 
 
 struct hostapd_cached_radius_acl {
-	os_time_t timestamp;
+	struct os_reltime timestamp;
 	macaddr addr;
 	int accepted; /* HOSTAPD_ACL_* */
 	struct hostapd_cached_radius_acl *next;
@@ -43,7 +43,7 @@
 
 
 struct hostapd_acl_query_data {
-	os_time_t timestamp;
+	struct os_reltime timestamp;
 	u8 radius_id;
 	macaddr addr;
 	u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
@@ -104,15 +104,16 @@
 				 char **identity, char **radius_cui)
 {
 	struct hostapd_cached_radius_acl *entry;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 
 	for (entry = hapd->acl_cache; entry; entry = entry->next) {
 		if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
 			continue;
 
-		if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
+		if (os_reltime_expired(&now, &entry->timestamp,
+				       RADIUS_ACL_TIMEOUT))
 			return -1; /* entry has expired */
 		if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
 			if (session_timeout)
@@ -265,7 +266,6 @@
 		return HOSTAPD_ACL_REJECT;
 #else /* CONFIG_NO_RADIUS */
 		struct hostapd_acl_query_data *query;
-		struct os_time t;
 
 		/* Check whether ACL cache has an entry for this station */
 		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
@@ -305,8 +305,7 @@
 			wpa_printf(MSG_ERROR, "malloc for query data failed");
 			return HOSTAPD_ACL_REJECT;
 		}
-		os_get_time(&t);
-		query->timestamp = t.sec;
+		os_get_reltime(&query->timestamp);
 		os_memcpy(query->addr, addr, ETH_ALEN);
 		if (hostapd_radius_acl_query(hapd, addr, query)) {
 			wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
@@ -338,7 +337,8 @@
 
 
 #ifndef CONFIG_NO_RADIUS
-static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
+				     struct os_reltime *now)
 {
 	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
 
@@ -346,7 +346,8 @@
 	entry = hapd->acl_cache;
 
 	while (entry) {
-		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+		if (os_reltime_expired(now, &entry->timestamp,
+				       RADIUS_ACL_TIMEOUT)) {
 			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
 				   " has expired.", MAC2STR(entry->addr));
 			if (prev)
@@ -367,7 +368,7 @@
 
 
 static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
-				       os_time_t now)
+				       struct os_reltime *now)
 {
 	struct hostapd_acl_query_data *prev, *entry, *tmp;
 
@@ -375,7 +376,8 @@
 	entry = hapd->acl_queries;
 
 	while (entry) {
-		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+		if (os_reltime_expired(now, &entry->timestamp,
+				       RADIUS_ACL_TIMEOUT)) {
 			wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
 				   " has expired.", MAC2STR(entry->addr));
 			if (prev)
@@ -403,11 +405,11 @@
 static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
 {
 	struct hostapd_data *hapd = eloop_ctx;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
-	hostapd_acl_expire_cache(hapd, now.sec);
-	hostapd_acl_expire_queries(hapd, now.sec);
+	os_get_reltime(&now);
+	hostapd_acl_expire_cache(hapd, &now);
+	hostapd_acl_expire_queries(hapd, &now);
 
 	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 }
@@ -480,7 +482,6 @@
 	struct hostapd_acl_query_data *query, *prev;
 	struct hostapd_cached_radius_acl *cache;
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
-	struct os_time t;
 
 	query = hapd->acl_queries;
 	prev = NULL;
@@ -515,8 +516,7 @@
 		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
 		goto done;
 	}
-	os_get_time(&t);
-	cache->timestamp = t.sec;
+	os_get_reltime(&cache->timestamp);
 	os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
 	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
 		u8 *buf;
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 6483e1c..fe87883 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -3,26 +3,22 @@
  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007-2008, Intel Corporation
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * 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 "common/ieee802_11_defs.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "sta_info.h"
 #include "beacon.h"
 #include "ieee802_11.h"
+#include "hw_features.h"
+#include "ap_drv_ops.h"
 
 
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
@@ -50,6 +46,35 @@
 
  	pos += sizeof(*cap);
 
+	if (hapd->iconf->obss_interval) {
+		struct ieee80211_obss_scan_parameters *scan_params;
+
+		*pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS;
+		*pos++ = sizeof(*scan_params);
+
+		scan_params = (struct ieee80211_obss_scan_parameters *) pos;
+		os_memset(scan_params, 0, sizeof(*scan_params));
+		scan_params->width_trigger_scan_interval =
+			host_to_le16(hapd->iconf->obss_interval);
+
+		/* Fill in default values for remaining parameters
+		 * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
+		scan_params->scan_passive_dwell =
+			host_to_le16(20);
+		scan_params->scan_active_dwell =
+			host_to_le16(10);
+		scan_params->scan_passive_total_per_channel =
+			host_to_le16(200);
+		scan_params->scan_active_total_per_channel =
+			host_to_le16(20);
+		scan_params->channel_transition_delay_factor =
+			host_to_le16(5);
+		scan_params->scan_activity_threshold =
+			host_to_le16(25);
+
+		pos += sizeof(*scan_params);
+	}
+
 	return pos;
 }
 
@@ -68,14 +93,14 @@
 	oper = (struct ieee80211_ht_operation *) pos;
 	os_memset(oper, 0, sizeof(*oper));
 
-	oper->control_chan = hapd->iconf->channel;
+	oper->primary_chan = hapd->iconf->channel;
 	oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
 	if (hapd->iconf->secondary_channel == 1)
 		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
-			HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
 	if (hapd->iconf->secondary_channel == -1)
 		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
-			HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
 
 	pos += sizeof(*oper);
 
@@ -105,44 +130,40 @@
 	wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
 		   __func__, iface->ht_op_mode);
 
-	if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)
+	if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
 	    && iface->num_sta_ht_no_gf) {
-		iface->ht_op_mode |=
-			HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+		iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
 		op_mode_changes++;
 	} else if ((iface->ht_op_mode &
-		    HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+		    HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
 		   iface->num_sta_ht_no_gf == 0) {
-		iface->ht_op_mode &=
-			~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
 		op_mode_changes++;
 	}
 
-	if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+	if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
 	    (iface->num_sta_no_ht || iface->olbc_ht)) {
-		iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+		iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
 		op_mode_changes++;
 	} else if ((iface->ht_op_mode &
-		    HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+		    HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
 		   (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
-		iface->ht_op_mode &=
-			~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
 		op_mode_changes++;
 	}
 
-	new_op_mode = 0;
 	if (iface->num_sta_no_ht)
-		new_op_mode = OP_MODE_MIXED;
+		new_op_mode = HT_PROT_NON_HT_MIXED;
 	else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
-		new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+		new_op_mode = HT_PROT_20MHZ_PROTECTION;
 	else if (iface->olbc_ht)
-		new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+		new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
 	else
-		new_op_mode = OP_MODE_PURE;
+		new_op_mode = HT_PROT_NO_PROTECTION;
 
-	cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+	cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
 	if (cur_op_mode != new_op_mode) {
-		iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
 		iface->ht_op_mode |= new_op_mode;
 		op_mode_changes++;
 	}
@@ -154,6 +175,115 @@
 }
 
 
+static int is_40_allowed(struct hostapd_iface *iface, int channel)
+{
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	int pri = 2407 + 5 * channel;
+
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return 1;
+
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	if ((pri < affected_start || pri > affected_end))
+		return 1; /* not within affected channel range */
+
+	wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+	wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
+	return 0;
+}
+
+
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+			      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct ieee80211_2040_bss_coex_ie *bc_ie;
+	struct ieee80211_2040_intol_chan_report *ic_report;
+	int is_ht_allowed = 1;
+	int i;
+	const u8 *data = ((const u8 *) mgmt) + 1;
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+		       mgmt->u.action.u.public_action.action);
+
+	if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+		return;
+
+	if (len < IEEE80211_HDRLEN + 1)
+		return;
+	data++;
+
+	bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0];
+	ic_report = (struct ieee80211_2040_intol_chan_report *)
+		(&data[0] + sizeof(*bc_ie));
+
+	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+		hostapd_logger(hapd, mgmt->sa,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "20 MHz BSS width request bit is set in BSS coexistence information field");
+		is_ht_allowed = 0;
+	}
+
+	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+		hostapd_logger(hapd, mgmt->sa,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "40 MHz intolerant bit is set in BSS coexistence information field");
+		is_ht_allowed = 0;
+	}
+
+	if (ic_report &&
+	    (ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) {
+		/* Go through the channel report to find any BSS there in the
+		 * affected channel range */
+		for (i = 0; i < ic_report->length - 1; i++) {
+			if (is_40_allowed(iface, ic_report->variable[i]))
+				continue;
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "20_40_INTOLERANT channel %d reported",
+				       ic_report->variable[i]);
+			is_ht_allowed = 0;
+			break;
+		}
+	}
+
+	if (!is_ht_allowed &&
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+		if (iface->conf->secondary_channel) {
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Switching to 20 MHz operation");
+			iface->conf->secondary_channel = 0;
+			ieee802_11_set_beacons(iface);
+		}
+		if (!iface->num_sta_ht40_intolerant) {
+			unsigned int delay_time;
+			delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+				iface->conf->obss_interval;
+			eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
+					     NULL);
+			eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+					       hapd->iface, NULL);
+		}
+	}
+}
+
+
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab, size_t ht_capab_len)
 {
@@ -182,6 +312,52 @@
 }
 
 
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+{
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return;
+
+	wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+		   " in Association Request", MAC2STR(sta->addr));
+
+	if (sta->ht40_intolerant_set)
+		return;
+
+	sta->ht40_intolerant_set = 1;
+	iface->num_sta_ht40_intolerant++;
+	eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+
+	if (iface->conf->secondary_channel &&
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+		iface->conf->secondary_channel = 0;
+		ieee802_11_set_beacons(iface);
+	}
+}
+
+
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
+{
+	if (!sta->ht40_intolerant_set)
+		return;
+
+	sta->ht40_intolerant_set = 0;
+	iface->num_sta_ht40_intolerant--;
+
+	if (iface->num_sta_ht40_intolerant == 0 &&
+	    (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+		unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+			iface->conf->obss_interval;
+		wpa_printf(MSG_DEBUG,
+			   "HT: Start 20->40 MHz transition timer (%d seconds)",
+			   delay_time);
+		eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+		eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+				       iface, NULL);
+	}
+}
+
+
 static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	u16 ht_capab;
@@ -209,6 +385,9 @@
 			   __func__, MAC2STR(sta->addr),
 			   hapd->iface->num_sta_ht_20mhz);
 	}
+
+	if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
+		ht40_intolerant_add(hapd->iface, sta);
 }
 
 
@@ -270,3 +449,14 @@
 
 	neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
 }
+
+
+void ap_ht2040_timeout(void *eloop_data, void *user_data)
+{
+	struct hostapd_iface *iface = eloop_data;
+
+	wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
+
+	iface->conf->secondary_channel = iface->secondary_ch;
+	ieee802_11_set_beacons(iface);
+}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index c36bbe3..12403f9 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -24,13 +24,13 @@
 {
 	u8 *pos = eid;
 	u32 timeout, tu;
-	struct os_time now, passed;
+	struct os_reltime now, passed;
 
 	*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
 	*pos++ = 5;
 	*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
-	os_get_time(&now);
-	os_time_sub(&now, &sta->sa_query_start, &passed);
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &sta->sa_query_start, &passed);
 	tu = (passed.sec * 1000000 + passed.usec) / 1024;
 	if (hapd->conf->assoc_sa_query_max_timeout > tu)
 		timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
@@ -69,7 +69,7 @@
 		  WLAN_SA_QUERY_TR_ID_LEN);
 	end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
 	if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0)
-		perror("ieee802_11_send_sa_query_req: send");
+		wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
 }
 
 
@@ -107,7 +107,7 @@
 		  WLAN_SA_QUERY_TR_ID_LEN);
 	end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
 	if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0)
-		perror("ieee80211_mgmt_sa_query_request: send");
+		wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
 }
 
 
@@ -170,6 +170,8 @@
 
 	switch (idx) {
 	case 0: /* Bits 0-7 */
+		if (hapd->iconf->obss_interval)
+			*pos |= 0x01; /* Bit 0 - Coexistence management */
 		break;
 	case 1: /* Bits 8-15 */
 		break;
@@ -189,6 +191,8 @@
 			*pos |= 0x80; /* Bit 31 - Interworking */
 		break;
 	case 4: /* Bits 32-39 */
+		if (hapd->conf->qos_map_set_len)
+			*pos |= 0x01; /* Bit 32 - QoS Map */
 		if (hapd->conf->tdls & TDLS_PROHIBIT)
 			*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
 		if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
@@ -197,6 +201,10 @@
 		}
 		break;
 	case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+		if (hapd->conf->hs20)
+			*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
 		break;
 	case 6: /* Bits 48-55 */
 		if (hapd->conf->ssid.utf8_ssid)
@@ -217,12 +225,18 @@
 		len = 4;
 	if (len < 3 && hapd->conf->wnm_sleep_mode)
 		len = 3;
+	if (len < 1 && hapd->iconf->obss_interval)
+		len = 1;
 	if (len < 7 && hapd->conf->ssid.utf8_ssid)
 		len = 7;
 #ifdef CONFIG_WNM
 	if (len < 4)
 		len = 4;
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+	if (hapd->conf->hs20 && len < 6)
+		len = 6;
+#endif /* CONFIG_HS20 */
 	if (len < hapd->iface->extended_capa_len)
 		len = hapd->iface->extended_capa_len;
 	if (len == 0)
@@ -250,6 +264,23 @@
 }
 
 
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 len = hapd->conf->qos_map_set_len;
+
+	if (!len)
+		return eid;
+
+	*pos++ = WLAN_EID_QOS_MAP_SET;
+	*pos++ = len;
+	os_memcpy(pos, hapd->conf->qos_map_set, len);
+	pos += len;
+
+	return pos;
+}
+
+
 u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
 {
 	u8 *pos = eid;
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 0012c0f..437cf50 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -12,7 +12,6 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "sta_info.h"
@@ -35,7 +34,7 @@
 	cap = (struct ieee80211_vht_capabilities *) pos;
 	os_memset(cap, 0, sizeof(*cap));
 	cap->vht_capabilities_info = host_to_le32(
-		hapd->iface->current_mode->vht_capab);
+		hapd->iface->conf->vht_capab);
 
 	/* Supported MCS set comes from hw */
 	os_memcpy(&cap->vht_supported_mcs_set,
@@ -109,13 +108,79 @@
 	return WLAN_STATUS_SUCCESS;
 }
 
+
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_oper_notif)
+{
+	if (!vht_oper_notif) {
+		sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
+	sta->vht_opmode = *vht_oper_notif;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
 void hostapd_get_vht_capab(struct hostapd_data *hapd,
 			   struct ieee80211_vht_capabilities *vht_cap,
 			   struct ieee80211_vht_capabilities *neg_vht_cap)
 {
+	u32 cap, own_cap, sym_caps;
+
 	if (vht_cap == NULL)
 		return;
 	os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
 
-	/* TODO: mask own capabilities, like get_ht_capab() */
+	cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
+	own_cap = hapd->iconf->vht_capab;
+
+	/* mask out symmetric VHT capabilities we don't support */
+	sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
+	cap &= ~sym_caps | (own_cap & sym_caps);
+
+	/* mask out beamformer/beamformee caps if not supported */
+	if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+			 VHT_CAP_BEAMFORMEE_STS_MAX);
+
+	if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
+			 VHT_CAP_SOUNDING_DIMENSION_MAX);
+
+	if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+		cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+	if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
+		cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+	/* mask channel widths we don't support */
+	switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+	case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+		break;
+	case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+		if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+			cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+			cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+		}
+		break;
+	default:
+		cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+		break;
+	}
+
+	if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
+		cap &= ~VHT_CAP_SHORT_GI_160;
+
+	/*
+	 * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+	 * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+	 */
+	if (!(own_cap & VHT_CAP_RXSTBC_MASK))
+		cap &= ~VHT_CAP_TXSTBC;
+	if (!(own_cap & VHT_CAP_TXSTBC))
+		cap &= ~VHT_CAP_RXSTBC_MASK;
+
+	neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
 }
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 9867581..e4681e9 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -29,11 +29,14 @@
 #include "pmksa_cache_auth.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "wps_hostapd.h"
+#include "hs20.h"
 #include "ieee802_1x.h"
 
 
 static void ieee802_1x_finished(struct hostapd_data *hapd,
-				struct sta_info *sta, int success);
+				struct sta_info *sta, int success,
+				int remediation);
 
 
 static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@@ -96,12 +99,13 @@
 	}
 
 	if (res && errno != ENOENT) {
-		printf("Could not set station " MACSTR " flags for kernel "
-		       "driver (errno=%d).\n", MAC2STR(sta->addr), errno);
+		wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
+			   " flags for kernel driver (errno=%d).",
+			   MAC2STR(sta->addr), errno);
 	}
 
 	if (authorized) {
-		os_get_time(&sta->connected_time);
+		os_get_reltime(&sta->connected_time);
 		accounting_sta_start(hapd, sta);
 	}
 }
@@ -302,6 +306,67 @@
 }
 
 
+static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
+					  struct hostapd_radius_attr *req_attr,
+					  struct sta_info *sta,
+					  struct radius_msg *msg)
+{
+	u32 suite;
+	int ver, val;
+
+	ver = wpa_auth_sta_wpa_version(sta->wpa_sm);
+	val = wpa_auth_get_pairwise(sta->wpa_sm);
+	suite = wpa_cipher_to_suite(ver, val);
+	if (val != -1 &&
+	    !hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER,
+				       suite)) {
+		wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher");
+		return -1;
+	}
+
+	suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ?
+				    WPA_PROTO_RSN : WPA_PROTO_WPA,
+				    hapd->conf->wpa_group);
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_WLAN_GROUP_CIPHER) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER,
+				       suite)) {
+		wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher");
+		return -1;
+	}
+
+	val = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	suite = wpa_akm_to_suite(val);
+	if (val != -1 &&
+	    !hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_WLAN_AKM_SUITE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+				       suite)) {
+		wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite");
+		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);
+		if (!hostapd_config_get_radius_attr(
+			    req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) &&
+		    !radius_msg_add_attr_int32(
+			    msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add WLAN-Group-Mgmt-Cipher");
+			return -1;
+		}
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	return 0;
+}
+
+
 static int add_common_radius_sta_attr(struct hostapd_data *hapd,
 				      struct hostapd_radius_attr *req_attr,
 				      struct sta_info *sta,
@@ -353,6 +418,25 @@
 		}
 	}
 
+#ifdef CONFIG_IEEE80211R
+	if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+	    sta->wpa_sm &&
+	    (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+	     sta->auth_alg == WLAN_AUTH_FT) &&
+	    !hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_MOBILITY_DOMAIN_ID) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID,
+				       WPA_GET_BE16(
+					       hapd->conf->mobility_domain))) {
+		wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (hapd->conf->wpa && sta->wpa_sm &&
+	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+		return -1;
+
 	return 0;
 }
 
@@ -416,6 +500,22 @@
 		return -1;
 	}
 
+#ifdef CONFIG_INTERWORKING
+	if (hapd->conf->interworking &&
+	    !is_zero_ether_addr(hapd->conf->hessid)) {
+		os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+			    MAC2STR(hapd->conf->hessid));
+		buf[sizeof(buf) - 1] = '\0';
+		if (!hostapd_config_get_radius_attr(req_attr,
+						    RADIUS_ATTR_WLAN_HESSID) &&
+		    !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID,
+					 (u8 *) buf, os_strlen(buf))) {
+			wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID");
+			return -1;
+		}
+	}
+#endif /* CONFIG_INTERWORKING */
+
 	if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
 		return -1;
 
@@ -452,7 +552,7 @@
 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
 			     sm->radius_identifier);
 	if (msg == NULL) {
-		printf("Could not create net RADIUS packet\n");
+		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
 		return;
 	}
 
@@ -461,7 +561,7 @@
 	if (sm->identity &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
 				 sm->identity, sm->identity_len)) {
-		printf("Could not add User-Name\n");
+		wpa_printf(MSG_INFO, "Could not add User-Name");
 		goto fail;
 	}
 
@@ -475,12 +575,12 @@
 	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
 					    RADIUS_ATTR_FRAMED_MTU) &&
 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
-		printf("Could not add Framed-MTU\n");
+		wpa_printf(MSG_INFO, "Could not add Framed-MTU");
 		goto fail;
 	}
 
 	if (eap && !radius_msg_add_eap(msg, eap, len)) {
-		printf("Could not add EAP-Message\n");
+		wpa_printf(MSG_INFO, "Could not add EAP-Message");
 		goto fail;
 	}
 
@@ -492,8 +592,7 @@
 		int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
 					       RADIUS_ATTR_STATE);
 		if (res < 0) {
-			printf("Could not copy State attribute from previous "
-			       "Access-Challenge\n");
+			wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge");
 			goto fail;
 		}
 		if (res > 0) {
@@ -520,6 +619,41 @@
 		}
 	}
 
+#ifdef CONFIG_HS20
+	if (hapd->conf->hs20) {
+		u8 ver = 1; /* Release 2 */
+		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");
+			goto fail;
+		}
+
+		if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) {
+			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) &&
+			    wpabuf_len(sta->hs20_ie) >= 3)
+				id = WPA_GET_LE16(pos + 1);
+			else
+				id = 0;
+			WPA_PUT_BE16(buf + 1, id);
+			if (!radius_msg_add_wfa(
+				    msg,
+				    RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
+				    buf, sizeof(buf))) {
+				wpa_printf(MSG_ERROR, "Could not add HS 2.0 "
+					   "STA version");
+				goto fail;
+			}
+		}
+	}
+#endif /* CONFIG_HS20 */
+
 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
 		goto fail;
 
@@ -543,7 +677,7 @@
 	data = (u8 *) (eap + 1);
 
 	if (len < sizeof(*eap) + 1) {
-		printf("handle_eap_response: too short response data\n");
+		wpa_printf(MSG_INFO, "handle_eap_response: too short response data");
 		return;
 	}
 
@@ -571,7 +705,7 @@
 	u16 eap_len;
 
 	if (len < sizeof(*eap)) {
-		printf("   too short EAP packet\n");
+		wpa_printf(MSG_INFO, "   too short EAP packet");
 		return;
 	}
 
@@ -649,7 +783,7 @@
 	struct rsn_pmksa_cache_entry *pmksa;
 	int key_mgmt;
 
-	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
 	    !hapd->conf->wps_state)
 		return;
 
@@ -664,7 +798,7 @@
 	}
 
 	if (len < sizeof(*hdr)) {
-		printf("   too short IEEE 802.1X packet\n");
+		wpa_printf(MSG_INFO, "   too short IEEE 802.1X packet");
 		return;
 	}
 
@@ -674,7 +808,7 @@
 		   hdr->version, hdr->type, datalen);
 
 	if (len - sizeof(*hdr) < datalen) {
-		printf("   frame too short for this IEEE 802.1X packet\n");
+		wpa_printf(MSG_INFO, "   frame too short for this IEEE 802.1X packet");
 		if (sta->eapol_sm)
 			sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
 		return;
@@ -700,7 +834,7 @@
 		return;
 	}
 
-	if (!hapd->conf->ieee802_1x &&
+	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");
@@ -720,7 +854,7 @@
 			return;
 
 #ifdef CONFIG_WPS
-		if (!hapd->conf->ieee802_1x) {
+		if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
 			u32 wflags = sta->flags & (WLAN_STA_WPS |
 						   WLAN_STA_WPS2 |
 						   WLAN_STA_MAYBE_WPS);
@@ -838,7 +972,7 @@
 	}
 #endif /* CONFIG_WPS */
 
-	if (!force_1x && !hapd->conf->ieee802_1x) {
+	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");
 		/*
@@ -876,7 +1010,8 @@
 
 #ifdef CONFIG_WPS
 	sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
-	if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) {
+	if (!hapd->conf->ieee802_1x && hapd->conf->wps_state &&
+	    !(sta->flags & WLAN_STA_WPS2)) {
 		/*
 		 * Delay EAPOL frame transmission until a possible WPS STA
 		 * initiates the handshake with EAPOL-Start. Only allow the
@@ -1202,6 +1337,147 @@
 }
 
 
+#ifdef CONFIG_HS20
+
+static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
+{
+	sta->remediation = 1;
+	os_free(sta->remediation_url);
+	if (len > 2) {
+		sta->remediation_url = os_malloc(len);
+		if (!sta->remediation_url)
+			return;
+		sta->remediation_method = pos[0];
+		os_memcpy(sta->remediation_url, pos + 1, len - 1);
+		sta->remediation_url[len - 1] = '\0';
+		wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+			   "for " MACSTR " - server method %u URL %s",
+			   MAC2STR(sta->addr), sta->remediation_method,
+			   sta->remediation_url);
+	} else {
+		sta->remediation_url = NULL;
+		wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+			   "for " MACSTR, MAC2STR(sta->addr));
+	}
+	/* TODO: assign the STA into remediation VLAN or add filtering */
+}
+
+
+static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
+				       struct sta_info *sta, u8 *pos,
+				       size_t len)
+{
+	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",
+		   *pos, WPA_GET_LE16(pos + 1));
+	wpabuf_free(sta->hs20_deauth_req);
+	sta->hs20_deauth_req = wpabuf_alloc(len + 1);
+	if (sta->hs20_deauth_req) {
+		wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
+		wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
+		wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+	}
+	ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+}
+
+
+static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *pos,
+					 size_t len, int session_timeout)
+{
+	unsigned int swt;
+	int warning_time, beacon_int;
+
+	if (len < 1)
+		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)
+		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)",
+		   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");
+		return;
+	}
+	if (swt == 255)
+		swt = 1; /* Use one minute as the AP selected value */
+
+	if ((unsigned int) session_timeout < swt * 60)
+		warning_time = 0;
+	else
+		warning_time = session_timeout - swt * 60;
+
+	beacon_int = hapd->iconf->beacon_int;
+	if (beacon_int < 1)
+		beacon_int = 100; /* best guess */
+	sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128;
+	if (sta->hs20_disassoc_timer > 65535)
+		sta->hs20_disassoc_timer = 65535;
+
+	ap_sta_session_warning_timeout(hapd, sta, warning_time);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  struct radius_msg *msg,
+				  int session_timeout)
+{
+#ifdef CONFIG_HS20
+	u8 *buf, *pos, *end, type, sublen;
+	size_t len;
+
+	buf = NULL;
+	sta->remediation = 0;
+	sta->hs20_deauth_requested = 0;
+
+	for (;;) {
+		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+					    &buf, &len, buf) < 0)
+			break;
+		if (len < 6)
+			continue;
+		pos = buf;
+		end = buf + len;
+		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+			continue;
+		pos += 4;
+
+		type = *pos++;
+		sublen = *pos++;
+		if (sublen < 2)
+			continue; /* invalid length */
+		sublen -= 2; /* skip header */
+		if (pos + sublen > end)
+			continue; /* invalid WFA VSA */
+
+		switch (type) {
+		case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
+			ieee802_1x_hs20_sub_rem(sta, pos, sublen);
+			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
+			ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
+			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL:
+			ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
+						     session_timeout);
+			break;
+		}
+	}
+#endif /* CONFIG_HS20 */
+}
+
+
 struct sta_id_search {
 	u8 identifier;
 	struct eapol_state_machine *sm;
@@ -1276,15 +1552,14 @@
 			   "EAP-Message");
 	} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
 				     req, 1)) {
-		printf("Incoming RADIUS packet did not have correct "
-		       "Message-Authenticator - dropped\n");
+		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
 		return RADIUS_RX_INVALID_AUTHENTICATOR;
 	}
 
 	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
 	    hdr->code != RADIUS_CODE_ACCESS_REJECT &&
 	    hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
-		printf("Unknown RADIUS message code\n");
+		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
 		return RADIUS_RX_UNKNOWN;
 	}
 
@@ -1361,7 +1636,11 @@
 		ieee802_1x_store_radius_class(hapd, sta, msg);
 		ieee802_1x_update_sta_identity(hapd, sta, msg);
 		ieee802_1x_update_sta_cui(hapd, sta, msg);
-		if (sm->eap_if->eapKeyAvailable &&
+		ieee802_1x_check_hs20(hapd, sta, msg,
+				      session_timeout_set ?
+				      (int) session_timeout : -1);
+		if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
+		    !sta->hs20_deauth_requested &&
 		    wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
 				       session_timeout_set ?
 				       (int) session_timeout : -1, sm) == 0) {
@@ -1448,7 +1727,7 @@
 	if (eapol->default_wep_key == NULL ||
 	    random_get_bytes(eapol->default_wep_key,
 			     hapd->conf->default_wep_key_len)) {
-		printf("Could not generate random WEP key.\n");
+		wpa_printf(MSG_INFO, "Could not generate random WEP key");
 		os_free(eapol->default_wep_key);
 		eapol->default_wep_key = NULL;
 		return -1;
@@ -1564,14 +1843,14 @@
 
 
 static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
-				 int preauth)
+				 int preauth, int remediation)
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
 	if (preauth)
 		rsn_preauth_finished(hapd, sta, success);
 	else
-		ieee802_1x_finished(hapd, sta, success);
+		ieee802_1x_finished(hapd, sta, success, remediation);
 }
 
 
@@ -1604,7 +1883,9 @@
 		user->password_hash = eap_user->password_hash;
 	}
 	user->force_version = eap_user->force_version;
+	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
+	user->remediation = eap_user->remediation;
 
 	return 0;
 }
@@ -1952,7 +2233,9 @@
 {
 	int len = 0, ret;
 	struct eapol_state_machine *sm = sta->eapol_sm;
-	struct os_time t;
+	struct os_reltime diff;
+	const char *name1;
+	const char *name2;
 
 	if (sm == NULL)
 		return 0;
@@ -2067,7 +2350,7 @@
 	len += ret;
 
 	/* dot1xAuthSessionStatsTable */
-	os_get_time(&t);
+	os_reltime_age(&sta->acct_session_start, &diff);
 	ret = os_snprintf(buf + len, buflen - len,
 			  /* TODO: dot1xAuthSessionOctetsRx */
 			  /* TODO: dot1xAuthSessionOctetsTx */
@@ -2082,26 +2365,73 @@
 			  (wpa_key_mgmt_wpa_ieee8021x(
 				   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
 			  1 : 2,
-			  (unsigned int) (t.sec - sta->acct_session_start),
+			  (unsigned int) diff.sec,
 			  sm->identity);
 	if (ret < 0 || (size_t) ret >= buflen - len)
 		return len;
 	len += ret;
 
+	name1 = eap_server_get_name(0, sm->eap_type_authsrv);
+	name2 = eap_server_get_name(0, sm->eap_type_supp);
+	ret = os_snprintf(buf + len, buflen - len,
+			  "last_eap_type_as=%d (%s)\n"
+			  "last_eap_type_sta=%d (%s)\n",
+			  sm->eap_type_authsrv,
+			  name1 ? name1 : "",
+			  sm->eap_type_supp,
+			  name2 ? name2 : "");
+	if (ret < 0 || (size_t) ret >= buflen - len)
+		return len;
+	len += ret;
+
 	return len;
 }
 
 
 static void ieee802_1x_finished(struct hostapd_data *hapd,
-				struct sta_info *sta, int success)
+				struct sta_info *sta, int success,
+				int remediation)
 {
 	const u8 *key;
 	size_t len;
 	/* TODO: get PMKLifetime from WPA parameters */
 	static const int dot11RSNAConfigPMKLifetime = 43200;
 
+#ifdef CONFIG_HS20
+	if (remediation && !sta->remediation) {
+		sta->remediation = 1;
+		os_free(sta->remediation_url);
+		sta->remediation_url =
+			os_strdup(hapd->conf->subscr_remediation_url);
+		sta->remediation_method = 1; /* SOAP-XML SPP */
+	}
+
+	if (success) {
+		if (sta->remediation) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+				   "to " MACSTR " to indicate Subscription "
+				   "Remediation",
+				   MAC2STR(sta->addr));
+			hs20_send_wnm_notification(hapd, sta->addr,
+						   sta->remediation_method,
+						   sta->remediation_url);
+			os_free(sta->remediation_url);
+			sta->remediation_url = NULL;
+		}
+
+		if (sta->hs20_deauth_req) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+				   "to " MACSTR " to indicate imminent "
+				   "deauthentication", MAC2STR(sta->addr));
+			hs20_send_wnm_notification_deauth_req(
+				hapd, sta->addr, sta->hs20_deauth_req);
+		}
+	}
+#endif /* CONFIG_HS20 */
+
 	key = ieee802_1x_get_key(sta->eapol_sm, &len);
-	if (success && key && len >= PMK_LEN &&
+	if (success && key && len >= PMK_LEN && !sta->remediation &&
+	    !sta->hs20_deauth_requested &&
 	    wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime,
 			       sta->eapol_sm) == 0) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
@@ -2127,12 +2457,8 @@
 		 * driver reorder operations.
 		 */
 		os_sleep(0, 10000);
-#ifndef ANDROID_P2P
-		/* We need not do this for driver. For AP-SME flags if we send this disassoc,
-		  * the p2p_client is gettig disassoc after it has completed the assoc
-		  */
 		ap_sta_disconnect(hapd, sta, sta->addr,
 				  WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
-#endif
+		hostapd_wps_eap_completed(hapd);
 	}
 }
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
index 795d313..9be640c 100644
--- a/src/ap/p2p_hostapd.c
+++ b/src/ap/p2p_hostapd.c
@@ -96,9 +96,8 @@
 	u8 bitmap;
 	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
 	*eid++ = 4 + 3 + 1;
-	WPA_PUT_BE24(eid, OUI_WFA);
-	eid += 3;
-	*eid++ = P2P_OUI_TYPE;
+	WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+	eid += 4;
 
 	*eid++ = P2P_ATTR_MANAGEABILITY;
 	WPA_PUT_LE16(eid, 1);
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
index ba5c606..612babc 100644
--- a/src/ap/peerkey_auth.c
+++ b/src/ap/peerkey_auth.c
@@ -221,8 +221,8 @@
 		return;
 
 	/* Peer RSN IE */
-	os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len);
-	pos = buf + kde->rsn_ie_len;
+	os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
+	pos += kde->rsn_ie_len;
 
 	/* Peer MAC Address */
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 40972e9..4720b59 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -91,9 +91,9 @@
 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
 		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
 			   MACSTR, MAC2STR(pmksa->pmksa->spa));
@@ -107,12 +107,12 @@
 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
 {
 	int sec;
-	struct os_time now;
+	struct os_reltime now;
 
 	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
 	if (pmksa->pmksa == NULL)
 		return;
-	os_get_time(&now);
+	os_get_reltime(&now);
 	sec = pmksa->pmksa->expiration - now.sec;
 	if (sec < 0)
 		sec = 0;
@@ -241,7 +241,7 @@
 		struct eapol_state_machine *eapol, int akmp)
 {
 	struct rsn_pmksa_cache_entry *entry, *pos;
-	struct os_time now;
+	struct os_reltime now;
 
 	if (pmk_len > PMK_LEN)
 		return NULL;
@@ -253,7 +253,7 @@
 	entry->pmk_len = pmk_len;
 	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
 		  wpa_key_mgmt_sha256(akmp));
-	os_get_time(&now);
+	os_get_reltime(&now);
 	entry->expiration = now.sec;
 	if (session_timeout > 0)
 		entry->expiration += session_timeout;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 6704c09..60f0768 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -15,7 +15,6 @@
 #include "common/sae.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
-#include "drivers/driver.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
 #include "accounting.h"
@@ -31,11 +30,13 @@
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
 #include "gas_serv.h"
+#include "wnm_ap.h"
 #include "sta_info.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
 				       struct sta_info *sta);
 static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+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
@@ -155,7 +156,8 @@
 	if (sta->flags & WLAN_STA_WDS)
 		hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
 
-	if (!(sta->flags & WLAN_STA_PREAUTH))
+	if (!hapd->iface->driver_ap_teardown &&
+	    !(sta->flags & WLAN_STA_PREAUTH))
 		hostapd_drv_sta_remove(hapd, sta->addr);
 
 	ap_sta_hash_del(hapd, sta);
@@ -204,6 +206,10 @@
 		hapd->iface->num_sta_ht_20mhz--;
 	}
 
+#ifdef CONFIG_IEEE80211N
+	ht40_intolerant_remove(hapd->iface, sta);
+#endif /* CONFIG_IEEE80211N */
+
 #ifdef CONFIG_P2P
 	if (sta->no_p2p_set) {
 		sta->no_p2p_set = 0;
@@ -225,6 +231,7 @@
 		   __func__, MAC2STR(sta->addr));
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
 	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
 
@@ -232,10 +239,10 @@
 	wpa_auth_sta_deinit(sta->wpa_sm);
 	rsn_preauth_free_station(hapd, sta);
 #ifndef CONFIG_NO_RADIUS
-	radius_client_flush_auth(hapd->radius, sta->addr);
+	if (hapd->radius)
+		radius_client_flush_auth(hapd->radius, sta->addr);
 #endif /* CONFIG_NO_RADIUS */
 
-	os_free(sta->last_assoc_req);
 	os_free(sta->challenge);
 
 #ifdef CONFIG_IEEE80211W
@@ -261,9 +268,13 @@
 	wpabuf_free(sta->hs20_ie);
 
 	os_free(sta->ht_capabilities);
+	os_free(sta->vht_capabilities);
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
+	os_free(sta->remediation_url);
+	wpabuf_free(sta->hs20_deauth_req);
+	os_free(sta->hs20_session_info_url);
 
 #ifdef CONFIG_SAE
 	sae_clear_data(sta->sae);
@@ -465,7 +476,6 @@
 {
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
-	u8 addr[ETH_ALEN];
 
 	if (!(sta->flags & WLAN_STA_AUTH)) {
 		if (sta->flags & WLAN_STA_GAS) {
@@ -476,6 +486,8 @@
 		return;
 	}
 
+	hostapd_drv_sta_deauth(hapd, sta->addr,
+			       WLAN_REASON_PREV_AUTH_NOT_VALID);
 	mlme_deauthenticate_indication(hapd, sta,
 				       WLAN_REASON_PREV_AUTH_NOT_VALID);
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -483,9 +495,19 @@
 		       "session timeout");
 	sta->acct_terminate_cause =
 		RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
-	os_memcpy(addr, sta->addr, ETH_ALEN);
 	ap_free_sta(hapd, sta);
-	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			      u32 session_timeout)
+{
+	if (eloop_replenish_timeout(session_timeout, 0,
+				    ap_handle_session_timer, hapd, sta) == 1) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "setting session timeout "
+			       "to %d seconds", session_timeout);
+	}
 }
 
 
@@ -507,6 +529,32 @@
 }
 
 
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
+{
+#ifdef CONFIG_WNM
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR,
+		   MAC2STR(sta->addr));
+	if (sta->hs20_session_info_url == NULL)
+		return;
+
+	wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
+				       sta->hs20_disassoc_timer);
+#endif /* CONFIG_WNM */
+}
+
+
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+				    struct sta_info *sta, int warning_time)
+{
+	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+	eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer,
+			       hapd, sta);
+}
+
+
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
 {
 	struct sta_info *sta;
@@ -531,13 +579,16 @@
 	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
 	accounting_sta_get_id(hapd, sta);
 
+	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)",
+			   __func__, MAC2STR(addr),
+			   hapd->conf->ap_max_inactivity);
+		eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+				       ap_handle_timer, hapd, sta);
+	}
+
 	/* initialize STA info data */
-	wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
-		   "for " MACSTR " (%d seconds - ap_max_inactivity)",
-		   __func__, MAC2STR(addr),
-		   hapd->conf->ap_max_inactivity);
-	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
-			       ap_handle_timer, hapd, sta);
 	os_memcpy(sta->addr, addr, ETH_ALEN);
 	sta->next = hapd->sta_list;
 	hapd->sta_list = sta;
@@ -808,9 +859,9 @@
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	u32 tu;
-	struct os_time now, passed;
-	os_get_time(&now);
-	os_time_sub(&now, &sta->sa_query_start, &passed);
+	struct os_reltime now, passed;
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &sta->sa_query_start, &passed);
 	tu = (passed.sec * 1000000 + passed.usec) / 1024;
 	if (hapd->conf->assoc_sa_query_max_timeout < tu) {
 		hostapd_logger(hapd, sta->addr,
@@ -847,7 +898,7 @@
 		return;
 	if (sta->sa_query_count == 0) {
 		/* Starting a new SA Query procedure */
-		os_get_time(&sta->sa_query_start);
+		os_get_reltime(&sta->sa_query_start);
 	}
 	trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
 	sta->sa_query_trans_id = nbuf;
@@ -892,6 +943,7 @@
 	char buf[100];
 #ifdef CONFIG_P2P
 	u8 addr[ETH_ALEN];
+	u8 ip_addr_buf[4];
 #endif /* CONFIG_P2P */
 
 	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
@@ -913,12 +965,25 @@
 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
 
 	if (authorized) {
-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s", buf);
+		char ip_addr[100];
+		ip_addr[0] = '\0';
+#ifdef CONFIG_P2P
+		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+			os_snprintf(ip_addr, sizeof(ip_addr),
+				    " ip_addr=%u.%u.%u.%u",
+				    ip_addr_buf[0], ip_addr_buf[1],
+				    ip_addr_buf[2], ip_addr_buf[3]);
+		}
+#endif /* CONFIG_P2P */
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
+			buf, ip_addr);
 
 		if (hapd->msg_ctx_parent &&
 		    hapd->msg_ctx_parent != hapd->msg_ctx)
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-					  AP_STA_CONNECTED "%s", buf);
+					  AP_STA_CONNECTED "%s%s",
+					  buf, ip_addr);
 
 		sta->flags |= WLAN_STA_AUTHORIZED;
 	} else {
@@ -995,3 +1060,33 @@
 	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
 	ap_sta_disassoc_cb_timeout(hapd, sta);
 }
+
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
+{
+	int res;
+
+	buf[0] = '\0';
+	res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
+			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
+			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
+			  (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
+			   ""),
+			  (flags & WLAN_STA_SHORT_PREAMBLE ?
+			   "[SHORT_PREAMBLE]" : ""),
+			  (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
+			  (flags & WLAN_STA_WMM ? "[WMM]" : ""),
+			  (flags & WLAN_STA_MFP ? "[MFP]" : ""),
+			  (flags & WLAN_STA_WPS ? "[WPS]" : ""),
+			  (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
+			  (flags & WLAN_STA_WDS ? "[WDS]" : ""),
+			  (flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
+			  (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
+			  (flags & WLAN_STA_GAS ? "[GAS]" : ""),
+			  (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
+			   "[WNM_SLEEP_MODE]" : ""));
+
+	return res;
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e5b5069..03db98f 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -12,9 +12,6 @@
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
 #define WLAN_STA_ASSOC BIT(1)
-#define WLAN_STA_PS BIT(2)
-#define WLAN_STA_TIM BIT(3)
-#define WLAN_STA_PERM BIT(4)
 #define WLAN_STA_AUTHORIZED BIT(5)
 #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
 #define WLAN_STA_SHORT_PREAMBLE BIT(7)
@@ -29,6 +26,8 @@
 #define WLAN_STA_WPS2 BIT(16)
 #define WLAN_STA_GAS BIT(17)
 #define WLAN_STA_VHT BIT(18)
+#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
+#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -55,11 +54,14 @@
 	unsigned int no_short_preamble_set:1;
 	unsigned int no_ht_gf_set:1;
 	unsigned int no_ht_set:1;
+	unsigned int ht40_intolerant_set:1;
 	unsigned int ht_20mhz_set:1;
 	unsigned int no_p2p_set:1;
+	unsigned int qos_map_enabled:1;
+	unsigned int remediation:1;
+	unsigned int hs20_deauth_requested:1;
 
 	u16 auth_alg;
-	u8 previous_ap[6];
 
 	enum {
 		STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
@@ -72,12 +74,9 @@
 	/* IEEE 802.1X related data */
 	struct eapol_state_machine *eapol_sm;
 
-	/* IEEE 802.11f (IAPP) related data */
-	struct ieee80211_mgmt *last_assoc_req;
-
 	u32 acct_session_id_hi;
 	u32 acct_session_id_lo;
-	time_t acct_session_start;
+	struct os_reltime acct_session_start;
 	int acct_session_started;
 	int acct_terminate_cause; /* Acct-Terminate-Cause */
 	int acct_interim_interval; /* Acct-Interim-Interval */
@@ -104,6 +103,7 @@
 
 	struct ieee80211_ht_capabilities *ht_capabilities;
 	struct ieee80211_vht_capabilities *vht_capabilities;
+	u8 vht_opmode;
 
 #ifdef CONFIG_IEEE80211W
 	int sa_query_count; /* number of pending SA Query requests;
@@ -112,7 +112,7 @@
 	u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
 				* sa_query_count octets of pending SA Query
 				* transaction identifiers */
-	struct os_time sa_query_start;
+	struct os_reltime sa_query_start;
 #endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_INTERWORKING
@@ -124,8 +124,13 @@
 	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
 	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
 	struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+	u8 remediation_method;
+	char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+	struct wpabuf *hs20_deauth_req;
+	char *hs20_session_info_url;
+	int hs20_disassoc_timer;
 
-	struct os_time connected_time;
+	struct os_reltime connected_time;
 
 #ifdef CONFIG_SAE
 	struct sae_data *sae;
@@ -161,10 +166,14 @@
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
 void hostapd_free_stas(struct hostapd_data *hapd);
 void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			      u32 session_timeout);
 void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
 			    u32 session_timeout);
 void ap_sta_no_session_timeout(struct hostapd_data *hapd,
 			       struct sta_info *sta);
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+				    struct sta_info *sta, int warning_time);
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
 void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
 			 u16 reason);
@@ -192,4 +201,6 @@
 void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
 
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+
 #endif /* STA_INFO_H */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 4a2ea06..4725e2b 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -68,7 +68,7 @@
 
 int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
 {
-	struct os_time now;
+	struct os_reltime now;
 	int ret = 0;
 
 	if (addr && local) {
@@ -89,8 +89,8 @@
 		}
 	}
 
-	os_get_time(&now);
-	if (now.sec > hapd->michael_mic_failure + 60) {
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
 		hapd->michael_mic_failures = 1;
 	} else {
 		hapd->michael_mic_failures++;
@@ -99,7 +99,7 @@
 			ret = 1;
 		}
 	}
-	hapd->michael_mic_failure = now.sec;
+	hapd->michael_mic_failure = now;
 
 	return ret;
 }
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 746af40..4e4a352 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -480,123 +474,6 @@
 #endif /* CONFIG_VLAN_NETLINK */
 
 
-/**
- * Increase the usage counter for given parent/ifname combination.
- * If create is set, then this iface is added to the global list.
- * Returns
- * 	-1 on error
- * 	0 if iface is not in list
- * 	1 if iface is in list (was there or has been added)
- */
-static int hapd_get_dynamic_iface(const char *parent, const char *ifname,
-				  int create, struct hostapd_data *hapd)
-{
-	size_t i;
-	struct hostapd_dynamic_iface *j = NULL, **tmp;
-	struct hapd_interfaces *hapd_global = hapd->iface->interfaces;
-
-	if (!parent)
-		parent = "";
-
-	for (i = 0; i < hapd_global->count_dynamic; i++) {
-		j = hapd_global->dynamic_iface[i];
-		if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 &&
-		    os_strncmp(j->parent, parent, sizeof(j->parent)) == 0)
-			break;
-	}
-	if (i < hapd_global->count_dynamic) {
-		j->usage++;
-		return 1;
-	}
-
-	/* new entry required */
-	if (!create)
-		return 0;
-
-	j = os_zalloc(sizeof(*j));
-	if (!j)
-		return -1;
-	os_strlcpy(j->iface, ifname, sizeof(j->iface));
-	os_strlcpy(j->parent, parent, sizeof(j->parent));
-
-	tmp = os_realloc_array(hapd_global->dynamic_iface, i + 1,
-			       sizeof(*hapd_global->dynamic_iface));
-	if (!tmp) {
-		wpa_printf(MSG_ERROR, "VLAN: Failed to allocate memory in %s",
-			   __func__);
-		return -1;
-	}
-	hapd_global->count_dynamic++;
-	hapd_global->dynamic_iface = tmp;
-	hapd_global->dynamic_iface[i] = j;
-
-	return 1;
-}
-
-
-/**
- * Decrease the usage counter for given ifname.
- * Returns
- *     -1 on error or if iface was not found
- *     0 if iface was found and is still present
- *     1 if iface was removed from global list
- */
-static int hapd_put_dynamic_iface(const char *parent, const char *ifname,
-				  struct hostapd_data *hapd)
-{
-	size_t i;
-	struct hostapd_dynamic_iface *j = NULL, **tmp;
-	struct hapd_interfaces *hapd_glob = hapd->iface->interfaces;
-
-	if (!parent)
-		parent = "";
-
-	for (i = 0; i < hapd_glob->count_dynamic; i++) {
-		j = hapd_glob->dynamic_iface[i];
-		if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 &&
-		    os_strncmp(j->parent, parent, sizeof(j->parent)) == 0)
-			break;
-	}
-
-	if (i == hapd_glob->count_dynamic) {
-		/*
-		 * Interface not in global list. This can happen if alloc in
-		 * _get_ failed.
-		 */
-		return -1;
-	}
-
-	if (j->usage > 0) {
-		j->usage--;
-		return 0;
-	}
-
-	os_free(j);
-	for (; i < hapd_glob->count_dynamic - 1; i++)
-		hapd_glob->dynamic_iface[i] = hapd_glob->dynamic_iface[i + 1];
-	hapd_glob->dynamic_iface[hapd_glob->count_dynamic - 1] = NULL;
-	hapd_glob->count_dynamic--;
-
-	if (hapd_glob->count_dynamic == 0) {
-		os_free(hapd_glob->dynamic_iface);
-		hapd_glob->dynamic_iface = NULL;
-		return 1;
-	}
-
-	tmp = os_realloc_array(hapd_glob->dynamic_iface,
-			       hapd_glob->count_dynamic,
-			       sizeof(*hapd_glob->dynamic_iface));
-	if (!tmp) {
-		wpa_printf(MSG_ERROR, "VLAN: Failed to release memory in %s",
-			   __func__);
-		return -1;
-	}
-	hapd_glob->dynamic_iface = tmp;
-
-	return 1;
-}
-
-
 static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
 {
 	char vlan_ifname[IFNAMSIZ];
@@ -604,7 +481,6 @@
 	struct hostapd_vlan *vlan = hapd->conf->vlan;
 	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
 	int vlan_naming = hapd->conf->ssid.vlan_naming;
-	int ret;
 
 	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
 
@@ -624,9 +500,7 @@
 				            "brvlan%d", vlan->vlan_id);
 			}
 
-			ret = br_addbr(br_name);
-			if (hapd_get_dynamic_iface(NULL, br_name, ret == 0,
-			                           hapd))
+			if (!br_addbr(br_name))
 				vlan->clean |= DVLAN_CLEAN_BR;
 
 			ifconfig_up(br_name);
@@ -644,24 +518,17 @@
 						    "vlan%d", vlan->vlan_id);
 
 				ifconfig_up(tagged_interface);
-				ret = vlan_add(tagged_interface, vlan->vlan_id,
-					      vlan_ifname);
-				if (hapd_get_dynamic_iface(NULL, vlan_ifname,
-				                           ret == 0, hapd))
+				if (!vlan_add(tagged_interface, vlan->vlan_id,
+					      vlan_ifname))
 					vlan->clean |= DVLAN_CLEAN_VLAN;
 
-				ret = br_addif(br_name, vlan_ifname);
-				if (hapd_get_dynamic_iface(br_name,
-							   vlan_ifname,
-							   ret == 0, hapd))
+				if (!br_addif(br_name, vlan_ifname))
 					vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
 
 				ifconfig_up(vlan_ifname);
 			}
 
-			ret = br_addif(br_name, ifname);
-			if (hapd_get_dynamic_iface(br_name, ifname, ret == 0,
-						   hapd))
+			if (!br_addif(br_name, ifname))
 				vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
 
 			ifconfig_up(ifname);
@@ -700,8 +567,7 @@
 				            "brvlan%d", vlan->vlan_id);
 			}
 
-			if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) &&
-			    hapd_put_dynamic_iface(br_name, vlan->ifname, hapd))
+			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
 				br_delif(br_name, vlan->ifname);
 
 			if (tagged_interface) {
@@ -715,20 +581,15 @@
 					os_snprintf(vlan_ifname,
 						    sizeof(vlan_ifname),
 						    "vlan%d", vlan->vlan_id);
-				if ((vlan->clean & DVLAN_CLEAN_VLAN_PORT) &&
-				    hapd_put_dynamic_iface(br_name, vlan_ifname,
-							   hapd))
+				if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
 					br_delif(br_name, vlan_ifname);
 				ifconfig_down(vlan_ifname);
 
-				if ((vlan->clean & DVLAN_CLEAN_VLAN) &&
-				    hapd_put_dynamic_iface(NULL, vlan_ifname,
-							   hapd))
+				if (vlan->clean & DVLAN_CLEAN_VLAN)
 					vlan_rem(vlan_ifname);
 			}
 
 			if ((vlan->clean & DVLAN_CLEAN_BR) &&
-			    hapd_put_dynamic_iface(NULL, br_name, hapd) &&
 			    br_getnumports(br_name) == 0) {
 				ifconfig_down(br_name);
 				br_delbr(br_name);
@@ -1021,6 +882,7 @@
 
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 	full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+	hapd->full_dynamic_vlan = NULL;
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 }
 
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index 382d5de..781eaac 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -3,14 +3,8 @@
  * Copyright 2003, Instant802 Networks, Inc.
  * Copyright 2005, Devicescape Software, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef VLAN_INIT_H
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index d21c82f..6d4177c 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -157,7 +151,7 @@
 	len = ((u8 *) (t + 1)) - buf;
 
 	if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
-		perror("wmm_send_action: send");
+		wpa_printf(MSG_INFO, "wmm_send_action: send failed");
 }
 
 
diff --git a/src/ap/wmm.h b/src/ap/wmm.h
index 96b04e8..b70b863 100644
--- a/src/ap/wmm.h
+++ b/src/ap/wmm.h
@@ -3,14 +3,8 @@
  * Copyright 2002-2003, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef WME_H
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 54a6b85..cf25dbb 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - WNM
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
@@ -72,7 +73,7 @@
 	wnmsleep_ie.len = wnmsleep_ie_len - 2;
 	wnmsleep_ie.action_type = action_type;
 	wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
-	wnmsleep_ie.intval = intval;
+	wnmsleep_ie.intval = host_to_le16(intval);
 
 	/* TFS IE(s) */
 	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
@@ -154,6 +155,7 @@
 		 */
 		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
 		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
+			sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
 					     addr, NULL, NULL);
 			wpa_set_wnmsleep(sta->wpa_sm, 1);
@@ -167,6 +169,7 @@
 		     wnmsleep_ie.status ==
 		     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
 		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
+			sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
 			wpa_set_wnmsleep(sta->wpa_sm, 0);
 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
 					     addr, NULL, NULL);
@@ -233,7 +236,7 @@
 
 	ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
 				      wnmsleep_ie->action_type,
-				      wnmsleep_ie->intval);
+				      le_to_host16(wnmsleep_ie->intval));
 
 	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
 		/* clear the tfs after sending the resp frame */
@@ -243,29 +246,262 @@
 }
 
 
-int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-				struct rx_action *action)
+static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+						  const u8 *addr,
+						  u8 dialog_token,
+						  const char *url)
 {
-	if (action->len < 1 || action->data == NULL)
+	struct ieee80211_mgmt *mgmt;
+	size_t url_len, len;
+	u8 *pos;
+	int res;
+
+	if (url)
+		url_len = os_strlen(url);
+	else
+		url_len = 0;
+
+	mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0));
+	if (mgmt == NULL)
+		return -1;
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+	mgmt->u.action.u.bss_tm_req.req_mode = 0;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+	if (url) {
+		*pos++ += url_len;
+		os_memcpy(pos, url, url_len);
+		pos += url_len;
+	}
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
+		   "validity_interval=%u",
+		   MAC2STR(addr), dialog_token,
+		   mgmt->u.action.u.bss_tm_req.req_mode,
+		   le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
+		   mgmt->u.action.u.bss_tm_req.validity_interval);
+
+	len = pos - &mgmt->u.action.category;
+	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+				      mgmt->da, &mgmt->u.action.category, len);
+	os_free(mgmt);
+	return res;
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+					       const u8 *addr, const u8 *frm,
+					       size_t len)
+{
+	u8 dialog_token, reason;
+	const u8 *pos, *end;
+
+	if (len < 2) {
+		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	pos = frm;
+	end = pos + len;
+	dialog_token = *pos++;
+	reason = *pos++;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
+		   MACSTR " dialog_token=%u reason=%u",
+		   MAC2STR(addr), dialog_token, reason);
+
+	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+		    pos, end - pos);
+
+	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL);
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+					      const u8 *addr, const u8 *frm,
+					      size_t len)
+{
+	u8 dialog_token, status_code, bss_termination_delay;
+	const u8 *pos, *end;
+
+	if (len < 3) {
+		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	pos = frm;
+	end = pos + len;
+	dialog_token = *pos++;
+	status_code = *pos++;
+	bss_termination_delay = *pos++;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
+		   MACSTR " dialog_token=%u status_code=%u "
+		   "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
+		   status_code, bss_termination_delay);
+
+	if (status_code == WNM_BSS_TM_ACCEPT) {
+		if (end - pos < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
+			   MAC2STR(pos));
+		pos += ETH_ALEN;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+		    pos, end - pos);
+}
+
+
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u8 action;
+	const u8 *payload;
+	size_t plen;
+
+	if (len < IEEE80211_HDRLEN + 2)
 		return -1;
 
-	switch (action->data[0]) {
+	payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+	action = *payload++;
+	plen = len - IEEE80211_HDRLEN - 2;
+
+	switch (action) {
 	case WNM_BSS_TRANS_MGMT_QUERY:
-		wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query");
-		/* TODO */
-		return -1;
+		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+						   plen);
+		return 0;
 	case WNM_BSS_TRANS_MGMT_RESP:
-		wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
-			   "Response");
-		/* TODO */
-		return -1;
+		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+						  plen);
+		return 0;
 	case WNM_SLEEP_MODE_REQ:
-		ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1,
-					   action->len - 1);
+		ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
 		return 0;
 	}
 
 	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
-		   action->data[0], MAC2STR(action->sa));
+		   action, MAC2STR(mgmt->sa));
 	return -1;
 }
+
+
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+			       struct sta_info *sta, int disassoc_timer)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+
+	os_memset(buf, 0, sizeof(buf));
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+	mgmt->u.action.u.bss_tm_req.req_mode =
+		WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+			   "Management Request frame");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+				   struct sta_info *sta, const char *url,
+				   int disassoc_timer)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t url_len;
+
+	os_memset(buf, 0, sizeof(buf));
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+	mgmt->u.action.u.bss_tm_req.req_mode =
+		WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
+		WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	/* Session Information URL */
+	url_len = os_strlen(url);
+	if (url_len > 255)
+		return -1;
+	*pos++ = url_len;
+	os_memcpy(pos, url, url_len);
+	pos += url_len;
+
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+			   "Management Request frame");
+		return -1;
+	}
+
+	/* send disassociation frame after time-out */
+	if (disassoc_timer) {
+		int timeout, beacon_int;
+
+		/*
+		 * Prevent STA from reconnecting using cached PMKSA to force
+		 * full authentication with the authentication server (which may
+		 * decide to reject the connection),
+		 */
+		wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+		beacon_int = hapd->iconf->beacon_int;
+		if (beacon_int < 1)
+			beacon_int = 100; /* best guess */
+		/* Calculate timeout in ms based on beacon_int in TU */
+		timeout = disassoc_timer * beacon_int * 128 / 125;
+		wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+			   " set to %d ms", MAC2STR(sta->addr), timeout);
+
+		sta->timeout_next = STA_DISASSOC_FROM_CLI;
+		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+		eloop_register_timeout(timeout / 1000,
+				       timeout % 1000 * 1000,
+				       ap_handle_timer, hapd, sta);
+	}
+
+	return 0;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index f05726e..eeaf5ec 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11v WNM related functions and structures
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,9 +9,14 @@
 #ifndef WNM_AP_H
 #define WNM_AP_H
 
-struct rx_action;
+struct sta_info;
 
 int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-				struct rx_action *action);
+				const struct ieee80211_mgmt *mgmt, size_t len);
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+			       struct sta_info *sta, int disassoc_timer);
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+				   struct sta_info *sta, const char *url,
+				   int disassoc_timer);
 
 #endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 0286c5b..2bb8aab 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/state_machine.h"
+#include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
@@ -210,6 +211,8 @@
 	if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
 		ret = 1;
 #endif /* CONFIG_IEEE80211W */
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+		ret = 1;
 	return ret;
 }
 
@@ -424,6 +427,17 @@
 				       wpa_rekey_gtk, wpa_auth, NULL);
 	}
 
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(conf->ip_addr_start)) {
+		int count = WPA_GET_BE32(conf->ip_addr_end) -
+			WPA_GET_BE32(conf->ip_addr_start) + 1;
+		if (count > 1000)
+			count = 1000;
+		if (count > 0)
+			wpa_auth->ip_pool = bitfield_alloc(count);
+	}
+#endif /* CONFIG_P2P */
+
 	return wpa_auth;
 }
 
@@ -437,6 +451,8 @@
 	wpa_group_sm_step(wpa_auth, group);
 	group->GInit = FALSE;
 	wpa_group_sm_step(wpa_auth, group);
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
 	return 0;
 }
 
@@ -464,6 +480,11 @@
 	wpa_auth->ft_pmk_cache = NULL;
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_P2P
+	bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
 	os_free(wpa_auth->wpa_ie);
 
 	group = wpa_auth->group;
@@ -516,6 +537,9 @@
 {
 	struct wpa_state_machine *sm;
 
+	if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return NULL;
+
 	sm = os_zalloc(sizeof(struct wpa_state_machine));
 	if (sm == NULL)
 		return NULL;
@@ -541,6 +565,8 @@
 		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 				"FT authentication already completed - do not "
 				"start 4-way handshake");
+		/* Go to PTKINITDONE state to allow GTK rekeying */
+		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
 		return 0;
 	}
 #endif /* CONFIG_IEEE80211R */
@@ -578,12 +604,26 @@
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr)) {
+		u32 start;
+		wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+			   "address %u.%u.%u.%u from " MACSTR,
+			   sm->ip_addr[0], sm->ip_addr[1],
+			   sm->ip_addr[2], sm->ip_addr[3],
+			   MAC2STR(sm->addr));
+		start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+		bitfield_clear(sm->wpa_auth->ip_pool,
+			       WPA_GET_BE32(sm->ip_addr) - start);
+	}
+#endif /* CONFIG_P2P */
 	if (sm->GUpdateStationKeys) {
 		sm->group->GKeyDoneStations--;
 		sm->GUpdateStationKeys = FALSE;
 	}
 #ifdef CONFIG_IEEE80211R
 	os_free(sm->assoc_resp_ftie);
+	wpabuf_free(sm->ft_pending_req_ies);
 #endif /* CONFIG_IEEE80211R */
 	os_free(sm->last_rx_eapol_key);
 	os_free(sm->wpa_ie);
@@ -843,6 +883,7 @@
 		if (sm->pairwise == WPA_CIPHER_CCMP ||
 		    sm->pairwise == WPA_CIPHER_GCMP) {
 			if (wpa_use_aes_cmac(sm) &&
+			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -966,6 +1007,9 @@
 		if (kde.rsn_ie) {
 			eapol_key_ie = kde.rsn_ie;
 			eapol_key_ie_len = kde.rsn_ie_len;
+		} else if (kde.osen) {
+			eapol_key_ie = kde.osen;
+			eapol_key_ie_len = kde.osen_len;
 		} else {
 			eapol_key_ie = kde.wpa_ie;
 			eapol_key_ie_len = kde.wpa_ie_len;
@@ -995,6 +1039,26 @@
 			return;
 		}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+		if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+		    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+			int idx;
+			wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+				   "EAPOL-Key exchange");
+			idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+			if (idx >= 0) {
+				u32 start = WPA_GET_BE32(wpa_auth->conf.
+							 ip_addr_start);
+				bitfield_set(wpa_auth->ip_pool, idx);
+				WPA_PUT_BE32(sm->ip_addr, start + idx);
+				wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+					   "address %u.%u.%u.%u to " MACSTR,
+					   sm->ip_addr[0], sm->ip_addr[1],
+					   sm->ip_addr[2], sm->ip_addr[3],
+					   MAC2STR(sm->addr));
+			}
+		}
+#endif /* CONFIG_P2P */
 		break;
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1231,6 +1295,8 @@
 
 	if (force_version)
 		version = force_version;
+	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
 	else if (wpa_use_aes_cmac(sm))
 		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
 	else if (sm->pairwise != WPA_CIPHER_TKIP)
@@ -1253,6 +1319,7 @@
 	key_data_len = kde_len;
 
 	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
 	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
 		pad_len = key_data_len % 8;
 		if (pad_len)
@@ -1321,6 +1388,7 @@
 		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
 				buf, key_data_len);
 		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
 		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 			if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf,
 				     (u8 *) (key + 1))) {
@@ -1355,7 +1423,7 @@
 				  key->key_mic);
 #ifdef CONFIG_TESTING_OPTIONS
 		if (!pairwise &&
-		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d &&
+		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
 		    drand48() <
 		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -1422,7 +1490,7 @@
 	os_memset(key->key_mic, 0, 16);
 	if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK,
 			      data, data_len, key->key_mic) ||
-	    os_memcmp(mic, key->key_mic, 16) != 0)
+	    os_memcmp_const(mic, key->key_mic, 16) != 0)
 		ret = -1;
 	os_memcpy(key->key_mic, mic, 16);
 	return ret;
@@ -1719,7 +1787,8 @@
 	 * one possible PSK for this STA.
 	 */
 	if (sm->wpa == WPA_VERSION_WPA2 &&
-	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
+	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+	    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
 		pmkid = buf;
 		pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
 		pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
@@ -1747,7 +1816,7 @@
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
 			  struct wpa_ptk *ptk)
 {
-	size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64;
+	size_t ptk_len = wpa_cipher_key_len(sm->pairwise) + 32;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
@@ -1808,8 +1877,8 @@
 		 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
 		 * with the value we derived.
 		 */
-		if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
-			      WPA_PMK_NAME_LEN) != 0) {
+		if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0) {
 			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 					"PMKR1Name mismatch in FT 4-way "
 					"handshake");
@@ -1852,7 +1921,9 @@
 static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 {
 	if (sm->mgmt_frame_prot) {
-		return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde);
+		size_t len;
+		len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+		return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
 	}
 
 	return 0;
@@ -1863,6 +1934,8 @@
 {
 	struct wpa_igtk_kde igtk;
 	struct wpa_group *gsm = sm->group;
+	u8 rsc[WPA_KEY_RSC_LEN];
+	size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
 
 	if (!sm->mgmt_frame_prot)
 		return pos;
@@ -1870,19 +1943,22 @@
 	igtk.keyid[0] = gsm->GN_igtk;
 	igtk.keyid[1] = 0;
 	if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
-	    wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
+	    wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
 		os_memset(igtk.pn, 0, sizeof(igtk.pn));
-	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+	else
+		os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
 	if (sm->wpa_auth->conf.disable_gtk) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use of
 		 * IGTK in the BSS.
 		 */
-		if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0)
+		if (random_get_bytes(igtk.igtk, len) < 0)
 			return pos;
 	}
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
-			  (const u8 *) &igtk, sizeof(igtk), NULL, 0);
+			  (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+			  NULL, 0);
 
 	return pos;
 }
@@ -1987,6 +2063,10 @@
 		kde_len += 300; /* FTIE + 2 * TIE */
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0)
+		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
 	kde = os_malloc(kde_len);
 	if (kde == NULL)
 		return;
@@ -2048,6 +2128,16 @@
 		pos += 4;
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0) {
+		u8 addr[3 * 4];
+		os_memcpy(addr, sm->ip_addr, 4);
+		os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+		os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+				  addr, sizeof(addr), NULL, 0);
+	}
+#endif /* CONFIG_P2P */
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
@@ -2374,15 +2464,16 @@
 
 #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);
 		os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
 		inc_byte_array(group->Counter, WPA_NONCE_LEN);
 		if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
 				   wpa_auth->addr, group->GNonce,
-				   group->IGTK[group->GN_igtk - 4],
-				   WPA_IGTK_LEN) < 0)
+				   group->IGTK[group->GN_igtk - 4], len) < 0)
 			ret = -1;
 		wpa_hexdump_key(MSG_DEBUG, "IGTK",
-				group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
+				group->IGTK[group->GN_igtk - 4], len);
 	}
 #endif /* CONFIG_IEEE80211W */
 
@@ -2449,7 +2540,7 @@
 /* update GTK when exiting WNM-Sleep Mode */
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 {
-	if (sm->is_wnmsleep)
+	if (sm == NULL || sm->is_wnmsleep)
 		return;
 
 	wpa_group_update_sta(sm, NULL);
@@ -2458,7 +2549,8 @@
 
 void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
 {
-	sm->is_wnmsleep = !!flag;
+	if (sm)
+		sm->is_wnmsleep = !!flag;
 }
 
 
@@ -2498,26 +2590,27 @@
 {
 	struct wpa_group *gsm = sm->group;
 	u8 *start = pos;
+	size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
 
 	/*
 	 * IGTK subelement:
 	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
 	 */
 	*pos++ = WNM_SLEEP_SUBELEM_IGTK;
-	*pos++ = 2 + 6 + WPA_IGTK_LEN;
+	*pos++ = 2 + 6 + len;
 	WPA_PUT_LE16(pos, gsm->GN_igtk);
 	pos += 2;
 	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
 		return 0;
 	pos += 6;
 
-	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
-	pos += WPA_IGTK_LEN;
+	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+	pos += len;
 
 	wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
 		   gsm->GN_igtk);
 	wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
-			gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+			gsm->IGTK[gsm->GN_igtk - 4], len);
 
 	return pos - start;
 }
@@ -2572,18 +2665,48 @@
 		ret = -1;
 
 #ifdef CONFIG_IEEE80211W
-	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
-	    wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
-			     broadcast_ether_addr, group->GN_igtk,
-			     group->IGTK[group->GN_igtk - 4],
-			     WPA_IGTK_LEN) < 0)
-		ret = -1;
+	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		enum wpa_alg alg;
+		size_t len;
+
+		alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher);
+		len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+		if (ret == 0 &&
+		    wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+				     broadcast_ether_addr, group->GN_igtk,
+				     group->IGTK[group->GN_igtk - 4], len) < 0)
+			ret = -1;
+	}
 #endif /* CONFIG_IEEE80211W */
 
 	return ret;
 }
 
 
+static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	if (sm->group == ctx) {
+		wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
+			   " for discconnection due to fatal failure",
+			   MAC2STR(sm->addr));
+		sm->Disconnect = TRUE;
+	}
+
+	return 0;
+}
+
+
+static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
+				    struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE");
+	group->changed = TRUE;
+	group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
+	wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
+}
+
+
 static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
 				 struct wpa_group *group)
 {
@@ -2592,8 +2715,10 @@
 	group->changed = TRUE;
 	group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
 
-	if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+	if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+		wpa_group_fatal_failure(wpa_auth, group);
 		return -1;
+	}
 
 	return 0;
 }
@@ -2604,6 +2729,8 @@
 {
 	if (group->GInit) {
 		wpa_group_gtk_init(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) {
+		/* Do not allow group operations */
 	} else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
 		   group->GTKAuthenticator) {
 		wpa_group_setkeysdone(wpa_auth, group);
@@ -3012,6 +3139,9 @@
 	if (sm->group == group)
 		return 0;
 
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+
 	wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
 		   "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
 
@@ -3056,3 +3186,22 @@
 		return 0;
 	return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
 }
+
+
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
+{
+	if (sm == NULL)
+		return 0;
+	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+	if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+		return -1;
+	os_memcpy(addr, sm->ip_addr, 4);
+	return 0;
+}
+#endif /* CONFIG_P2P */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 47503d0..929a253 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -42,6 +42,7 @@
 #define FT_R0KH_R1KH_PULL_DATA_LEN 44
 #define FT_R0KH_R1KH_RESP_DATA_LEN 76
 #define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
 
 struct ft_r0kh_r1kh_pull_frame {
 	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
@@ -49,7 +50,7 @@
 	le16 data_length; /* little endian length of data (44) */
 	u8 ap_address[ETH_ALEN];
 
-	u8 nonce[16];
+	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
 	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
 	u8 r1kh_id[FT_R1KH_ID_LEN];
 	u8 s1kh_id[ETH_ALEN];
@@ -63,7 +64,7 @@
 	le16 data_length; /* little endian length of data (76) */
 	u8 ap_address[ETH_ALEN];
 
-	u8 nonce[16]; /* copied from pull */
+	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
 	u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
 	u8 s1kh_id[ETH_ALEN]; /* copied from pull */
 	u8 pmk_r1[PMK_LEN];
@@ -142,6 +143,7 @@
 	int tx_status;
 #ifdef CONFIG_IEEE80211W
 	enum mfp_options ieee80211w;
+	int group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
 #define SSID_LEN 32
@@ -163,6 +165,12 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	double corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 };
 
 typedef enum {
@@ -203,7 +211,7 @@
 	int (*send_ft_action)(void *ctx, const u8 *dst,
 			      const u8 *data, size_t data_len);
 	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
-                         size_t tspec_ielen);
+			 size_t tspec_ielen);
 #endif /* CONFIG_IEEE80211R */
 };
 
@@ -226,6 +234,9 @@
 			struct wpa_state_machine *sm,
 			const u8 *wpa_ie, size_t wpa_ie_len,
 			const u8 *mdie, size_t mdie_len);
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm,
+		      const u8 *osen_ie, size_t osen_ie_len);
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
 struct wpa_state_machine *
 wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
@@ -295,5 +306,8 @@
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
 
 int wpa_auth_uses_sae(struct wpa_state_machine *sm);
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
+
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
 
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 29d9d29..8a6ca71 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
@@ -22,6 +23,12 @@
 
 #ifdef CONFIG_IEEE80211R
 
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+				     const u8 *current_ap, const u8 *sta_addr,
+				     u16 status, const u8 *resp_ies,
+				     size_t resp_ies_len);
+
+
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
 			   const u8 *data, size_t data_len)
 {
@@ -57,7 +64,7 @@
 			    u8 *tspec_ie, size_t tspec_ielen)
 {
 	if (wpa_auth->cb.add_tspec == NULL) {
-	        wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+		wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
 		return -1;
 	}
 	return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
@@ -228,8 +235,8 @@
 	r0 = cache->pmk_r0;
 	while (r0) {
 		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
-		    os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN)
-		    == 0) {
+		    os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
+				    WPA_PMK_NAME_LEN) == 0) {
 			os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
 			if (pairwise)
 				*pairwise = r0->pairwise;
@@ -278,8 +285,8 @@
 	r1 = cache->pmk_r1;
 	while (r1) {
 		if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
-		    os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN)
-		    == 0) {
+		    os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
+				    WPA_PMK_NAME_LEN) == 0) {
 			os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
 			if (pairwise)
 				*pairwise = r1->pairwise;
@@ -293,22 +300,26 @@
 }
 
 
-static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
-			      const u8 *s1kh_id, const u8 *r0kh_id,
-			      size_t r0kh_id_len, const u8 *pmk_r0_name)
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+			      const u8 *ies, size_t ies_len,
+			      const u8 *pmk_r0_name)
 {
 	struct ft_remote_r0kh *r0kh;
 	struct ft_r0kh_r1kh_pull_frame frame, f;
 
-	r0kh = wpa_auth->conf.r0kh_list;
+	r0kh = sm->wpa_auth->conf.r0kh_list;
 	while (r0kh) {
-		if (r0kh->id_len == r0kh_id_len &&
-		    os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0)
+		if (r0kh->id_len == sm->r0kh_id_len &&
+		    os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
+		    0)
 			break;
 		r0kh = r0kh->next;
 	}
-	if (r0kh == NULL)
+	if (r0kh == NULL) {
+		wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
 		return -1;
+	}
 
 	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
 		   "address " MACSTR, MAC2STR(r0kh->addr));
@@ -317,25 +328,32 @@
 	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
 	frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
 	frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
-	os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+	os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
 
 	/* aes_wrap() does not support inplace encryption, so use a temporary
 	 * buffer for the data. */
-	if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
+	if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
 			   "nonce");
 		return -1;
 	}
+	os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
+		  FT_R0KH_R1KH_PULL_NONCE_LEN);
 	os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
-	os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
-	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+	os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+	os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
 	os_memset(f.pad, 0, sizeof(f.pad));
 
 	if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
 		     f.nonce, frame.nonce) < 0)
 		return -1;
 
-	wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+	wpabuf_free(sm->ft_pending_req_ies);
+	sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+	if (sm->ft_pending_req_ies == NULL)
+		return -1;
+
+	wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
 
 	return 0;
 }
@@ -570,8 +588,8 @@
 			else {
 				/* TSPEC accepted; include updated TSPEC in
 				 * response */
-		                rdie->descr_count = 1;
-	                        pos += sizeof(*tspec);
+				rdie->descr_count = 1;
+				pos += sizeof(*tspec);
 			}
 			return pos;
 		}
@@ -633,8 +651,7 @@
 
 	conf = &sm->wpa_auth->conf;
 
-	if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-	    sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK)
+	if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return pos;
 
 	end = pos + max_len;
@@ -778,7 +795,7 @@
 }
 
 
-static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 				   const u8 *ies, size_t ies_len,
 				   u8 **resp_ies, size_t *resp_ies_len)
 {
@@ -849,19 +866,13 @@
 
 	if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
 		    &pairwise) < 0) {
-		if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id,
-				       sm->r0kh_id_len, parse.rsn_pmkid) < 0) {
+		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
 			wpa_printf(MSG_DEBUG, "FT: Did not have matching "
 				   "PMK-R1 and unknown R0KH-ID");
 			return WLAN_STATUS_INVALID_PMKID;
 		}
 
-		/*
-		 * TODO: Should return "status pending" (and the caller should
-		 * not send out response now). The real response will be sent
-		 * once the response from R0KH is received.
-		 */
-		return WLAN_STATUS_INVALID_PMKID;
+		return -1; /* Status pending */
 	}
 
 	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
@@ -888,6 +899,7 @@
 	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
 
 	sm->pairwise = pairwise;
+	sm->PTK_valid = TRUE;
 	wpa_ft_install_ptk(sm);
 
 	buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
@@ -941,6 +953,7 @@
 	u16 status;
 	u8 *resp_ies;
 	size_t resp_ies_len;
+	int res;
 
 	if (sm == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
@@ -951,8 +964,16 @@
 	wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
 		   " BSSID=" MACSTR " transaction=%d",
 		   MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
-	status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
-					 &resp_ies_len);
+	sm->ft_pending_cb = cb;
+	sm->ft_pending_cb_ctx = ctx;
+	sm->ft_pending_auth_transaction = auth_transaction;
+	res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+				      &resp_ies_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
+		return;
+	}
+	status = res;
 
 	wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
 		   " auth_transaction=%d status=%d",
@@ -993,8 +1014,8 @@
 		return WLAN_STATUS_INVALID_PMKID;
 	}
 
-	if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
-	{
+	if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
+	    != 0) {
 		wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
 			   "with the PMKR1Name derived from auth request");
 		return WLAN_STATUS_INVALID_PMKID;
@@ -1040,7 +1061,8 @@
 	}
 
 	if (parse.r0kh_id_len != sm->r0kh_id_len ||
-	    os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+	    os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+	{
 		wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
 			   "the current R0KH-ID");
 		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
@@ -1055,8 +1077,8 @@
 		return -1;
 	}
 
-	if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
-		      FT_R1KH_ID_LEN) != 0) {
+	if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
 			   "ReassocReq");
 		wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
@@ -1067,7 +1089,8 @@
 	}
 
 	if (parse.rsn_pmkid == NULL ||
-	    os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+	    os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+	{
 		wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
 			   "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
 		return -1;
@@ -1093,7 +1116,7 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	if (os_memcmp(mic, ftie->mic, 16) != 0) {
+	if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
 		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
 			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
@@ -1183,15 +1206,27 @@
 }
 
 
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+				     u16 auth_transaction, u16 resp,
+				     const u8 *ies, size_t ies_len)
+{
+	struct wpa_state_machine *sm = ctx;
+	wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
+		   MAC2STR(sm->addr));
+	wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
+				  WLAN_STATUS_SUCCESS, ies, ies_len);
+}
+
+
 static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
 				 const u8 *current_ap, const u8 *sta_addr,
 				 const u8 *body, size_t len)
 {
 	struct wpa_state_machine *sm;
 	u16 status;
-	u8 *resp_ies, *pos;
-	size_t resp_ies_len, rlen;
-	struct ft_rrb_frame *frame;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+	int res;
 
 	sm = wpa_ft_add_sta(wpa_auth, sta_addr);
 	if (sm == NULL) {
@@ -1202,8 +1237,33 @@
 
 	wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
 
-	status = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
-					 &resp_ies_len);
+	sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+	sm->ft_pending_cb_ctx = sm;
+	os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+	res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+				      &resp_ies_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
+		return 0;
+	}
+	status = res;
+
+	res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
+					resp_ies, resp_ies_len);
+	os_free(resp_ies);
+	return res;
+}
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+				     const u8 *current_ap, const u8 *sta_addr,
+				     u16 status, const u8 *resp_ies,
+				     size_t resp_ies_len)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	size_t rlen;
+	struct ft_rrb_frame *frame;
+	u8 *pos;
 
 	wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
 		   " CurrentAP=" MACSTR " status=%d",
@@ -1219,10 +1279,8 @@
 	rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
 
 	frame = os_malloc(sizeof(*frame) + rlen);
-	if (frame == NULL) {
-		os_free(resp_ies);
+	if (frame == NULL)
 		return -1;
-	}
 	frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
 	frame->packet_type = FT_PACKET_RESPONSE;
 	frame->action_length = host_to_le16(rlen);
@@ -1236,10 +1294,8 @@
 	pos += ETH_ALEN;
 	WPA_PUT_LE16(pos, status);
 	pos += 2;
-	if (resp_ies) {
+	if (resp_ies)
 		os_memcpy(pos, resp_ies, resp_ies_len);
-		os_free(resp_ies);
-	}
 
 	wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
 			sizeof(*frame) + rlen);
@@ -1291,7 +1347,7 @@
 		    f.nonce, sizeof(f.nonce));
 	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
 		    f.pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
 		   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
 
 	os_memset(&resp, 0, sizeof(resp));
@@ -1334,13 +1390,58 @@
 }
 
 
+static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+	int res;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+	u16 status;
+
+	res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+				      wpabuf_len(sm->ft_pending_req_ies),
+				      &resp_ies, &resp_ies_len);
+	wpabuf_free(sm->ft_pending_req_ies);
+	sm->ft_pending_req_ies = NULL;
+	if (res < 0)
+		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	status = res;
+	wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+		   " - status %u", MAC2STR(sm->addr), status);
+
+	sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+			  sm->ft_pending_auth_transaction + 1, status,
+			  resp_ies, resp_ies_len);
+	os_free(resp_ies);
+}
+
+
+static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+
+	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
+		return 0;
+	if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
+		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
+		return 0;
+	if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
+		   MACSTR " - process from timeout", MAC2STR(sm->addr));
+	eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+	return 1;
+}
+
+
 static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
 			      const u8 *data, size_t data_len)
 {
 	struct ft_r0kh_r1kh_resp_frame *frame, f;
 	struct ft_remote_r0kh *r0kh;
-	int pairwise;
+	int pairwise, res;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
 
@@ -1370,21 +1471,17 @@
 		return -1;
 	}
 
-	if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
-	    != 0) {
+	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
 			   "matching R1KH-ID");
 		return -1;
 	}
 
-	/* TODO: verify that <nonce,s1kh_id> matches with a pending request
-	 * and call this requests callback function to finish request
-	 * processing */
-
 	pairwise = le_to_host16(f.pairwise);
 	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
 		    f.nonce, sizeof(f.nonce));
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
 		   MACSTR " pairwise=0x%x",
 		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
@@ -1392,11 +1489,13 @@
 	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
 			f.pmk_r1_name, WPA_PMK_NAME_LEN);
 
-	wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-			    pairwise);
+	res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+				  pairwise);
+	wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
+	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
 	os_memset(f.pmk_r1, 0, PMK_LEN);
 
-	return 0;
+	return res ? 0 : -1;
 }
 
 
@@ -1448,8 +1547,8 @@
 		return -1;
 	}
 
-	if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
-	    != 0) {
+	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
 			   "R1KH-ID (received " MACSTR " own " MACSTR ")",
 			   MAC2STR(f.r1kh_id),
@@ -1590,6 +1689,11 @@
 		return -1;
 	}
 
+	if (end > pos) {
+		wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end",
+			    pos, end - pos);
+	}
+
 	return 0;
 }
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index d977b42..6ee9a4f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -15,7 +15,6 @@
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
 #include "l2_packet/l2_packet.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "preauth_auth.h"
@@ -50,6 +49,7 @@
 	wconf->okc = conf->okc;
 #ifdef CONFIG_IEEE80211W
 	wconf->ieee80211w = conf->ieee80211w;
+	wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
 	wconf->ssid_len = conf->ssid.ssid_len;
@@ -74,11 +74,30 @@
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_HS20
 	wconf->disable_gtk = conf->disable_dgaf;
+	if (conf->osen) {
+		wconf->disable_gtk = 1;
+		wconf->wpa = WPA_PROTO_OSEN;
+		wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+		wconf->wpa_pairwise = 0;
+		wconf->wpa_group = WPA_CIPHER_CCMP;
+		wconf->rsn_pairwise = WPA_CIPHER_CCMP;
+		wconf->rsn_preauth = 0;
+		wconf->disable_pmksa_caching = 1;
+#ifdef CONFIG_IEEE80211W
+		wconf->ieee80211w = 1;
+#endif /* CONFIG_IEEE80211W */
+	}
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_TESTING_OPTIONS
 	wconf->corrupt_gtk_rekey_mic_probability =
 		iconf->corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+	os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+	os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+	os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
 }
 
 
@@ -618,5 +637,6 @@
 
 #ifdef CONFIG_IEEE80211R
 	l2_packet_deinit(hapd->l2);
+	hapd->l2 = NULL;
 #endif /* CONFIG_IEEE80211R */
 }
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 12e59bc..6960ff3 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -118,9 +118,22 @@
 	u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
 					       * message 2/4 */
 	u8 *assoc_resp_ftie;
+
+	void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+			      u16 auth_transaction, u16 status,
+			      const u8 *ies, size_t ies_len);
+	void *ft_pending_cb_ctx;
+	struct wpabuf *ft_pending_req_ies;
+	u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+	u8 ft_pending_auth_transaction;
+	u8 ft_pending_current_ap[ETH_ALEN];
 #endif /* CONFIG_IEEE80211R */
 
 	int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+	u8 ip_addr[4];
+#endif /* CONFIG_P2P */
 };
 
 
@@ -139,7 +152,8 @@
 
 	enum {
 		WPA_GROUP_GTK_INIT = 0,
-		WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE
+		WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
+		WPA_GROUP_FATAL_FAILURE
 	} wpa_group_state;
 
 	u8 GMK[WPA_GMK_LEN];
@@ -149,7 +163,7 @@
 	Boolean first_sta_seen;
 	Boolean reject_4way_hs_for_entropy;
 #ifdef CONFIG_IEEE80211W
-	u8 IGTK[2][WPA_IGTK_LEN];
+	u8 IGTK[2][WPA_IGTK_MAX_LEN];
 	int GN_igtk, GM_igtk;
 #endif /* CONFIG_IEEE80211W */
 };
@@ -184,6 +198,10 @@
 
 	struct rsn_pmksa_cache *pmksa;
 	struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+	struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
 };
 
 
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index cdfcca1..1e4defc 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -261,7 +261,25 @@
 		}
 
 		/* Management Group Cipher Suite */
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+		switch (conf->group_mgmt_cipher) {
+		case WPA_CIPHER_AES_128_CMAC:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+			break;
+		case WPA_CIPHER_BIP_GMAC_128:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+			break;
+		case WPA_CIPHER_BIP_GMAC_256:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+			break;
+		case WPA_CIPHER_BIP_CMAC_256:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "Invalid group management cipher (0x%x)",
+				   conf->group_mgmt_cipher);
+			return -1;
+		}
 		pos += RSN_SELECTOR_LEN;
 	}
 #endif /* CONFIG_IEEE80211W */
@@ -295,6 +313,55 @@
 }
 
 
+static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
+{
+	u8 *len;
+	u16 capab;
+
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	len = eid++; /* to be filled */
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = HS20_OSEN_OUI_TYPE;
+
+	/* Group Data Cipher Suite */
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	eid += RSN_SELECTOR_LEN;
+
+	/* Pairwise Cipher Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+	eid += RSN_SELECTOR_LEN;
+
+	/* AKM Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+	eid += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities */
+	capab = 0;
+	if (conf->wmm_enabled) {
+		/* 4 PTKSA replay counters when using WMM */
+		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+	}
+#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 */
+	WPA_PUT_LE16(eid, capab);
+	eid += 2;
+
+	*len = eid - len - 1;
+
+	return eid;
+}
+
+
 int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 {
 	u8 *pos, buf[128];
@@ -302,6 +369,9 @@
 
 	pos = buf;
 
+	if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
+		pos = wpa_write_osen(&wpa_auth->conf, pos);
+	}
 	if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
 		res = wpa_write_rsn_ie(&wpa_auth->conf,
 				       pos, buf + sizeof(buf) - pos, NULL);
@@ -534,7 +604,8 @@
 			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
 		}
 
-		if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+		if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
+		{
 			wpa_printf(MSG_DEBUG, "Unsupported management group "
 				   "cipher %d", data.mgmt_group_cipher);
 			return WPA_INVALID_MGMT_GROUP_CIPHER;
@@ -604,7 +675,7 @@
 			break;
 		}
 	}
-	if (sm->pmksa) {
+	if (sm->pmksa && pmkid) {
 		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 				 "PMKID found from PMKSA cache "
 				 "eap_type=%d vlan_id=%d",
@@ -626,6 +697,36 @@
 }
 
 
+#ifdef CONFIG_HS20
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm,
+		      const u8 *osen_ie, size_t osen_ie_len)
+{
+	if (wpa_auth == NULL || sm == NULL)
+		return -1;
+
+	/* TODO: parse OSEN element */
+	sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+	sm->mgmt_frame_prot = 1;
+	sm->pairwise = WPA_CIPHER_CCMP;
+	sm->wpa = WPA_VERSION_WPA2;
+
+	if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
+		os_free(sm->wpa_ie);
+		sm->wpa_ie = os_malloc(osen_ie_len);
+		if (sm->wpa_ie == NULL)
+			return -1;
+	}
+
+	os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
+	sm->wpa_ie_len = osen_ie_len;
+
+	return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
 /**
  * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
  * @pos: Pointer to the IE header
@@ -648,6 +749,12 @@
 		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 (pos + 1 + RSN_SELECTOR_LEN < end &&
 	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
@@ -708,6 +815,25 @@
 	}
 #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 */
+
 	return 0;
 }
 
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 4999139..d2067ba 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -39,6 +39,13 @@
 	const u8 *ftie;
 	size_t ftie_len;
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
+
+	const u8 *osen;
+	size_t osen_len;
 };
 
 int wpa_parse_kde_ies(const u8 *buf, size_t len,
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index eb300f1..6f16f50 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -40,6 +40,7 @@
 				    const u8 *ie, size_t ie_len,
 				    int ssi_signal);
 static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+static void hostapd_wps_nfc_clear(struct wps_context *wps);
 
 
 struct wps_for_each_data {
@@ -287,6 +288,20 @@
 }
 
 
+void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+	/*
+	 * Reduce race condition of the station trying to reconnect immediately
+	 * after AP reconfiguration through WPS by rescheduling the reload
+	 * timeout to happen after EAP completion rather than the originally
+	 * scheduled 100 ms after new configuration became known.
+	 */
+	if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) ==
+	    1)
+		wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload");
+}
+
+
 static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
 			      size_t attr_len)
 {
@@ -363,40 +378,11 @@
 		}
 		bss->auth_algs = 1;
 	} else {
-		if ((cred->auth_type & WPS_AUTH_OPEN) &&
-		    (cred->auth_type & WPS_AUTH_SHARED))
-			bss->auth_algs = 3;
-		else if (cred->auth_type & WPS_AUTH_SHARED)
-			bss->auth_algs = 2;
-		else
-			bss->auth_algs = 1;
-		if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx > 0 &&
-		    cred->key_idx <= 4) {
-			struct hostapd_wep_keys *wep = &bss->ssid.wep;
-			int idx = cred->key_idx;
-			if (idx)
-				idx--;
-			wep->idx = idx;
-			if (cred->key_len == 10 || cred->key_len == 26) {
-				os_free(wep->key[idx]);
-				wep->key[idx] = os_malloc(cred->key_len / 2);
-				if (wep->key[idx] == NULL ||
-				    hexstr2bin((const char *) cred->key,
-					       wep->key[idx],
-					       cred->key_len / 2))
-					return -1;
-				wep->len[idx] = cred->key_len / 2;
-			} else {
-				os_free(wep->key[idx]);
-				wep->key[idx] = os_malloc(cred->key_len);
-				if (wep->key[idx] == NULL)
-					return -1;
-				os_memcpy(wep->key[idx], cred->key,
-					  cred->key_len);
-				wep->len[idx] = cred->key_len;
-			}
-			wep->keys_set = 1;
-		}
+		/*
+		 * WPS 2.0 does not allow WEP to be configured, so no need to
+		 * process that option here either.
+		 */
+		bss->auth_algs = 1;
 	}
 
 	/* Schedule configuration reload after short period of time to allow
@@ -457,6 +443,8 @@
 	hapd->wps->ssid_len = cred->ssid_len;
 	hapd->wps->encr_types = cred->encr_type;
 	hapd->wps->auth_types = cred->auth_type;
+	hapd->wps->ap_encr_type = cred->encr_type;
+	hapd->wps->ap_auth_type = cred->auth_type;
 	if (cred->key_len == 0) {
 		os_free(hapd->wps->network_key);
 		hapd->wps->network_key = NULL;
@@ -569,31 +557,11 @@
 
 		fprintf(nconf, "auth_algs=1\n");
 	} else {
-		if ((cred->auth_type & WPS_AUTH_OPEN) &&
-		    (cred->auth_type & WPS_AUTH_SHARED))
-			fprintf(nconf, "auth_algs=3\n");
-		else if (cred->auth_type & WPS_AUTH_SHARED)
-			fprintf(nconf, "auth_algs=2\n");
-		else
-			fprintf(nconf, "auth_algs=1\n");
-
-		if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) {
-			int key_idx = cred->key_idx;
-			if (key_idx)
-				key_idx--;
-			fprintf(nconf, "wep_default_key=%d\n", key_idx);
-			fprintf(nconf, "wep_key%d=", key_idx);
-			if (cred->key_len == 10 || cred->key_len == 26) {
-				/* WEP key as a hex string */
-				for (i = 0; i < cred->key_len; i++)
-					fputc(cred->key[i], nconf);
-			} else {
-				/* Raw WEP key; convert to hex */
-				for (i = 0; i < cred->key_len; i++)
-					fprintf(nconf, "%02x", cred->key[i]);
-			}
-			fprintf(nconf, "\n");
-		}
+		/*
+		 * WPS 2.0 does not allow WEP to be configured, so no need to
+		 * process that option here either.
+		 */
+		fprintf(nconf, "auth_algs=1\n");
 	}
 
 	fprintf(nconf, "# WPS configuration - END\n");
@@ -881,7 +849,7 @@
 }
 
 
-static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
 {
 	wpabuf_free(hapd->wps_beacon_ie);
 	hapd->wps_beacon_ie = NULL;
@@ -889,6 +857,9 @@
 	wpabuf_free(hapd->wps_probe_resp_ie);
 	hapd->wps_probe_resp_ie = NULL;
 
+	if (deinit_only)
+		return;
+
 	hostapd_set_ap_wps_ie(hapd);
 }
 
@@ -971,6 +942,21 @@
 }
 
 
+static void hostapd_free_wps(struct wps_context *wps)
+{
+	int i;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+		wpabuf_free(wps->dev.vendor_ext[i]);
+	wps_device_data_free(&wps->dev);
+	os_free(wps->network_key);
+	hostapd_wps_nfc_clear(wps);
+	wpabuf_free(wps->dh_pubkey);
+	wpabuf_free(wps->dh_privkey);
+	os_free(wps);
+}
+
+
 int hostapd_init_wps(struct hostapd_data *hapd,
 		     struct hostapd_bss_config *conf)
 {
@@ -978,7 +964,7 @@
 	struct wps_registrar_config cfg;
 
 	if (conf->wps_state == 0) {
-		hostapd_wps_clear_ies(hapd);
+		hostapd_wps_clear_ies(hapd, 0);
 		return 0;
 	}
 
@@ -1027,7 +1013,6 @@
 		os_strdup(hapd->conf->serial_number) : NULL;
 	wps->config_methods =
 		wps_config_methods_str2bin(hapd->conf->config_methods);
-#ifdef CONFIG_WPS2
 	if ((wps->config_methods &
 	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
 	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
@@ -1042,14 +1027,11 @@
 			   "virtual_push_button for WPS 2.0 compliance");
 		wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 	}
-#endif /* CONFIG_WPS2 */
 	os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
 		  WPS_DEV_TYPE_LEN);
 
-	if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
-		os_free(wps);
-		return -1;
-	}
+	if (hostapd_wps_set_vendor_ext(hapd, wps) < 0)
+		goto fail;
 
 	wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
 
@@ -1088,18 +1070,6 @@
 	if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
 		wps->encr_types |= WPS_ENCR_NONE;
 		wps->auth_types |= WPS_AUTH_OPEN;
-	} else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) {
-		wps->encr_types |= WPS_ENCR_WEP;
-		if (conf->auth_algs & WPA_AUTH_ALG_OPEN)
-			wps->auth_types |= WPS_AUTH_OPEN;
-		if (conf->auth_algs & WPA_AUTH_ALG_SHARED)
-			wps->auth_types |= WPS_AUTH_SHARED;
-	} else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) {
-		wps->auth_types |= WPS_AUTH_OPEN;
-		if (conf->default_wep_key_len)
-			wps->encr_types |= WPS_ENCR_WEP;
-		else
-			wps->encr_types |= WPS_ENCR_NONE;
 	}
 
 	if (conf->ssid.wpa_psk_file) {
@@ -1109,19 +1079,15 @@
 		wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
 	} else if (conf->ssid.wpa_psk) {
 		wps->network_key = os_malloc(2 * PMK_LEN + 1);
-		if (wps->network_key == NULL) {
-			os_free(wps);
-			return -1;
-		}
+		if (wps->network_key == NULL)
+			goto fail;
 		wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
 				 conf->ssid.wpa_psk->psk, PMK_LEN);
 		wps->network_key_len = 2 * PMK_LEN;
 	} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
 		wps->network_key = os_malloc(conf->ssid.wep.len[0]);
-		if (wps->network_key == NULL) {
-			os_free(wps);
-			return -1;
-		}
+		if (wps->network_key == NULL)
+			goto fail;
 		os_memcpy(wps->network_key, conf->ssid.wep.key[0],
 			  conf->ssid.wep.len[0]);
 		wps->network_key_len = conf->ssid.wep.len[0];
@@ -1132,6 +1098,8 @@
 		wps->psk_set = 1;
 	}
 
+	wps->ap_auth_type = wps->auth_types;
+	wps->ap_encr_type = wps->encr_types;
 	if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
 		/* Override parameters to enable security by default */
 		wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
@@ -1165,9 +1133,7 @@
 	wps->registrar = wps_registrar_init(wps, &cfg);
 	if (wps->registrar == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
-		os_free(wps->network_key);
-		os_free(wps);
-		return -1;
+		goto fail;
 	}
 
 #ifdef CONFIG_WPS_UPNP
@@ -1183,6 +1149,10 @@
 	hapd->wps = wps;
 
 	return 0;
+
+fail:
+	hostapd_free_wps(wps);
+	return -1;
 }
 
 
@@ -1197,8 +1167,7 @@
 	if (hostapd_wps_upnp_init(hapd, wps) < 0) {
 		wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
 		wps_registrar_deinit(wps->registrar);
-		os_free(wps->network_key);
-		os_free(wps);
+		hostapd_free_wps(wps);
 		hapd->wps = NULL;
 		return -1;
 	}
@@ -1211,6 +1180,7 @@
 static void hostapd_wps_nfc_clear(struct wps_context *wps)
 {
 #ifdef CONFIG_WPS_NFC
+	wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps);
 	wps->ap_nfc_dev_pw_id = 0;
 	wpabuf_free(wps->ap_nfc_dh_pubkey);
 	wps->ap_nfc_dh_pubkey = NULL;
@@ -1226,21 +1196,19 @@
 {
 	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
 	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
-	if (hapd->wps == NULL)
+	eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
+	if (hapd->wps == NULL) {
+		hostapd_wps_clear_ies(hapd, 1);
 		return;
+	}
 #ifdef CONFIG_WPS_UPNP
 	hostapd_wps_upnp_deinit(hapd);
 #endif /* CONFIG_WPS_UPNP */
 	wps_registrar_deinit(hapd->wps->registrar);
-	os_free(hapd->wps->network_key);
-	wps_device_data_free(&hapd->wps->dev);
-	wpabuf_free(hapd->wps->dh_pubkey);
-	wpabuf_free(hapd->wps->dh_privkey);
 	wps_free_pending_msgs(hapd->wps->upnp_msgs);
-	hostapd_wps_nfc_clear(hapd->wps);
-	os_free(hapd->wps);
+	hostapd_free_wps(hapd->wps);
 	hapd->wps = NULL;
-	hostapd_wps_clear_ies(hapd);
+	hostapd_wps_clear_ies(hapd, 1);
 }
 
 
@@ -1322,7 +1290,7 @@
 {
 	const u8 *p2p_dev_addr = ctx;
 	if (hapd->wps == NULL)
-		return 0;
+		return -1;
 	return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
 }
 
@@ -1338,7 +1306,7 @@
 static int wps_cancel(struct hostapd_data *hapd, void *ctx)
 {
 	if (hapd->wps == NULL)
-		return 0;
+		return -1;
 
 	wps_registrar_wps_cancel(hapd->wps->registrar);
 	ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
@@ -1459,6 +1427,16 @@
 		return 0;
 	}
 
+	if (!sta->eapol_sm) {
+		/*
+		 * This can happen, e.g., if an ER sends an extra message after
+		 * the station has disassociated (but not fully
+		 * deauthenticated).
+		 */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
+		return 0;
+	}
+
 	p = os_zalloc(sizeof(*p));
 	if (p == NULL)
 		return -1;
@@ -1652,8 +1630,6 @@
 	if (encr) {
 		if (os_strncmp(encr, "NONE", 4) == 0)
 			cred.encr_type = WPS_ENCR_NONE;
-		else if (os_strncmp(encr, "WEP", 3) == 0)
-			cred.encr_type = WPS_ENCR_WEP;
 		else if (os_strncmp(encr, "TKIP", 4) == 0)
 			cred.encr_type = WPS_ENCR_TKIP;
 		else if (os_strncmp(encr, "CCMP", 4) == 0)
@@ -1768,7 +1744,8 @@
 	if (hapd->wps == NULL)
 		return NULL;
 
-	ret = wps_get_oob_cred(hapd->wps);
+	ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+			       hapd->iconf->channel);
 	if (ndef && ret) {
 		struct wpabuf *tmp;
 		tmp = ndef_build_wifi(ret);
@@ -1784,11 +1761,136 @@
 
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
 {
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+		struct wps_context *wps = hapd->wps;
+		if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+				   &hapd->conf->wps_nfc_dh_privkey) < 0)
+			return NULL;
+		hostapd_wps_nfc_clear(wps);
+		wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+		wps->ap_nfc_dh_pubkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+		wps->ap_nfc_dh_privkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+		if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+			hostapd_wps_nfc_clear(wps);
+			return NULL;
+		}
+	}
+
+	ret = wps_build_nfc_handover_sel(hapd->wps,
+					 hapd->conf->wps_nfc_dh_pubkey,
+					 hapd->own_addr, hapd->iface->freq);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct wpabuf *wps;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+
 	/*
-	 * Handover Select carrier record for WPS uses the same format as
-	 * configuration token.
+	 * Enrollee/station is always initiator of the NFC connection handover,
+	 * so use the request message here to find Enrollee public key hash.
 	 */
-	return hostapd_wps_nfc_config_token(hapd, ndef);
+	wps = ndef_parse_wifi(req);
+	if (wps == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+		   "payload from NFC connection handover");
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in rt Wi-Fi Handover Request Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Request Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+			   "Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+					     attr.oob_dev_password,
+					     DEV_PW_NFC_CONNECTION_HANDOVER,
+					     NULL, 0, 1);
+
+out:
+	wpabuf_free(wps);
+	return ret;
 }
 
 
@@ -1823,6 +1925,9 @@
 		return -1;
 
 	hostapd_wps_nfc_clear(wps);
+	wpa_printf(MSG_DEBUG,
+		   "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)",
+		   hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps);
 	wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
 	wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
 	wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
@@ -1849,6 +1954,8 @@
 
 void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
 {
+	wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s",
+		   hapd->conf->iface);
 	hostapd_wps_nfc_clear(hapd->wps);
 }
 
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index a2c2cf0..204bd82 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -16,6 +16,7 @@
 int hostapd_init_wps_complete(struct hostapd_data *hapd);
 void hostapd_deinit_wps(struct hostapd_data *hapd);
 void hostapd_update_wps(struct hostapd_data *hapd);
+void hostapd_wps_eap_completed(struct hostapd_data *hapd);
 int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
 			const char *uuid, const char *pin, int timeout);
 int hostapd_wps_button_pushed(struct hostapd_data *hapd,
@@ -36,6 +37,9 @@
 struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
 					     int ndef);
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
 struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
 int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
 void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
@@ -61,6 +65,10 @@
 {
 }
 
+static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+}
+
 static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
 					  const u8 *addr,
 					  char *buf, size_t buflen)
diff --git a/src/common/Makefile b/src/common/Makefile
index 9c41962..adfd3df 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
new file mode 100644
index 0000000..56b1122
--- /dev/null
+++ b/src/common/common_module_tests.c
@@ -0,0 +1,172 @@
+/*
+ * common module tests
+ * Copyright (c) 2014, 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 "ieee802_11_common.h"
+#include "wpa_common.h"
+
+
+struct ieee802_11_parse_test_data {
+	u8 *data;
+	size_t len;
+	ParseRes result;
+	int count;
+};
+
+static const struct ieee802_11_parse_test_data parse_tests[] = {
+	{ (u8 *) "", 0, ParseOK, 0 },
+	{ (u8 *) " ", 1, ParseFailed, 0 },
+	{ (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
+	{ (u8 *) "\xff\x01", 2, ParseFailed, 0 },
+	{ (u8 *) "\xdd\x03\x01\x02\x03", 5, ParseUnknown, 1 },
+	{ (u8 *) "\xdd\x04\x01\x02\x03\x04", 6, ParseUnknown, 1 },
+	{ (u8 *) "\xdd\x04\x00\x50\xf2\x02", 6, ParseUnknown, 1 },
+	{ (u8 *) "\xdd\x05\x00\x50\xf2\x02\x02", 7, ParseOK, 1 },
+	{ (u8 *) "\xdd\x05\x00\x50\xf2\x02\xff", 7, ParseUnknown, 1 },
+	{ (u8 *) "\xdd\x04\x00\x50\xf2\xff", 6, ParseUnknown, 1 },
+	{ (u8 *) "\xdd\x04\x50\x6f\x9a\xff", 6, ParseUnknown, 1 },
+	{ (u8 *) "\xdd\x04\x00\x90\x4c\x33", 6, ParseOK, 1 },
+	{ (u8 *) "\xdd\x04\x00\x90\x4c\xff\xdd\x04\x00\x90\x4c\x33", 12,
+	  ParseUnknown, 2 },
+	{ (u8 *) "\x10\x01\x00\x21\x00", 5, ParseOK, 2 },
+	{ (u8 *) "\x24\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\x38\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\x54\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\x5a\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\x65\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\x65\x12\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11",
+	  20, ParseOK, 1 },
+	{ (u8 *) "\x6e\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xc7\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 },
+	{ NULL, 0, ParseOK, 0 }
+};
+
+static int ieee802_11_parse_tests(void)
+{
+	int i, ret = 0;
+
+	wpa_printf(MSG_INFO, "ieee802_11_parse tests");
+
+	for (i = 0; parse_tests[i].data; i++) {
+		const struct ieee802_11_parse_test_data *test;
+		struct ieee802_11_elems elems;
+		ParseRes res;
+
+		test = &parse_tests[i];
+		res = ieee802_11_parse_elems(test->data, test->len, &elems, 1);
+		if (res != test->result ||
+		    ieee802_11_ie_count(test->data, test->len) != test->count) {
+			wpa_printf(MSG_ERROR, "ieee802_11_parse test %d failed",
+				   i);
+			ret = -1;
+		}
+	}
+
+	if (ieee802_11_vendor_ie_concat((const u8 *) "\x00\x01", 2, 0) != NULL)
+	{
+		wpa_printf(MSG_ERROR,
+			   "ieee802_11_vendor_ie_concat test failed");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+struct rsn_ie_parse_test_data {
+	u8 *data;
+	size_t len;
+	int result;
+};
+
+static const struct rsn_ie_parse_test_data rsn_parse_tests[] = {
+	{ (u8 *) "", 0, -1 },
+	{ (u8 *) "\x30\x00", 2, -1 },
+	{ (u8 *) "\x30\x02\x01\x00", 4, 0 },
+	{ (u8 *) "\x30\x02\x00\x00", 4, -2 },
+	{ (u8 *) "\x30\x02\x02\x00", 4, -2 },
+	{ (u8 *) "\x30\x02\x00\x01", 4, -2 },
+	{ (u8 *) "\x30\x02\x00\x00\x00", 5, -2 },
+	{ (u8 *) "\x30\x03\x01\x00\x00", 5, -3 },
+	{ (u8 *) "\x30\x06\x01\x00\x00\x00\x00\x00", 8, -1 },
+	{ (u8 *) "\x30\x06\x01\x00\x00\x0f\xac\x04", 8, 0 },
+	{ (u8 *) "\x30\x07\x01\x00\x00\x0f\xac\x04\x00", 9, -5 },
+	{ (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x00", 10, -4 },
+	{ (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x01", 10, -4 },
+	{ (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04",
+	  14, 0 },
+	{ (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x00\x01\x00\x0f\xac\x04",
+	  14, -4 },
+	{ (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x06",
+	  14, -1 },
+	{ (u8 *) "\x30\x10\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x04\x00\x0f\xac\x08",
+	  18, 0 },
+	{ (u8 *) "\x30\x0d\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00",
+	  15, -7 },
+	{ (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x00",
+	  16, -6 },
+	{ (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x01",
+	  16, -6 },
+	{ (u8 *) "\x30\x12\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01",
+	  20, 0 },
+	{ (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x01\x00\x0f\xac\x02",
+	  24, 0 },
+	{ (u8 *) "\x30\x13\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00",
+	  21, 0 },
+	{ (u8 *) "\x30\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00",
+	  22, 0 },
+	{ (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00",
+	  24, 0 },
+	{ (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x01",
+	  24, -9 },
+	{ (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x00\x00\x00",
+	  28, -10 },
+	{ (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06",
+	  28, 0 },
+	{ (u8 *) "\x30\x1c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06\x01\x02",
+	  30, 0 },
+	{ NULL, 0, 0 }
+};
+
+static int rsn_ie_parse_tests(void)
+{
+	int i, ret = 0;
+
+	wpa_printf(MSG_INFO, "rsn_ie_parse tests");
+
+	for (i = 0; rsn_parse_tests[i].data; i++) {
+		const struct rsn_ie_parse_test_data *test;
+		struct wpa_ie_data data;
+
+		test = &rsn_parse_tests[i];
+		if (wpa_parse_wpa_ie_rsn(test->data, test->len, &data) !=
+		    test->result) {
+			wpa_printf(MSG_ERROR, "rsn_ie_parse test %d failed", i);
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+
+int common_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "common module tests");
+
+	if (ieee802_11_parse_tests() < 0 ||
+	    rsn_ie_parse_tests() < 0)
+		ret = -1;
+
+	return ret;
+}
diff --git a/src/common/defs.h b/src/common/defs.h
index 281dd8a..d4091e3 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -23,11 +23,15 @@
 #define WPA_CIPHER_WEP104 BIT(2)
 #define WPA_CIPHER_TKIP BIT(3)
 #define WPA_CIPHER_CCMP BIT(4)
-#ifdef CONFIG_IEEE80211W
 #define WPA_CIPHER_AES_128_CMAC BIT(5)
-#endif /* CONFIG_IEEE80211W */
 #define WPA_CIPHER_GCMP BIT(6)
 #define WPA_CIPHER_SMS4 BIT(7)
+#define WPA_CIPHER_GCMP_256 BIT(8)
+#define WPA_CIPHER_CCMP_256 BIT(9)
+#define WPA_CIPHER_BIP_GMAC_128 BIT(11)
+#define WPA_CIPHER_BIP_GMAC_256 BIT(12)
+#define WPA_CIPHER_BIP_CMAC_256 BIT(13)
+#define WPA_CIPHER_GTK_NOT_USED BIT(14)
 
 #define WPA_KEY_MGMT_IEEE8021X BIT(0)
 #define WPA_KEY_MGMT_PSK BIT(1)
@@ -44,12 +48,14 @@
 #define WPA_KEY_MGMT_WAPI_PSK BIT(12)
 #define WPA_KEY_MGMT_WAPI_CERT BIT(13)
 #define WPA_KEY_MGMT_CCKM BIT(14)
+#define WPA_KEY_MGMT_OSEN BIT(15)
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
 	return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
 			 WPA_KEY_MGMT_FT_IEEE8021X |
 			 WPA_KEY_MGMT_CCKM |
+			 WPA_KEY_MGMT_OSEN |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256));
 }
 
@@ -58,7 +64,8 @@
 	return !!(akm & (WPA_KEY_MGMT_PSK |
 			 WPA_KEY_MGMT_FT_PSK |
 			 WPA_KEY_MGMT_PSK_SHA256 |
-			 WPA_KEY_MGMT_SAE));
+			 WPA_KEY_MGMT_SAE |
+			 WPA_KEY_MGMT_FT_SAE));
 }
 
 static inline int wpa_key_mgmt_ft(int akm)
@@ -77,13 +84,15 @@
 static inline int wpa_key_mgmt_sha256(int akm)
 {
 	return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
-			 WPA_KEY_MGMT_IEEE8021X_SHA256));
+			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
+			 WPA_KEY_MGMT_OSEN));
 }
 
 static inline int wpa_key_mgmt_wpa(int akm)
 {
 	return wpa_key_mgmt_wpa_ieee8021x(akm) ||
-		wpa_key_mgmt_wpa_psk(akm);
+		wpa_key_mgmt_wpa_psk(akm) ||
+		wpa_key_mgmt_sae(akm);
 }
 
 static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -100,6 +109,7 @@
 #define WPA_PROTO_WPA BIT(0)
 #define WPA_PROTO_RSN BIT(1)
 #define WPA_PROTO_WAPI BIT(2)
+#define WPA_PROTO_OSEN BIT(3)
 
 #define WPA_AUTH_ALG_OPEN BIT(0)
 #define WPA_AUTH_ALG_SHARED BIT(1)
@@ -117,41 +127,12 @@
 	WPA_ALG_PMK,
 	WPA_ALG_GCMP,
 	WPA_ALG_SMS4,
-	WPA_ALG_KRK
-};
-
-/**
- * enum wpa_cipher - Cipher suites
- */
-enum wpa_cipher {
-	CIPHER_NONE,
-	CIPHER_WEP40,
-	CIPHER_TKIP,
-	CIPHER_CCMP,
-	CIPHER_WEP104,
-	CIPHER_GCMP,
-	CIPHER_SMS4
-};
-
-/**
- * enum wpa_key_mgmt - Key management suites
- */
-enum wpa_key_mgmt {
-	KEY_MGMT_802_1X,
-	KEY_MGMT_PSK,
-	KEY_MGMT_NONE,
-	KEY_MGMT_802_1X_NO_WPA,
-	KEY_MGMT_WPA_NONE,
-	KEY_MGMT_FT_802_1X,
-	KEY_MGMT_FT_PSK,
-	KEY_MGMT_802_1X_SHA256,
-	KEY_MGMT_PSK_SHA256,
-	KEY_MGMT_WPS,
-	KEY_MGMT_SAE,
-	KEY_MGMT_FT_SAE,
-	KEY_MGMT_WAPI_PSK,
-	KEY_MGMT_WAPI_CERT,
-	KEY_MGMT_CCKM
+	WPA_ALG_KRK,
+	WPA_ALG_GCMP_256,
+	WPA_ALG_CCMP_256,
+	WPA_ALG_BIP_GMAC_128,
+	WPA_ALG_BIP_GMAC_256,
+	WPA_ALG_BIP_CMAC_256
 };
 
 /**
@@ -312,6 +293,7 @@
 	WPA_CTRL_REQ_EAP_PIN,
 	WPA_CTRL_REQ_EAP_OTP,
 	WPA_CTRL_REQ_EAP_PASSPHRASE,
+	WPA_CTRL_REQ_SIM,
 	NUM_WPA_CTRL_REQS
 };
 
diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h
index 4811f38..6958661 100644
--- a/src/common/eapol_common.h
+++ b/src/common/eapol_common.h
@@ -22,17 +22,28 @@
 	/* followed by length octets of data */
 } STRUCT_PACKED;
 
+struct ieee8023_hdr {
+	u8 dest[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	u16 ethertype;
+} STRUCT_PACKED;
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
 
+#ifdef CONFIG_MACSEC
+#define EAPOL_VERSION 3
+#else /* CONFIG_MACSEC */
 #define EAPOL_VERSION 2
+#endif /* CONFIG_MACSEC */
 
 enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
        IEEE802_1X_TYPE_EAPOL_START = 1,
        IEEE802_1X_TYPE_EAPOL_LOGOFF = 2,
        IEEE802_1X_TYPE_EAPOL_KEY = 3,
-       IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4
+       IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4,
+       IEEE802_1X_TYPE_EAPOL_MKA = 5,
 };
 
 enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index aab8ac6..173a400 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -108,10 +108,15 @@
 			elems->hs20 = pos;
 			elems->hs20_len = elen;
 			break;
+		case HS20_OSEN_OUI_TYPE:
+			/* Hotspot 2.0 OSEN */
+			elems->osen = pos;
+			elems->osen_len = elen;
+			break;
 		default:
 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
 				   "information element ignored "
-				   "(type=%d len=%lu)\n",
+				   "(type=%d len=%lu)",
 				   pos[3], (unsigned long) elen);
 			return -1;
 		}
@@ -189,25 +194,12 @@
 			elems->supp_rates = pos;
 			elems->supp_rates_len = elen;
 			break;
-		case WLAN_EID_FH_PARAMS:
-			elems->fh_params = pos;
-			elems->fh_params_len = elen;
-			break;
 		case WLAN_EID_DS_PARAMS:
 			elems->ds_params = pos;
 			elems->ds_params_len = elen;
 			break;
 		case WLAN_EID_CF_PARAMS:
-			elems->cf_params = pos;
-			elems->cf_params_len = elen;
-			break;
 		case WLAN_EID_TIM:
-			elems->tim = pos;
-			elems->tim_len = elen;
-			break;
-		case WLAN_EID_IBSS_PARAMS:
-			elems->ibss_params = pos;
-			elems->ibss_params_len = elen;
 			break;
 		case WLAN_EID_CHALLENGE:
 			elems->challenge = pos;
@@ -232,8 +224,6 @@
 			elems->rsn_ie_len = elen;
 			break;
 		case WLAN_EID_PWR_CAPABILITY:
-			elems->power_cap = pos;
-			elems->power_cap_len = elen;
 			break;
 		case WLAN_EID_SUPPORTED_CHANNELS:
 			elems->supp_channels = pos;
@@ -267,6 +257,11 @@
 			elems->vht_operation = pos;
 			elems->vht_operation_len = elen;
 			break;
+		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
+			if (elen != 1)
+				break;
+			elems->vht_opmode_notif = pos;
+			break;
 		case WLAN_EID_LINK_ID:
 			if (elen < 18)
 				break;
@@ -276,6 +271,12 @@
 			elems->interworking = pos;
 			elems->interworking_len = elen;
 			break;
+		case WLAN_EID_QOS_MAP_SET:
+			if (elen < 16)
+				break;
+			elems->qos_map_set = pos;
+			elems->qos_map_set_len = elen;
+			break;
 		case WLAN_EID_EXT_CAPAB:
 			elems->ext_capab = pos;
 			elems->ext_capab_len = elen;
@@ -545,3 +546,60 @@
 
 	return num_11b > 0 && num_others == 0;
 }
+
+
+const char * fc2str(u16 fc)
+{
+	u16 stype = WLAN_FC_GET_STYPE(fc);
+#define C2S(x) case x: return #x;
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		switch (stype) {
+		C2S(WLAN_FC_STYPE_ASSOC_REQ)
+		C2S(WLAN_FC_STYPE_ASSOC_RESP)
+		C2S(WLAN_FC_STYPE_REASSOC_REQ)
+		C2S(WLAN_FC_STYPE_REASSOC_RESP)
+		C2S(WLAN_FC_STYPE_PROBE_REQ)
+		C2S(WLAN_FC_STYPE_PROBE_RESP)
+		C2S(WLAN_FC_STYPE_BEACON)
+		C2S(WLAN_FC_STYPE_ATIM)
+		C2S(WLAN_FC_STYPE_DISASSOC)
+		C2S(WLAN_FC_STYPE_AUTH)
+		C2S(WLAN_FC_STYPE_DEAUTH)
+		C2S(WLAN_FC_STYPE_ACTION)
+		}
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		switch (stype) {
+		C2S(WLAN_FC_STYPE_PSPOLL)
+		C2S(WLAN_FC_STYPE_RTS)
+		C2S(WLAN_FC_STYPE_CTS)
+		C2S(WLAN_FC_STYPE_ACK)
+		C2S(WLAN_FC_STYPE_CFEND)
+		C2S(WLAN_FC_STYPE_CFENDACK)
+		}
+		break;
+	case WLAN_FC_TYPE_DATA:
+		switch (stype) {
+		C2S(WLAN_FC_STYPE_DATA)
+		C2S(WLAN_FC_STYPE_DATA_CFACK)
+		C2S(WLAN_FC_STYPE_DATA_CFPOLL)
+		C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
+		C2S(WLAN_FC_STYPE_NULLFUNC)
+		C2S(WLAN_FC_STYPE_CFACK)
+		C2S(WLAN_FC_STYPE_CFPOLL)
+		C2S(WLAN_FC_STYPE_CFACKPOLL)
+		C2S(WLAN_FC_STYPE_QOS_DATA)
+		C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
+		C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
+		C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
+		C2S(WLAN_FC_STYPE_QOS_NULL)
+		C2S(WLAN_FC_STYPE_QOS_CFPOLL)
+		C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
+		}
+		break;
+	}
+	return "WLAN_FC_TYPE_UNKNOWN";
+#undef C2S
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 68c6b96..cf83057 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -13,11 +13,7 @@
 struct ieee802_11_elems {
 	const u8 *ssid;
 	const u8 *supp_rates;
-	const u8 *fh_params;
 	const u8 *ds_params;
-	const u8 *cf_params;
-	const u8 *tim;
-	const u8 *ibss_params;
 	const u8 *challenge;
 	const u8 *erp_info;
 	const u8 *ext_supp_rates;
@@ -26,7 +22,6 @@
 	const u8 *wmm; /* WMM Information or Parameter Element */
 	const u8 *wmm_tspec;
 	const u8 *wps_ie;
-	const u8 *power_cap;
 	const u8 *supp_channels;
 	const u8 *mdie;
 	const u8 *ftie;
@@ -35,23 +30,22 @@
 	const u8 *ht_operation;
 	const u8 *vht_capabilities;
 	const u8 *vht_operation;
+	const u8 *vht_opmode_notif;
 	const u8 *vendor_ht_cap;
 	const u8 *p2p;
 	const u8 *wfd;
 	const u8 *link_id;
 	const u8 *interworking;
+	const u8 *qos_map_set;
 	const u8 *hs20;
 	const u8 *ext_capab;
 	const u8 *bss_max_idle_period;
 	const u8 *ssid_list;
+	const u8 *osen;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
-	u8 fh_params_len;
 	u8 ds_params_len;
-	u8 cf_params_len;
-	u8 tim_len;
-	u8 ibss_params_len;
 	u8 challenge_len;
 	u8 erp_info_len;
 	u8 ext_supp_rates_len;
@@ -60,7 +54,6 @@
 	u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
 	u8 wmm_tspec_len;
 	u8 wps_ie_len;
-	u8 power_cap_len;
 	u8 supp_channels_len;
 	u8 mdie_len;
 	u8 ftie_len;
@@ -73,9 +66,11 @@
 	u8 p2p_len;
 	u8 wfd_len;
 	u8 interworking_len;
+	u8 qos_map_set_len;
 	u8 hs20_len;
 	u8 ext_capab_len;
 	u8 ssid_list_len;
+	u8 osen_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -103,4 +98,5 @@
 
 int supp_rates_11b_only(struct ieee802_11_elems *elems);
 
+const char * fc2str(u16 fc);
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 137c309..6de71e9 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -161,6 +161,8 @@
 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
 #define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
 
 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
 #define WLAN_REASON_UNSPECIFIED 1
@@ -203,6 +205,7 @@
 #define WLAN_EID_TIM 5
 #define WLAN_EID_IBSS_PARAMS 6
 #define WLAN_EID_COUNTRY 7
+#define WLAN_EID_BSS_LOAD 11
 #define WLAN_EID_CHALLENGE 16
 /* EIDs defined by IEEE 802.11h - START */
 #define WLAN_EID_PWR_CONSTRAINT 32
@@ -221,10 +224,12 @@
 #define WLAN_EID_QOS 46
 #define WLAN_EID_RSN 48
 #define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_NEIGHBOR_REPORT 52
 #define WLAN_EID_MOBILITY_DOMAIN 54
 #define WLAN_EID_FAST_BSS_TRANSITION 55
 #define WLAN_EID_TIMEOUT_INTERVAL 56
 #define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
 #define WLAN_EID_HT_OPERATION 61
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
 #define WLAN_EID_WAPI 68
@@ -242,6 +247,7 @@
 #define WLAN_EID_LINK_ID 101
 #define WLAN_EID_INTERWORKING 107
 #define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_QOS_MAP_SET 110
 #define WLAN_EID_ROAMING_CONSORTIUM 111
 #define WLAN_EID_EXT_CAPAB 127
 #define WLAN_EID_CCKM 156
@@ -267,6 +273,7 @@
 #define WLAN_ACTION_FT 6
 #define WLAN_ACTION_HT 7
 #define WLAN_ACTION_SA_QUERY 8
+#define WLAN_ACTION_PROTECTED_DUAL 9
 #define WLAN_ACTION_WNM 10
 #define WLAN_ACTION_UNPROTECTED_WNM 11
 #define WLAN_ACTION_TDLS 12
@@ -282,6 +289,19 @@
 #define WLAN_PA_GAS_COMEBACK_RESP 13
 #define WLAN_TDLS_DISCOVERY_RESPONSE 14
 
+/* Protected Dual of Public Action frames */
+#define WLAN_PROT_DSE_ENABLEMENT 1
+#define WLAN_PROT_DSE_DEENABLEMENT 2
+#define WLAN_PROT_EXT_CSA 4
+#define WLAN_PROT_MEASUREMENT_REQ 5
+#define WLAN_PROT_MEASUREMENT_REPORT 6
+#define WLAN_PROT_DSE_POWER_CONSTRAINT 8
+#define WLAN_PROT_VENDOR_SPECIFIC 9
+#define WLAN_PROT_GAS_INITIAL_REQ 10
+#define WLAN_PROT_GAS_INITIAL_RESP 11
+#define WLAN_PROT_GAS_COMEBACK_REQ 12
+#define WLAN_PROT_GAS_COMEBACK_RESP 13
+
 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
 #define WLAN_SA_QUERY_REQUEST 0
 #define WLAN_SA_QUERY_RESPONSE 1
@@ -566,9 +586,12 @@
 /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
 #define IEEE80211_HT_MCS_MASK_LEN 10
 
+/* HT Capabilities element */
 struct ieee80211_ht_capabilities {
 	le16 ht_capabilities_info;
-	u8 a_mpdu_params;
+	u8 a_mpdu_params; /* Maximum A-MPDU Length Exponent B0..B1
+			   * Minimum MPDU Start Spacing B2..B4
+			   * Reserved B5..B7 */
 	u8 supported_mcs_set[16];
 	le16 ht_extended_capabilities;
 	le32 tx_bf_capability_info;
@@ -576,12 +599,25 @@
 } STRUCT_PACKED;
 
 
+/* HT Operation element */
 struct ieee80211_ht_operation {
-	u8 control_chan;
-	u8 ht_param;
-	le16 operation_mode;
-	le16 stbc_param;
-	u8 basic_set[16];
+	u8 primary_chan;
+	/* Five octets of HT Operation Information */
+	u8 ht_param; /* B0..B7 */
+	le16 operation_mode; /* B8..B23 */
+	le16 param; /* B24..B39 */
+	u8 basic_mcs_set[16];
+} STRUCT_PACKED;
+
+
+struct ieee80211_obss_scan_parameters {
+	le16 scan_passive_dwell;
+	le16 scan_active_dwell;
+	le16 width_trigger_scan_interval;
+	le16 scan_passive_total_per_channel;
+	le16 scan_active_total_per_channel;
+	le16 channel_transition_delay_factor;
+	le16 scan_activity_threshold;
 } STRUCT_PACKED;
 
 
@@ -610,7 +646,9 @@
 #define ERP_INFO_USE_PROTECTION BIT(1)
 #define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
 
+#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
 
+/* HT Capabilities Info field within HT Capabilities element */
 #define HT_CAP_INFO_LDPC_CODING_CAP		((u16) BIT(0))
 #define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET	((u16) BIT(1))
 #define HT_CAP_INFO_SMPS_MASK			((u16) (BIT(2) | BIT(3)))
@@ -628,73 +666,86 @@
 #define HT_CAP_INFO_DELAYED_BA			((u16) BIT(10))
 #define HT_CAP_INFO_MAX_AMSDU_SIZE		((u16) BIT(11))
 #define HT_CAP_INFO_DSSS_CCK40MHZ		((u16) BIT(12))
-#define HT_CAP_INFO_PSMP_SUPP			((u16) BIT(13))
+/* B13 - Reserved (was PSMP support during P802.11n development) */
 #define HT_CAP_INFO_40MHZ_INTOLERANT		((u16) BIT(14))
 #define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT	((u16) BIT(15))
 
-
+/* HT Extended Capabilities field within HT Capabilities element */
 #define EXT_HT_CAP_INFO_PCO			((u16) BIT(0))
+#define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK	((u16) (BIT(1) | BIT(2)))
 #define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET	1
+/* B3..B7 - Reserved */
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK	((u16) (BIT(8) | BIT(9)))
 #define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET	8
-#define EXT_HT_CAP_INFO_HTC_SUPPORTED		((u16) BIT(10))
+#define EXT_HT_CAP_INFO_HTC_SUPPORT		((u16) BIT(10))
 #define EXT_HT_CAP_INFO_RD_RESPONDER		((u16) BIT(11))
+/* B12..B15 - Reserved */
 
+/* Transmit Beanforming Capabilities within HT Capabilities element */
+#define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0))
+#define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
+#define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
+#define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3))
+#define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4))
+#define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5))
+#define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7))
+#define TX_BF_CAP_CALIB_OFFSET 6
+#define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
+#define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9))
+#define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10))
+#define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11)))
+#define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
+#define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
+#define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
+#define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17
+#define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
+#define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
+#define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
+#define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28)))
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27
+/* B29..B31 - Reserved */
 
-#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0))
-#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
-#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
-#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3))
-#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4))
-#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5))
-#define TX_BEAMFORM_CAP_CALIB_OFFSET 6
-#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
-#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9))
-#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10))
-#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
-#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
-#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
-#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17
-#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
-#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
-#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
-#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+/* ASEL Capability field within HT Capabilities element */
+#define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
+#define ASEL_CAP_RX_AS_CAP ((u8) BIT(5))
+#define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6))
+/* B7 - Reserved */
 
-
-#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0))
-#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
-#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
-#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
-#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
-#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5))
-#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6))
-
+/* First octet of HT Operation Information within HT Operation element */
 #define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK	((u8) BIT(0) | BIT(1))
 #define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE		((u8) BIT(0))
 #define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW		((u8) BIT(0) | BIT(1))
-#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH		((u8) BIT(2))
+#define HT_INFO_HT_PARAM_STA_CHNL_WIDTH			((u8) BIT(2))
 #define HT_INFO_HT_PARAM_RIFS_MODE			((u8) BIT(3))
-#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY		((u8) BIT(4))
-#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY	((u8) BIT(5))
+/* B4..B7 - Reserved */
 
+/* HT Protection (B8..B9 of HT Operation Information) */
+#define HT_PROT_NO_PROTECTION           0
+#define HT_PROT_NONMEMBER_PROTECTION    1
+#define HT_PROT_20MHZ_PROTECTION        2
+#define HT_PROT_NON_HT_MIXED            3
+/* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in
+ * HT Operation Information) */
+#define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */
+#define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT	((u16) BIT(2)) /* B10 */
+/* BIT(3), i.e., B11 in HT Operation Information field - Reserved */
+#define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT	((u16) BIT(4)) /* B12 */
+/* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */
 
-#define OP_MODE_PURE                    0
-#define OP_MODE_MAY_BE_LEGACY_STAS      1
-#define OP_MODE_20MHZ_HT_STA_ASSOCED    2
-#define OP_MODE_MIXED                   3
-
-#define HT_INFO_OPERATION_MODE_OP_MODE_MASK	\
-		(0x0001 | 0x0002)
-#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET		0
-#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT	((u8) BIT(2))
-#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT	((u8) BIT(3))
-#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT	((u8) BIT(4))
-
-#define HT_INFO_STBC_PARAM_DUAL_BEACON			((u16) BIT(6))
-#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT		((u16) BIT(7))
-#define HT_INFO_STBC_PARAM_SECONDARY_BCN		((u16) BIT(8))
-#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED	((u16) BIT(9))
-#define HT_INFO_STBC_PARAM_PCO_ACTIVE			((u16) BIT(10))
-#define HT_INFO_STBC_PARAM_PCO_PHASE			((u16) BIT(11))
+/* Last two octets of HT Operation Information (BIT(0) = B24) */
+/* B24..B29 - Reserved */
+#define HT_OPER_PARAM_DUAL_BEACON			((u16) BIT(6))
+#define HT_OPER_PARAM_DUAL_CTS_PROTECTION		((u16) BIT(7))
+#define HT_OPER_PARAM_STBC_BEACON			((u16) BIT(8))
+#define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP		((u16) BIT(9))
+#define HT_OPER_PARAM_PCO_ACTIVE			((u16) BIT(10))
+#define HT_OPER_PARAM_PCO_PHASE				((u16) BIT(11))
+/* B36..B39 - Reserved */
 
 #define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
@@ -702,8 +753,10 @@
 /* VHT Defines */
 #define VHT_CAP_MAX_MPDU_LENGTH_7991                ((u32) BIT(0))
 #define VHT_CAP_MAX_MPDU_LENGTH_11454               ((u32) BIT(1))
+#define VHT_CAP_MAX_MPDU_LENGTH_MASK                ((u32) BIT(0) | BIT(1))
 #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ              ((u32) BIT(2))
 #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ     ((u32) BIT(3))
+#define VHT_CAP_SUPP_CHAN_WIDTH_MASK                ((u32) BIT(2) | BIT(3))
 #define VHT_CAP_RXLDPC                              ((u32) BIT(4))
 #define VHT_CAP_SHORT_GI_80                         ((u32) BIT(5))
 #define VHT_CAP_SHORT_GI_160                        ((u32) BIT(6))
@@ -712,20 +765,41 @@
 #define VHT_CAP_RXSTBC_2                            ((u32) BIT(9))
 #define VHT_CAP_RXSTBC_3                            ((u32) BIT(8) | BIT(9))
 #define VHT_CAP_RXSTBC_4                            ((u32) BIT(10))
+#define VHT_CAP_RXSTBC_MASK                         ((u32) BIT(8) | BIT(9) | \
+							   BIT(10))
 #define VHT_CAP_SU_BEAMFORMER_CAPABLE               ((u32) BIT(11))
 #define VHT_CAP_SU_BEAMFORMEE_CAPABLE               ((u32) BIT(12))
-#define VHT_CAP_BEAMFORMER_ANTENNAS_MAX             ((u32) BIT(13) | BIT(14))
-#define VHT_CAP_SOUNDING_DIMENTION_MAX              ((u32) BIT(16) | BIT(17))
+#define VHT_CAP_BEAMFORMEE_STS_MAX                  ((u32) BIT(13) | \
+							   BIT(14) | BIT(15))
+#define VHT_CAP_BEAMFORMEE_STS_OFFSET               13
+#define VHT_CAP_SOUNDING_DIMENSION_MAX              ((u32) BIT(16) | \
+							   BIT(17) | BIT(18))
+#define VHT_CAP_SOUNDING_DIMENSION_OFFSET           16
 #define VHT_CAP_MU_BEAMFORMER_CAPABLE               ((u32) BIT(19))
 #define VHT_CAP_MU_BEAMFORMEE_CAPABLE               ((u32) BIT(20))
 #define VHT_CAP_VHT_TXOP_PS                         ((u32) BIT(21))
 #define VHT_CAP_HTC_VHT                             ((u32) BIT(22))
-#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT          ((u32) BIT(23))
+
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1        ((u32) BIT(23))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2        ((u32) BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3        ((u32) BIT(23) | BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4        ((u32) BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5        ((u32) BIT(23) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6        ((u32) BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX      ((u32) BIT(23) | \
+							   BIT(24) | BIT(25))
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB   ((u32) BIT(27))
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
 #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
 #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
 
+#define VHT_OPMODE_CHANNEL_WIDTH_MASK		    ((u8) BIT(0) | BIT(1))
+#define VHT_OPMODE_CHANNEL_RxNSS_MASK		    ((u8) BIT(4) | BIT(5) | \
+						     BIT(6))
+#define VHT_OPMODE_NOTIF_RX_NSS_SHIFT		    4
+
+#define VHT_RX_NSS_MAX_STREAMS			    8
+
 /* VHT channel widths */
 #define VHT_CHANWIDTH_USE_HT	0
 #define VHT_CHANWIDTH_80MHZ	1
@@ -735,12 +809,14 @@
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
 				* 00:50:F2 */
 #define WPA_IE_VENDOR_TYPE 0x0050f201
+#define WMM_IE_VENDOR_TYPE 0x0050f202
 #define WPS_IE_VENDOR_TYPE 0x0050f204
 #define OUI_WFA 0x506f9a
 #define P2P_IE_VENDOR_TYPE 0x506f9a09
 #define WFD_IE_VENDOR_TYPE 0x506f9a0a
 #define WFD_OUI_TYPE 10
 #define HS20_IE_VENDOR_TYPE 0x506f9a10
+#define OSEN_IE_VENDOR_TYPE 0x506f9a12
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -856,6 +932,7 @@
 
 #define HS20_INDICATION_OUI_TYPE 16
 #define HS20_ANQP_OUI_TYPE 17
+#define HS20_OSEN_OUI_TYPE 18
 #define HS20_STYPE_QUERY_LIST 1
 #define HS20_STYPE_CAPABILITY_LIST 2
 #define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
@@ -863,6 +940,21 @@
 #define HS20_STYPE_CONNECTION_CAPABILITY 5
 #define HS20_STYPE_NAI_HOME_REALM_QUERY 6
 #define HS20_STYPE_OPERATING_CLASS 7
+#define HS20_STYPE_OSU_PROVIDERS_LIST 8
+#define HS20_STYPE_ICON_REQUEST 10
+#define HS20_STYPE_ICON_BINARY_FILE 11
+
+#define HS20_DGAF_DISABLED 0x01
+#define HS20_PPS_MO_ID_PRESENT 0x02
+#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
+#define HS20_VERSION 0x10 /* Release 2 */
+
+/* WNM-Notification WFA vendors specific subtypes */
+#define HS20_WNM_SUB_REM_NEEDED 0
+#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
+
+#define HS20_DEAUTH_REASON_CODE_BSS 0
+#define HS20_DEAUTH_REASON_CODE_ESS 1
 
 /* Wi-Fi Direct (P2P) */
 
@@ -888,6 +980,7 @@
 	P2P_ATTR_INTERFACE = 16,
 	P2P_ATTR_OPERATING_CHANNEL = 17,
 	P2P_ATTR_INVITATION_FLAGS = 18,
+	P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
 	P2P_ATTR_VENDOR_SPECIFIC = 221
 };
 
@@ -909,6 +1002,7 @@
 #define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
 #define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
 #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
 
 /* Invitation Flags */
 #define P2P_INVITATION_FLAGS_TYPE BIT(0)
@@ -933,6 +1027,12 @@
 	P2P_SC_FAIL_REJECTED_BY_USER = 11,
 };
 
+enum p2p_role_indication {
+	P2P_DEVICE_NOT_IN_GROUP = 0x00,
+	P2P_CLIENT_IN_A_GROUP = 0x01,
+	P2P_GO_IN_A_GROUP = 0x02,
+};
+
 #define P2P_WILDCARD_SSID "DIRECT-"
 #define P2P_WILDCARD_SSID_LEN 7
 
@@ -1002,6 +1102,11 @@
 #define WLAN_CIPHER_SUITE_AES_CMAC	0x000FAC06
 #define WLAN_CIPHER_SUITE_NO_GROUP_ADDR	0x000FAC07
 #define WLAN_CIPHER_SUITE_GCMP		0x000FAC08
+#define WLAN_CIPHER_SUITE_GCMP_256	0x000FAC09
+#define WLAN_CIPHER_SUITE_CCMP_256	0x000FAC0A
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128	0x000FAC0B
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256	0x000FAC0C
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256	0x000FAC0D
 
 #define WLAN_CIPHER_SUITE_SMS4		0x00147201
 
@@ -1015,7 +1120,10 @@
 #define WLAN_AKM_SUITE_PSK		0x000FAC02
 #define WLAN_AKM_SUITE_FT_8021X		0x000FAC03
 #define WLAN_AKM_SUITE_FT_PSK		0x000FAC04
+#define WLAN_AKM_SUITE_8021X_SHA256	0x000FAC05
+#define WLAN_AKM_SUITE_PSK_SHA256	0x000FAC06
 #define WLAN_AKM_SUITE_CCKM		0x00409600
+#define WLAN_AKM_SUITE_OSEN		0x506f9a01
 
 
 /* IEEE 802.11v - WNM Action field values */
@@ -1079,6 +1187,15 @@
 #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES   70
 #define WNM_NEIGHBOR_MULTIPLE_BSSID             71
 
+/* QoS action */
+enum qos_action {
+	QOS_ADDTS_REQ = 0,
+	QOS_ADDTS_RESP = 1,
+	QOS_DELTS = 2,
+	QOS_SCHEDULE = 3,
+	QOS_QOS_MAP_CONFIG = 4,
+};
+
 /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
 #define WLAN_20_40_BSS_COEX_INFO_REQ            BIT(0)
 #define WLAN_20_40_BSS_COEX_40MHZ_INTOL         BIT(1)
@@ -1126,4 +1243,8 @@
 	WNM_SLEEP_SUBELEM_IGTK = 1
 };
 
+/* Channel Switch modes (802.11h) */
+#define CHAN_SWITCH_MODE_ALLOW_TX	0
+#define CHAN_SWITCH_MODE_BLOCK_TX	1
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h
new file mode 100644
index 0000000..cc88caa
--- /dev/null
+++ b/src/common/ieee802_1x_defs.h
@@ -0,0 +1,78 @@
+/*
+ * IEEE Std 802.1X-2010 definitions
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_DEFS_H
+#define IEEE802_1X_DEFS_H
+
+#define CS_ID_LEN		8
+#define CS_ID_GCM_AES_128	{0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01}
+#define CS_NAME_GCM_AES_128	"GCM-AES-128"
+
+enum macsec_policy {
+	/**
+	 * Should secure sessions.
+	 * This accepts key server's advice to determine whether to secure the
+	 * session or not.
+	 */
+	SHOULD_SECURE,
+
+	/**
+	 * Disabled MACsec - do not secure sessions.
+	 */
+	DO_NOT_SECURE,
+};
+
+
+/* IEEE Std 802.1X-2010 - Table 11-6 - MACsec Capability */
+enum macsec_cap {
+	/**
+	 * MACsec is not implemented
+	 */
+	MACSEC_CAP_NOT_IMPLEMENTED,
+
+	/**
+	 * 'Integrity without confidentiality'
+	 */
+	MACSEC_CAP_INTEGRITY,
+
+	/**
+	 * 'Integrity without confidentiality' and
+	 * 'Integrity and confidentiality' with a confidentiality offset of 0
+	 */
+	MACSEC_CAP_INTEG_AND_CONF,
+
+	/**
+	 * 'Integrity without confidentiality' and
+	 * 'Integrity and confidentiality' with a confidentiality offset of 0,
+	 * 30, 50
+	 */
+	MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+};
+
+enum validate_frames {
+	Disabled,
+	Checked,
+	Strict,
+};
+
+/* IEEE Std 802.1X-2010 - Table 11-6 - Confidentiality Offset */
+enum confidentiality_offset {
+	CONFIDENTIALITY_NONE      = 0,
+	CONFIDENTIALITY_OFFSET_0  = 1,
+	CONFIDENTIALITY_OFFSET_30 = 2,
+	CONFIDENTIALITY_OFFSET_50 = 3,
+};
+
+/* IEEE Std 802.1X-2010 - Table 9-2 */
+#define DEFAULT_PRIO_INFRA_PORT        0x10
+#define DEFAULT_PRIO_PRIMRAY_AP        0x30
+#define DEFAULT_PRIO_SECONDARY_AP      0x50
+#define DEFAULT_PRIO_GROUP_CA_MEMBER   0x70
+#define DEFAULT_PRIO_NOT_KEY_SERVER    0xFF
+
+#endif /* IEEE802_1X_DEFS_H */
diff --git a/src/common/qca-vendor-attr.h b/src/common/qca-vendor-attr.h
new file mode 100644
index 0000000..6f51803
--- /dev/null
+++ b/src/common/qca-vendor-attr.h
@@ -0,0 +1,28 @@
+/*
+ * Qualcomm Atheros vendor specific attribute definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_ATTR_H
+#define QCA_VENDOR_ATTR_H
+
+/*
+ * This file defines some of the attributes used with Qualcomm Atheros OUI
+ * 00:13:74 in a way that is not suitable for qca-vendor.h, e.g., due to
+ * compiler dependencies.
+ */
+
+struct qca_avoid_freq_range {
+	u32 start_freq;
+	u32 end_freq;
+} __attribute__ ((packed));
+
+struct qca_avoid_freq_list {
+	u32 count;
+	struct qca_avoid_freq_range range[0];
+} __attribute__ ((packed));
+
+#endif /* QCA_VENDOR_ATTR_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
new file mode 100644
index 0000000..4d9efd5
--- /dev/null
+++ b/src/common/qca-vendor.h
@@ -0,0 +1,74 @@
+/*
+ * Qualcomm Atheros OUI and vendor specific assignments
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_H
+#define QCA_VENDOR_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
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_QCA 0x001374
+
+/**
+ * enum qca_radiotap_vendor_ids - QCA radiotap vendor namespace IDs
+ */
+enum qca_radiotap_vendor_ids {
+	QCA_RADIOTAP_VID_WLANTEST = 0,
+};
+
+/**
+ * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ *	ranges to avoid to reduce issues due to interference or internal
+ *	co-existence information in the driver. The event data structure is
+ *	defined in struct qca_avoid_freq_list.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support
+ *	for DFS offloading.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
+ *	NAN Request/Response and NAN Indication messages. These messages are
+ *	interpreted between the framework and the firmware component.
+ */
+enum qca_nl80211_vendor_subcmds {
+	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+	QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+	/* subcmds 2..9 not yet allocated */
+	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY =  11,
+	QCA_NL80211_VENDOR_SUBCMD_NAN =  12,
+	QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
+	/* 14..33 - reserved for QCA */
+};
+
+
+enum qca_wlan_vendor_attr {
+	QCA_WLAN_VENDOR_ATTR_INVALID = 0,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
+	QCA_WLAN_VENDOR_ATTR_DFS     = 1,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_NAN */
+	QCA_WLAN_VENDOR_ATTR_NAN     = 2,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+	QCA_WLAN_VENDOR_ATTR_STATS_EXT     = 3,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+	QCA_WLAN_VENDOR_ATTR_IFINDEX     = 4,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
+};
+
+#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index bce60a3..b67623f 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -134,8 +134,10 @@
 			return NULL;
 		if (crypto_bignum_is_zero(bn) ||
 		    crypto_bignum_is_one(bn) ||
-		    crypto_bignum_cmp(bn, sae->tmp->order) >= 0)
+		    crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
+			crypto_bignum_deinit(bn, 0);
 			continue;
+		}
 		break;
 	}
 
@@ -503,6 +505,8 @@
 		       const u8 *password, size_t password_len,
 		       struct sae_data *sae)
 {
+	if (sae->tmp == NULL)
+		return -1;
 	if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
 					  password_len) < 0)
 		return -1;
@@ -634,7 +638,8 @@
 int sae_process_commit(struct sae_data *sae)
 {
 	u8 k[SAE_MAX_PRIME_LEN];
-	if ((sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
+	if (sae->tmp == NULL ||
+	    (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
 	    (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
 	    sae_derive_keys(sae, k) < 0)
 		return -1;
@@ -646,6 +651,10 @@
 		      const struct wpabuf *token)
 {
 	u8 *pos;
+
+	if (sae->tmp == NULL)
+		return;
+
 	wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
 	if (token)
 		wpabuf_put_buf(buf, token);
@@ -678,7 +687,7 @@
 {
 	if (allowed_groups) {
 		int i;
-		for (i = 0; allowed_groups[i] >= 0; i++) {
+		for (i = 0; allowed_groups[i] > 0; i++) {
 			if (allowed_groups[i] == group)
 				break;
 		}
@@ -701,6 +710,11 @@
 		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
 	}
 
+	if (sae->tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
 	if (sae->tmp->dh && !allowed_groups) {
 		wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without "
 			   "explicit configuration enabling it", group);
@@ -797,7 +811,7 @@
 
 	/* element x and y coordinates < p */
 	if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
-	    os_memcmp(pos + sae->tmp->prime_len + sae->tmp->prime_len, prime,
+	    os_memcmp(pos + sae->tmp->prime_len, prime,
 		      sae->tmp->prime_len) >= 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
 			   "element");
@@ -985,6 +999,9 @@
 {
 	const u8 *sc;
 
+	if (sae->tmp == NULL)
+		return;
+
 	/* Send-Confirm */
 	sc = wpabuf_put(buf, 0);
 	wpabuf_put_le16(buf, sae->send_confirm);
@@ -1016,6 +1033,11 @@
 
 	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
 
+	if (sae->tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
+		return -1;
+	}
+
 	if (sae->tmp->ec)
 		sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
 				   sae->tmp->peer_commit_element_ecc,
@@ -1029,7 +1051,7 @@
 				   sae->tmp->own_commit_element_ffc,
 				   verifier);
 
-	if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+	if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
 		wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
 			    data + 2, SHA256_MAC_LEN);
diff --git a/src/common/tnc.h b/src/common/tnc.h
new file mode 100644
index 0000000..108acf9
--- /dev/null
+++ b/src/common/tnc.h
@@ -0,0 +1,121 @@
+/*
+ * TNC - Common defines
+ * Copyright (c) 2007-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TNC_H
+#define TNC_H
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMVID;
+typedef TNC_UInt32 TNC_IMCID;
+typedef TNC_UInt32 TNC_ConnectionID;
+typedef TNC_UInt32 TNC_ConnectionState;
+typedef TNC_UInt32 TNC_RetryReason;
+typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
+typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
+typedef TNC_UInt32 TNC_MessageType;
+typedef TNC_MessageType *TNC_MessageTypeList;
+typedef TNC_UInt32 TNC_VendorID;
+typedef TNC_UInt32 TNC_Subtype;
+typedef TNC_UInt32 TNC_MessageSubtype;
+typedef TNC_UInt32 TNC_Version;
+typedef TNC_UInt32 TNC_Result;
+typedef TNC_UInt32 TNC_AttributeID;
+
+typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
+	TNC_IMVID imvID,
+	char *functionName,
+	void **pOutfunctionPointer);
+typedef TNC_Result (*TNC_TNCS_ReportMessageTypesPointer)(
+	TNC_IMVID imvID,
+	TNC_MessageTypeList supportedTypes,
+	TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCS_SendMessagePointer)(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_BufferReference message,
+	TNC_UInt32 messageLength,
+	TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCS_RequestHandshakeRetryPointer)(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_RetryReason reason);
+typedef TNC_Result (*TNC_TNCS_ProvideRecommendationPointer)(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_IMV_Action_Recommendation recommendation,
+	TNC_IMV_Evaluation_Result evaluation);
+typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
+	TNC_IMCID imcID,
+	char *functionName,
+	void **pOutfunctionPointer);
+typedef TNC_Result (*TNC_TNCC_SendMessagePointer)(
+	TNC_IMCID imcID,
+	TNC_ConnectionID connectionID,
+	TNC_BufferReference message,
+	TNC_UInt32 messageLength,
+	TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCC_ReportMessageTypesPointer)(
+	TNC_IMCID imcID,
+	TNC_MessageTypeList supportedTypes,
+	TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCC_RequestHandshakeRetryPointer)(
+	TNC_IMCID imcID,
+	TNC_ConnectionID connectionID,
+	TNC_RetryReason reason);
+
+#define TNC_IFIMV_VERSION_1 1
+#define TNC_IFIMC_VERSION_1 1
+
+#define TNC_RESULT_SUCCESS 0
+#define TNC_RESULT_NOT_INITIALIZED 1
+#define TNC_RESULT_ALREADY_INITIALIZED 2
+#define TNC_RESULT_NO_COMMON_VERSION 3
+#define TNC_RESULT_CANT_RETRY 4
+#define TNC_RESULT_WONT_RETRY 5
+#define TNC_RESULT_INVALID_PARAMETER 6
+#define TNC_RESULT_CANT_RESPOND 7
+#define TNC_RESULT_ILLEGAL_OPERATION 8
+#define TNC_RESULT_OTHER 9
+#define TNC_RESULT_FATAL 10
+
+#define TNC_CONNECTION_STATE_CREATE 0
+#define TNC_CONNECTION_STATE_HANDSHAKE 1
+#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
+#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
+#define TNC_CONNECTION_STATE_ACCESS_NONE 4
+#define TNC_CONNECTION_STATE_DELETE 5
+
+#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
+#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
+
+/* TNCC-TNCS Message Types */
+#define TNC_TNCCS_RECOMMENDATION		0x00000001
+#define TNC_TNCCS_ERROR				0x00000002
+#define TNC_TNCCS_PREFERREDLANGUAGE		0x00000003
+#define TNC_TNCCS_REASONSTRINGS			0x00000004
+
+/* Possible TNC_IMV_Action_Recommendation values: */
+enum IMV_Action_Recommendation {
+	TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
+	TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
+	TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
+	TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
+};
+
+/* Possible TNC_IMV_Evaluation_Result values: */
+enum IMV_Evaluation_Result {
+	TNC_IMV_EVALUATION_RESULT_COMPLIANT,
+	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
+	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
+	TNC_IMV_EVALUATION_RESULT_ERROR,
+	TNC_IMV_EVALUATION_RESULT_DONT_KNOW
+};
+
+#endif /* TNC_H */
diff --git a/src/common/version.h b/src/common/version.h
index 2faa8c7..1f25432 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,6 @@
 #define VERSION_STR_POSTFIX ""
 #endif /* VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.1-devel" VERSION_STR_POSTFIX
+#define VERSION_STR "2.3-devel" VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index c3afbfd..998a51a 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -14,6 +14,7 @@
 #include "crypto/sha256.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
+#include "drivers/driver.h"
 #include "ieee802_11_defs.h"
 #include "defs.h"
 #include "wpa_common.h"
@@ -56,6 +57,11 @@
 	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
 		return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_HS20
+	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+		/* FIX: This should be based on negotiated AKM */
+		return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_HS20 */
 	default:
 		return -1;
 	}
@@ -353,6 +359,18 @@
 #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)
+		return WPA_CIPHER_CCMP_256;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256)
+		return WPA_CIPHER_GCMP_256;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128)
+		return WPA_CIPHER_BIP_GMAC_128;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256)
+		return WPA_CIPHER_BIP_GMAC_256;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256)
+		return WPA_CIPHER_BIP_CMAC_256;
+	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
+		return WPA_CIPHER_GTK_NOT_USED;
 	return 0;
 }
 
@@ -385,6 +403,26 @@
 }
 
 
+static int wpa_cipher_valid_group(int cipher)
+{
+	return wpa_cipher_valid_pairwise(cipher) ||
+		cipher == WPA_CIPHER_WEP104 ||
+		cipher == WPA_CIPHER_WEP40 ||
+		cipher == WPA_CIPHER_GTK_NOT_USED;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_cipher_valid_mgmt_group(int cipher)
+{
+	return cipher == WPA_CIPHER_AES_128_CMAC ||
+		cipher == WPA_CIPHER_BIP_GMAC_128 ||
+		cipher == WPA_CIPHER_BIP_GMAC_256 ||
+		cipher == WPA_CIPHER_BIP_CMAC_256;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
 /**
  * wpa_parse_wpa_ie_rsn - Parse RSN IE
  * @rsn_ie: Buffer containing RSN IE
@@ -440,13 +478,11 @@
 
 	if (left >= RSN_SELECTOR_LEN) {
 		data->group_cipher = rsn_selector_to_bitfield(pos);
-#ifdef CONFIG_IEEE80211W
-		if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) {
-			wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group "
-				   "cipher", __func__);
+		if (!wpa_cipher_valid_group(data->group_cipher)) {
+			wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
+				   __func__, data->group_cipher);
 			return -1;
 		}
-#endif /* CONFIG_IEEE80211W */
 		pos += RSN_SELECTOR_LEN;
 		left -= RSN_SELECTOR_LEN;
 	} else if (left > 0) {
@@ -531,7 +567,7 @@
 #ifdef CONFIG_IEEE80211W
 	if (left >= 4) {
 		data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
-		if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+		if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
 			wpa_printf(MSG_DEBUG, "%s: Unsupported management "
 				   "group cipher 0x%x", __func__,
 				   data->mgmt_group_cipher);
@@ -543,8 +579,9 @@
 #endif /* CONFIG_IEEE80211W */
 
 	if (left > 0) {
-		wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
-			   __func__, left);
+		wpa_hexdump(MSG_DEBUG,
+			    "wpa_parse_wpa_ie_rsn: ignore trailing bytes",
+			    pos, left);
 	}
 
 	return 0;
@@ -681,8 +718,9 @@
 	}
 
 	if (left > 0) {
-		wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
-			   __func__, left);
+		wpa_hexdump(MSG_DEBUG,
+			    "wpa_parse_wpa_ie_wpa: ignore trailing bytes",
+			    pos, left);
 	}
 
 	return 0;
@@ -912,6 +950,12 @@
 		return "CCMP+TKIP";
 	case WPA_CIPHER_GCMP:
 		return "GCMP";
+	case WPA_CIPHER_GCMP_256:
+		return "GCMP-256";
+	case WPA_CIPHER_CCMP_256:
+		return "CCMP-256";
+	case WPA_CIPHER_GTK_NOT_USED:
+		return "GTK_NOT_USED";
 	default:
 		return "UNKNOWN";
 	}
@@ -959,6 +1003,30 @@
 }
 
 
+u32 wpa_akm_to_suite(int akm)
+{
+	if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
+		return WLAN_AKM_SUITE_FT_8021X;
+	if (akm & WPA_KEY_MGMT_FT_PSK)
+		return WLAN_AKM_SUITE_FT_PSK;
+	if (akm & WPA_KEY_MGMT_IEEE8021X)
+		return WLAN_AKM_SUITE_8021X;
+	if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
+		return WLAN_AKM_SUITE_8021X_SHA256;
+	if (akm & WPA_KEY_MGMT_IEEE8021X)
+		return WLAN_AKM_SUITE_8021X;
+	if (akm & WPA_KEY_MGMT_PSK_SHA256)
+		return WLAN_AKM_SUITE_PSK_SHA256;
+	if (akm & WPA_KEY_MGMT_PSK)
+		return WLAN_AKM_SUITE_PSK;
+	if (akm & WPA_KEY_MGMT_CCKM)
+		return WLAN_AKM_SUITE_CCKM;
+	if (akm & WPA_KEY_MGMT_OSEN)
+		return WLAN_AKM_SUITE_OSEN;
+	return 0;
+}
+
+
 int wpa_compare_rsn_ie(int ft_initial_assoc,
 		       const u8 *ie1, size_t ie1len,
 		       const u8 *ie2, size_t ie2len)
@@ -1078,8 +1146,15 @@
 int wpa_cipher_key_len(int cipher)
 {
 	switch (cipher) {
+	case WPA_CIPHER_CCMP_256:
+	case WPA_CIPHER_GCMP_256:
+	case WPA_CIPHER_BIP_GMAC_256:
+	case WPA_CIPHER_BIP_CMAC_256:
+		return 32;
 	case WPA_CIPHER_CCMP:
 	case WPA_CIPHER_GCMP:
+	case WPA_CIPHER_AES_128_CMAC:
+	case WPA_CIPHER_BIP_GMAC_128:
 		return 16;
 	case WPA_CIPHER_TKIP:
 		return 32;
@@ -1096,6 +1171,8 @@
 int wpa_cipher_rsc_len(int cipher)
 {
 	switch (cipher) {
+	case WPA_CIPHER_CCMP_256:
+	case WPA_CIPHER_GCMP_256:
 	case WPA_CIPHER_CCMP:
 	case WPA_CIPHER_GCMP:
 	case WPA_CIPHER_TKIP:
@@ -1112,6 +1189,10 @@
 int wpa_cipher_to_alg(int cipher)
 {
 	switch (cipher) {
+	case WPA_CIPHER_CCMP_256:
+		return WPA_ALG_CCMP_256;
+	case WPA_CIPHER_GCMP_256:
+		return WPA_ALG_GCMP_256;
 	case WPA_CIPHER_CCMP:
 		return WPA_ALG_CCMP;
 	case WPA_CIPHER_GCMP:
@@ -1121,34 +1202,24 @@
 	case WPA_CIPHER_WEP104:
 	case WPA_CIPHER_WEP40:
 		return WPA_ALG_WEP;
+	case WPA_CIPHER_AES_128_CMAC:
+		return WPA_ALG_IGTK;
+	case WPA_CIPHER_BIP_GMAC_128:
+		return WPA_ALG_BIP_GMAC_128;
+	case WPA_CIPHER_BIP_GMAC_256:
+		return WPA_ALG_BIP_GMAC_256;
+	case WPA_CIPHER_BIP_CMAC_256:
+		return WPA_ALG_BIP_CMAC_256;
 	}
 	return WPA_ALG_NONE;
 }
 
 
-enum wpa_cipher wpa_cipher_to_suite_driver(int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_NONE:
-		return CIPHER_NONE;
-	case WPA_CIPHER_WEP40:
-		return CIPHER_WEP40;
-	case WPA_CIPHER_WEP104:
-		return CIPHER_WEP104;
-	case WPA_CIPHER_CCMP:
-		return CIPHER_CCMP;
-	case WPA_CIPHER_GCMP:
-		return CIPHER_GCMP;
-	case WPA_CIPHER_TKIP:
-	default:
-		return CIPHER_TKIP;
-	}
-}
-
-
 int wpa_cipher_valid_pairwise(int cipher)
 {
-	return cipher == WPA_CIPHER_CCMP ||
+	return cipher == WPA_CIPHER_CCMP_256 ||
+		cipher == WPA_CIPHER_GCMP_256 ||
+		cipher == WPA_CIPHER_CCMP ||
 		cipher == WPA_CIPHER_GCMP ||
 		cipher == WPA_CIPHER_TKIP;
 }
@@ -1156,6 +1227,10 @@
 
 u32 wpa_cipher_to_suite(int proto, int cipher)
 {
+	if (cipher & WPA_CIPHER_CCMP_256)
+		return RSN_CIPHER_SUITE_CCMP_256;
+	if (cipher & WPA_CIPHER_GCMP_256)
+		return RSN_CIPHER_SUITE_GCMP_256;
 	if (cipher & WPA_CIPHER_CCMP)
 		return (proto == WPA_PROTO_RSN ?
 			RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
@@ -1173,65 +1248,80 @@
 	if (cipher & WPA_CIPHER_NONE)
 		return (proto == WPA_PROTO_RSN ?
 			RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+	if (cipher & WPA_CIPHER_GTK_NOT_USED)
+		return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+	if (cipher & WPA_CIPHER_AES_128_CMAC)
+		return RSN_CIPHER_SUITE_AES_128_CMAC;
+	if (cipher & WPA_CIPHER_BIP_GMAC_128)
+		return RSN_CIPHER_SUITE_BIP_GMAC_128;
+	if (cipher & WPA_CIPHER_BIP_GMAC_256)
+		return RSN_CIPHER_SUITE_BIP_GMAC_256;
+	if (cipher & WPA_CIPHER_BIP_CMAC_256)
+		return RSN_CIPHER_SUITE_BIP_CMAC_256;
 	return 0;
 }
 
 
-int rsn_cipher_put_suites(u8 *pos, int ciphers)
+int rsn_cipher_put_suites(u8 *start, int ciphers)
 {
-	int num_suites = 0;
+	u8 *pos = start;
 
+	if (ciphers & WPA_CIPHER_CCMP_256) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256);
+		pos += RSN_SELECTOR_LEN;
+	}
+	if (ciphers & WPA_CIPHER_GCMP_256) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256);
+		pos += RSN_SELECTOR_LEN;
+	}
 	if (ciphers & WPA_CIPHER_CCMP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
 		pos += RSN_SELECTOR_LEN;
-		num_suites++;
 	}
 	if (ciphers & WPA_CIPHER_GCMP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
 		pos += RSN_SELECTOR_LEN;
-		num_suites++;
 	}
 	if (ciphers & WPA_CIPHER_TKIP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 		pos += RSN_SELECTOR_LEN;
-		num_suites++;
 	}
 	if (ciphers & WPA_CIPHER_NONE) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
 		pos += RSN_SELECTOR_LEN;
-		num_suites++;
 	}
 
-	return num_suites;
+	return (pos - start) / RSN_SELECTOR_LEN;
 }
 
 
-int wpa_cipher_put_suites(u8 *pos, int ciphers)
+int wpa_cipher_put_suites(u8 *start, int ciphers)
 {
-	int num_suites = 0;
+	u8 *pos = start;
 
 	if (ciphers & WPA_CIPHER_CCMP) {
 		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
 		pos += WPA_SELECTOR_LEN;
-		num_suites++;
 	}
 	if (ciphers & WPA_CIPHER_TKIP) {
 		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
 		pos += WPA_SELECTOR_LEN;
-		num_suites++;
 	}
 	if (ciphers & WPA_CIPHER_NONE) {
 		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
 		pos += WPA_SELECTOR_LEN;
-		num_suites++;
 	}
 
-	return num_suites;
+	return (pos - start) / RSN_SELECTOR_LEN;
 }
 
 
 int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
 {
+	if (ciphers & WPA_CIPHER_CCMP_256)
+		return WPA_CIPHER_CCMP_256;
+	if (ciphers & WPA_CIPHER_GCMP_256)
+		return WPA_CIPHER_GCMP_256;
 	if (ciphers & WPA_CIPHER_CCMP)
 		return WPA_CIPHER_CCMP;
 	if (ciphers & WPA_CIPHER_GCMP)
@@ -1246,10 +1336,16 @@
 
 int wpa_pick_group_cipher(int ciphers)
 {
+	if (ciphers & WPA_CIPHER_CCMP_256)
+		return WPA_CIPHER_CCMP_256;
+	if (ciphers & WPA_CIPHER_GCMP_256)
+		return WPA_CIPHER_GCMP_256;
 	if (ciphers & WPA_CIPHER_CCMP)
 		return WPA_CIPHER_CCMP;
 	if (ciphers & WPA_CIPHER_GCMP)
 		return WPA_CIPHER_GCMP;
+	if (ciphers & WPA_CIPHER_GTK_NOT_USED)
+		return WPA_CIPHER_GTK_NOT_USED;
 	if (ciphers & WPA_CIPHER_TKIP)
 		return WPA_CIPHER_TKIP;
 	if (ciphers & WPA_CIPHER_WEP104)
@@ -1280,7 +1376,11 @@
 			end++;
 		last = *end == '\0';
 		*end = '\0';
-		if (os_strcmp(start, "CCMP") == 0)
+		if (os_strcmp(start, "CCMP-256") == 0)
+			val |= WPA_CIPHER_CCMP_256;
+		else if (os_strcmp(start, "GCMP-256") == 0)
+			val |= WPA_CIPHER_GCMP_256;
+		else if (os_strcmp(start, "CCMP") == 0)
 			val |= WPA_CIPHER_CCMP;
 		else if (os_strcmp(start, "GCMP") == 0)
 			val |= WPA_CIPHER_GCMP;
@@ -1292,6 +1392,8 @@
 			val |= WPA_CIPHER_WEP40;
 		else if (os_strcmp(start, "NONE") == 0)
 			val |= WPA_CIPHER_NONE;
+		else if (os_strcmp(start, "GTK_NOT_USED") == 0)
+			val |= WPA_CIPHER_GTK_NOT_USED;
 		else {
 			os_free(buf);
 			return -1;
@@ -1312,6 +1414,20 @@
 	char *pos = start;
 	int ret;
 
+	if (ciphers & WPA_CIPHER_CCMP_256) {
+		ret = os_snprintf(pos, end - pos, "%sCCMP-256",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_GCMP_256) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP-256",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
 	if (ciphers & WPA_CIPHER_CCMP) {
 		ret = os_snprintf(pos, end - pos, "%sCCMP",
 				  pos == start ? "" : delim);
@@ -1373,5 +1489,86 @@
 		return WPA_CIPHER_TKIP;
 	if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP)
 		return WPA_CIPHER_GCMP;
+	if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP |
+			 WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256)
+		return WPA_CIPHER_GCMP_256;
+	if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+			 WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256)
+		return WPA_CIPHER_CCMP_256;
 	return WPA_CIPHER_CCMP;
 }
+
+
+static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
+				    int capa_trigger, u8 *param_trigger)
+{
+	if (os_strcmp(start, trigger) != 0)
+		return 0;
+	if (!capa_trigger)
+		return 0;
+
+	*param_trigger = 1;
+	return 1;
+}
+
+
+struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
+						struct wpa_driver_capa *capa)
+{
+	struct wowlan_triggers *triggers;
+	char *start, *end, *buf;
+	int last;
+
+	if (!wowlan_triggers)
+		return NULL;
+
+	buf = os_strdup(wowlan_triggers);
+	if (buf == NULL)
+		return NULL;
+
+	triggers = os_zalloc(sizeof(*triggers));
+	if (triggers == NULL)
+		goto out;
+
+#define CHECK_TRIGGER(trigger) \
+	wpa_check_wowlan_trigger(start, #trigger,			\
+				  capa->wowlan_triggers.trigger,	\
+				  &triggers->trigger)
+
+	start = buf;
+	while (*start != '\0') {
+		while (isblank(*start))
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (!isblank(*end) && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+
+		if (!CHECK_TRIGGER(any) &&
+		    !CHECK_TRIGGER(disconnect) &&
+		    !CHECK_TRIGGER(magic_pkt) &&
+		    !CHECK_TRIGGER(gtk_rekey_failure) &&
+		    !CHECK_TRIGGER(eap_identity_req) &&
+		    !CHECK_TRIGGER(four_way_handshake) &&
+		    !CHECK_TRIGGER(rfkill_release)) {
+			wpa_printf(MSG_DEBUG,
+				   "Unknown/unsupported wowlan trigger '%s'",
+				   start);
+			os_free(triggers);
+			triggers = NULL;
+			goto out;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+#undef CHECK_TRIGGER
+
+out:
+	os_free(buf);
+	return triggers;
+}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 2d63662..0ef5a9d 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -21,10 +21,12 @@
 #define WPA_GTK_MAX_LEN 32
 
 #define WPA_ALLOWED_PAIRWISE_CIPHERS \
-(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
+WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
 #define WPA_ALLOWED_GROUP_CIPHERS \
 (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \
-WPA_CIPHER_WEP40)
+WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
+WPA_CIPHER_GTK_NOT_USED)
 
 #define WPA_SELECTOR_LEN 4
 #define WPA_VERSION 1
@@ -60,7 +62,12 @@
 #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 #define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
 #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_384 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_384 \
+RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
+#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
 
 #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
 #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -70,11 +77,14 @@
 #endif
 #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
 #define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
-#ifdef CONFIG_IEEE80211W
 #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
-#endif /* CONFIG_IEEE80211W */
 #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
 
 /* EAPOL-Key Key Data Encapsulation
  * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
@@ -98,6 +108,9 @@
 #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
 #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 
+#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
+#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
+
 #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 
 #define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
@@ -115,6 +128,7 @@
 
 #ifdef CONFIG_IEEE80211W
 #define WPA_IGTK_LEN 16
+#define WPA_IGTK_MAX_LEN 32
 #endif /* CONFIG_IEEE80211W */
 
 
@@ -143,6 +157,7 @@
 
 /* IEEE 802.11, 8.5.2 EAPOL-Key frames */
 #define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
+#define WPA_KEY_INFO_TYPE_AKM_DEFINED 0
 #define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
 #define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
 #define WPA_KEY_INFO_TYPE_AES_128_CMAC 3
@@ -269,10 +284,11 @@
 } 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_LEN];
+	u8 igtk[WPA_IGTK_MAX_LEN];
 } STRUCT_PACKED;
 #endif /* CONFIG_IEEE80211W */
 
@@ -361,6 +377,7 @@
 
 const char * wpa_cipher_txt(int cipher);
 const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
+u32 wpa_akm_to_suite(int akm);
 int wpa_compare_rsn_ie(int ft_initial_assoc,
 		       const u8 *ie1, size_t ie1len,
 		       const u8 *ie2, size_t ie2len);
@@ -392,8 +409,8 @@
 int wpa_cipher_key_len(int cipher);
 int wpa_cipher_rsc_len(int cipher);
 int wpa_cipher_to_alg(int cipher);
-enum wpa_cipher wpa_cipher_to_suite_driver(int cipher);
 int wpa_cipher_valid_pairwise(int cipher);
+int wpa_cipher_valid_mgmt_group(int cipher);
 u32 wpa_cipher_to_suite(int proto, int cipher);
 int rsn_cipher_put_suites(u8 *pos, int ciphers);
 int wpa_cipher_put_suites(u8 *pos, int ciphers);
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index 83788d7..5820a13 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -25,6 +25,10 @@
 #include "private/android_filesystem_config.h"
 #endif /* ANDROID */
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+#include <net/if.h>
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+
 #include "wpa_ctrl.h"
 #include "common.h"
 
@@ -46,8 +50,13 @@
 struct wpa_ctrl {
 #ifdef CONFIG_CTRL_IFACE_UDP
 	int s;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	struct sockaddr_in6 local;
+	struct sockaddr_in6 dest;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	struct sockaddr_in local;
 	struct sockaddr_in dest;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	char *cookie;
 	char *remote_ifname;
 	char *remote_ip;
@@ -279,19 +288,33 @@
 		return NULL;
 	os_memset(ctrl, 0, sizeof(*ctrl));
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	if (ctrl->s < 0) {
 		perror("socket");
 		os_free(ctrl);
 		return NULL;
 	}
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	ctrl->local.sin6_family = AF_INET6;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	ctrl->local.sin6_addr = in6addr_any;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+	inet_pton(AF_INET6, "::1", &ctrl->local.sin6_addr);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	ctrl->local.sin_family = AF_INET;
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 	ctrl->local.sin_addr.s_addr = INADDR_ANY;
 #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 	ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+
 	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
 		 sizeof(ctrl->local)) < 0) {
 		close(ctrl->s);
@@ -299,14 +322,24 @@
 		return NULL;
 	}
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	ctrl->dest.sin6_family = AF_INET6;
+	inet_pton(AF_INET6, "::1", &ctrl->dest.sin6_addr);
+	ctrl->dest.sin6_port = htons(WPA_CTRL_IFACE_PORT);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	ctrl->dest.sin_family = AF_INET;
 	ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
 	ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 	if (ctrl_path) {
 		char *port, *name;
 		int port_id;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+		char *scope;
+		int scope_id = 0;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 		name = os_strdup(ctrl_path);
 		if (name == NULL) {
@@ -314,7 +347,11 @@
 			os_free(ctrl);
 			return NULL;
 		}
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+		port = os_strchr(name, ',');
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 		port = os_strchr(name, ':');
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 		if (port) {
 			port_id = atoi(&port[1]);
@@ -322,7 +359,16 @@
 		} else
 			port_id = WPA_CTRL_IFACE_PORT;
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+		scope = os_strchr(name, '%');
+		if (scope) {
+			scope_id = if_nametoindex(&scope[1]);
+			scope[0] = '\0';
+		}
+		h = gethostbyname2(name, AF_INET6);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 		h = gethostbyname(name);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 		ctrl->remote_ip = os_strdup(name);
 		os_free(name);
 		if (h == NULL) {
@@ -332,16 +378,33 @@
 			os_free(ctrl);
 			return NULL;
 		}
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+		ctrl->dest.sin6_scope_id = scope_id;
+		ctrl->dest.sin6_port = htons(port_id);
+		os_memcpy(&ctrl->dest.sin6_addr, h->h_addr, h->h_length);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 		ctrl->dest.sin_port = htons(port_id);
-		os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr,
-			  h->h_length);
+		os_memcpy(&ctrl->dest.sin_addr.s_addr, h->h_addr, h->h_length);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	} else
 		ctrl->remote_ip = os_strdup("localhost");
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
 	if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
 		    sizeof(ctrl->dest)) < 0) {
-		perror("connect");
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+		char addr[INET6_ADDRSTRLEN];
+		wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
+			   inet_ntop(AF_INET6, &ctrl->dest.sin6_addr, addr,
+				     sizeof(ctrl->dest)),
+			   ntohs(ctrl->dest.sin6_port),
+			   strerror(errno));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+		wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
+			   inet_ntoa(ctrl->dest.sin_addr),
+			   ntohs(ctrl->dest.sin_port),
+			   strerror(errno));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 		close(ctrl->s);
 		os_free(ctrl->remote_ip);
 		os_free(ctrl);
@@ -391,7 +454,7 @@
 		     void (*msg_cb)(char *msg, size_t len))
 {
 	struct timeval tv;
-	struct os_time started_at;
+	struct os_reltime started_at;
 	int res;
 	fd_set rfds;
 	const char *_cmd;
@@ -430,12 +493,12 @@
 			 * longer before giving up.
 			 */
 			if (started_at.sec == 0)
-				os_get_time(&started_at);
+				os_get_reltime(&started_at);
 			else {
-				struct os_time n;
-				os_get_time(&n);
+				struct os_reltime n;
+				os_get_reltime(&n);
 				/* Try for a few seconds. */
-				if (n.sec > started_at.sec + 5)
+				if (os_reltime_expired(&n, &started_at, 5))
 					goto send_err;
 			}
 			os_sleep(1, 0);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index fd7f686..d91594e 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -54,6 +54,8 @@
 #define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
 /** Temporarily disabled network block re-enabled */
 #define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED "
+/** New scan started */
+#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED "
 /** New scan results available */
 #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
 /** wpa_supplicant state change */
@@ -62,14 +64,22 @@
 #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
 /** A BSS entry was removed (followed by BSS entry id and BSSID) */
 #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
-#ifdef ANDROID_P2P
-/** Notify the Userspace about the freq conflict */
-#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
-#endif
+/** Change in the signal level was reported by the driver */
+#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
+/** Regulatory domain channel */
+#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
 
 /** RSN IBSS 4-way handshakes completed with specified peer */
 #define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
 
+/** Notification of frequency conflict due to a concurrent operation.
+ *
+ * The indicated network is disabled and needs to be re-enabled before it can
+ * be used again.
+ */
+#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
+/** Frequency ranges that the driver recommends to avoid */
+#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
 /** Available WPS AP with active PBC found in scan results */
@@ -143,14 +153,39 @@
 #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
 #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
 #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
+#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
+#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
+#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
+#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
 
 /* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
 #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
+#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
 
 #define INTERWORKING_AP "INTERWORKING-AP "
+#define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED "
 #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
+#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED "
+#define INTERWORKING_SELECTED "INTERWORKING-SELECTED "
+
+/* Credential block added; parameters: <id> */
+#define CRED_ADDED "CRED-ADDED "
+/* Credential block modified; parameters: <id> <field> */
+#define CRED_MODIFIED "CRED-MODIFIED "
+/* Credential block removed; parameters: <id> */
+#define CRED_REMOVED "CRED-REMOVED "
 
 #define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
+/* parameters: <addr> <dialog_token> <freq> */
+#define GAS_QUERY_START "GAS-QUERY-START "
+/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
+#define GAS_QUERY_DONE "GAS-QUERY-DONE "
+
+#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
+#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
+
+#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
+#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
 
 /* hostapd control interface - fixed message prefixes */
 #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
@@ -166,6 +201,21 @@
 #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
 #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
 
+#define AP_EVENT_ENABLED "AP-ENABLED "
+#define AP_EVENT_DISABLED "AP-DISABLED "
+
+#define ACS_EVENT_STARTED "ACS-STARTED "
+#define ACS_EVENT_COMPLETED "ACS-COMPLETED "
+#define ACS_EVENT_FAILED "ACS-FAILED "
+
+#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED "
+#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL "
+#define DFS_EVENT_CAC_START "DFS-CAC-START "
+#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+
+#define AP_CSA_FINISHED "AP-CSA-FINISHED "
+
 /* BSS command information masks */
 
 #define WPA_BSS_MASK_ALL		0xFFFDFFFF
@@ -189,6 +239,25 @@
 #define WPA_BSS_MASK_DELIM		BIT(17)
 
 
+/* VENDOR_ELEM_* frame id values */
+enum wpa_vendor_elem_frame {
+	VENDOR_ELEM_PROBE_REQ_P2P = 0,
+	VENDOR_ELEM_PROBE_RESP_P2P = 1,
+	VENDOR_ELEM_PROBE_RESP_P2P_GO = 2,
+	VENDOR_ELEM_BEACON_P2P_GO = 3,
+	VENDOR_ELEM_P2P_PD_REQ = 4,
+	VENDOR_ELEM_P2P_PD_RESP = 5,
+	VENDOR_ELEM_P2P_GO_NEG_REQ = 6,
+	VENDOR_ELEM_P2P_GO_NEG_RESP = 7,
+	VENDOR_ELEM_P2P_GO_NEG_CONF = 8,
+	VENDOR_ELEM_P2P_INV_REQ = 9,
+	VENDOR_ELEM_P2P_INV_RESP = 10,
+	VENDOR_ELEM_P2P_ASSOC_REQ = 11,
+	VENDOR_ELEM_P2P_ASSOC_RESP = 12,
+	NUM_VENDOR_ELEM_FRAMES
+};
+
+
 /* wpa_supplicant/hostapd control interface access */
 
 /**
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
new file mode 100644
index 0000000..28913b9
--- /dev/null
+++ b/src/common/wpa_helpers.c
@@ -0,0 +1,292 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+
+#include "common.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+
+
+char *wpas_ctrl_path = "/var/run/wpa_supplicant/";
+static int default_timeout = 60;
+
+
+static struct wpa_ctrl * wpa_open_ctrl(const char *ifname)
+{
+	char buf[128];
+	struct wpa_ctrl *ctrl;
+
+	os_snprintf(buf, sizeof(buf), "%s%s", wpas_ctrl_path, ifname);
+	ctrl = wpa_ctrl_open(buf);
+	if (ctrl == NULL)
+		printf("wpa_command: wpa_ctrl_open(%s) failed\n", buf);
+	return ctrl;
+}
+
+
+int wpa_command(const char *ifname, const char *cmd)
+{
+	struct wpa_ctrl *ctrl;
+	char buf[128];
+	size_t len;
+
+	printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+	ctrl = wpa_open_ctrl(ifname);
+	if (ctrl == NULL)
+		return -1;
+	len = sizeof(buf);
+	if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL) < 0) {
+		printf("wpa_command: wpa_ctrl_request failed\n");
+		wpa_ctrl_close(ctrl);
+		return -1;
+	}
+	wpa_ctrl_close(ctrl);
+	buf[len] = '\0';
+	if (strncmp(buf, "FAIL", 4) == 0) {
+		printf("wpa_command: Command failed (FAIL received)\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+int wpa_command_resp(const char *ifname, const char *cmd,
+		     char *resp, size_t resp_size)
+{
+	struct wpa_ctrl *ctrl;
+	size_t len;
+
+	printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+	ctrl = wpa_open_ctrl(ifname);
+	if (ctrl == NULL)
+		return -1;
+	len = resp_size;
+	if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), resp, &len, NULL) < 0) {
+		printf("wpa_command: wpa_ctrl_request failed\n");
+		wpa_ctrl_close(ctrl);
+		return -1;
+	}
+	wpa_ctrl_close(ctrl);
+	resp[len] = '\0';
+	return 0;
+}
+
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname)
+{
+	struct wpa_ctrl *ctrl;
+
+	ctrl = wpa_open_ctrl(ifname);
+	if (ctrl == NULL)
+		return NULL;
+	if (wpa_ctrl_attach(ctrl) < 0) {
+		wpa_ctrl_close(ctrl);
+		return NULL;
+	}
+
+	return ctrl;
+}
+
+
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+		       const char *event, const char *event2,
+		       char *buf, size_t buf_size)
+{
+	int fd, ret;
+	fd_set rfd;
+	char *pos;
+	struct timeval tv;
+	time_t start, now;
+
+	printf("Waiting for wpa_cli event %s\n", event);
+	fd = wpa_ctrl_get_fd(mon);
+	if (fd < 0)
+		return -1;
+
+	time(&start);
+	while (1) {
+		size_t len;
+
+		FD_ZERO(&rfd);
+		FD_SET(fd, &rfd);
+		tv.tv_sec = default_timeout;
+		tv.tv_usec = 0;
+		ret = select(fd + 1, &rfd, NULL, NULL, &tv);
+		if (ret == 0) {
+			printf("Timeout on waiting for event %s\n", event);
+			return -1;
+		}
+		if (ret < 0) {
+			printf("select: %s\n", strerror(errno));
+			return -1;
+		}
+		len = buf_size;
+		if (wpa_ctrl_recv(mon, buf, &len) < 0) {
+			printf("Failure while waiting for event %s\n", event);
+			return -1;
+		}
+		if (len == buf_size)
+			len--;
+		buf[len] = '\0';
+
+		pos = strchr(buf, '>');
+		if (pos &&
+		    (strncmp(pos + 1, event, strlen(event)) == 0 ||
+		     (event2 &&
+		      strncmp(pos + 1, event2, strlen(event2)) == 0)))
+			return 0; /* Event found */
+
+		time(&now);
+		if ((int) (now - start) > default_timeout) {
+			printf("Timeout on waiting for event %s\n", event);
+			return -1;
+		}
+	}
+}
+
+
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+		      const char *event, char *buf, size_t buf_size)
+{
+	return get_wpa_cli_event2(mon, event, NULL, buf, buf_size);
+}
+
+
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+		   size_t obuf_size)
+{
+	struct wpa_ctrl *ctrl;
+	char buf[4096];
+	char *pos, *end;
+	size_t len, flen;
+
+	ctrl = wpa_open_ctrl(ifname);
+	if (ctrl == NULL)
+		return -1;
+	len = sizeof(buf);
+	if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
+		wpa_ctrl_close(ctrl);
+		return -1;
+	}
+	wpa_ctrl_close(ctrl);
+	buf[len] = '\0';
+
+	flen = strlen(field);
+	pos = buf;
+	while (pos + flen < buf + len) {
+		if (pos > buf) {
+			if (*pos != '\n') {
+				pos++;
+				continue;
+			}
+			pos++;
+		}
+		if (strncmp(pos, field, flen) != 0 || pos[flen] != '=') {
+			pos++;
+			continue;
+		}
+		pos += flen + 1;
+		end = strchr(pos, '\n');
+		if (end == NULL)
+			return -1;
+		*end++ = '\0';
+		if (end - pos > (int) obuf_size)
+			return -1;
+		memcpy(obuf, pos, end - pos);
+		return 0;
+	}
+
+	return -1;
+}
+
+
+int wait_ip_addr(const char *ifname, int timeout)
+{
+	char ip[30];
+	int count = timeout;
+	struct wpa_ctrl *ctrl;
+
+	while (count > 0) {
+		printf("%s: ifname='%s' - %d seconds remaining\n",
+		       __func__, ifname, count);
+		count--;
+		if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
+		    && strlen(ip) > 0) {
+			printf("IP address found: '%s'\n", ip);
+			return 0;
+		}
+		ctrl = wpa_open_ctrl(ifname);
+		if (ctrl == NULL)
+			return -1;
+		wpa_ctrl_close(ctrl);
+		sleep(1);
+	}
+	printf("%s: Could not get IP address for ifname='%s'", __func__,
+	       ifname);
+	return -1;
+}
+
+
+int add_network(const char *ifname)
+{
+	char res[30];
+
+	if (wpa_command_resp(ifname, "ADD_NETWORK", res, sizeof(res)) < 0)
+		return -1;
+	return atoi(res);
+}
+
+
+int set_network(const char *ifname, int id, const char *field,
+		const char *value)
+{
+	char buf[200];
+	snprintf(buf, sizeof(buf), "SET_NETWORK %d %s %s", id, field, value);
+	return wpa_command(ifname, buf);
+}
+
+
+int set_network_quoted(const char *ifname, int id, const char *field,
+		       const char *value)
+{
+	char buf[200];
+	snprintf(buf, sizeof(buf), "SET_NETWORK %d %s \"%s\"",
+		 id, field, value);
+	return wpa_command(ifname, buf);
+}
+
+
+int add_cred(const char *ifname)
+{
+	char res[30];
+
+	if (wpa_command_resp(ifname, "ADD_CRED", res, sizeof(res)) < 0)
+		return -1;
+	return atoi(res);
+}
+
+
+int set_cred(const char *ifname, int id, const char *field, const char *value)
+{
+	char buf[200];
+	snprintf(buf, sizeof(buf), "SET_CRED %d %s %s", id, field, value);
+	return wpa_command(ifname, buf);
+}
+
+
+int set_cred_quoted(const char *ifname, int id, const char *field,
+		    const char *value)
+{
+	char buf[200];
+	snprintf(buf, sizeof(buf), "SET_CRED %d %s \"%s\"",
+		 id, field, value);
+	return wpa_command(ifname, buf);
+}
diff --git a/src/common/wpa_helpers.h b/src/common/wpa_helpers.h
new file mode 100644
index 0000000..54c2872
--- /dev/null
+++ b/src/common/wpa_helpers.h
@@ -0,0 +1,37 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_HELPERS_H
+#define WPA_HELPERS_H
+
+int wpa_command(const char *ifname, const char *cmd);
+int wpa_command_resp(const char *ifname, const char *cmd,
+		     char *resp, size_t resp_size);
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+		   size_t obuf_size);
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname);
+int wait_ip_addr(const char *ifname, int timeout);
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+		      const char *event, char *buf, size_t buf_size);
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+		       const char *event, const char *event2,
+		       char *buf, size_t buf_size);
+
+int add_network(const char *ifname);
+int set_network(const char *ifname, int id, const char *field,
+		const char *value);
+int set_network_quoted(const char *ifname, int id, const char *field,
+		       const char *value);
+int add_cred(const char *ifname);
+int set_cred(const char *ifname, int id, const char *field, const char *value);
+int set_cred_quoted(const char *ifname, int id, const char *field,
+		    const char *value);
+
+#endif /* WPA_HELPERS_H */
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index a605a65..2a92109 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -1,7 +1,7 @@
 all: libcrypto.a
 
 clean:
-	rm -f *~ *.o *.d libcrypto.a
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a
 
 install:
 	@echo Nothing to be made.
@@ -9,6 +9,7 @@
 
 include ../lib.rules
 
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
 CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
 CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
 #CFLAGS += -DALL_DH_GROUPS
diff --git a/src/crypto/aes-ccm.c b/src/crypto/aes-ccm.c
index d14670d..cf22778 100644
--- a/src/crypto/aes-ccm.c
+++ b/src/crypto/aes-ccm.c
@@ -203,7 +203,7 @@
 
 	aes_encrypt_deinit(aes);
 
-	if (os_memcmp(x, t, M) != 0) {
+	if (os_memcmp_const(x, t, M) != 0) {
 		wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch");
 		return -1;
 	}
diff --git a/src/crypto/aes-gcm.c b/src/crypto/aes-gcm.c
index 3d91c71..84294d2 100644
--- a/src/crypto/aes-gcm.c
+++ b/src/crypto/aes-gcm.c
@@ -310,7 +310,7 @@
 
 	aes_encrypt_deinit(aes);
 
-	if (os_memcmp(tag, T, 16) != 0) {
+	if (os_memcmp_const(tag, T, 16) != 0) {
 		wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
 		return -1;
 	}
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 9bccaaa..f2d5662 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -271,6 +271,10 @@
  */
 struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
 
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+			       const u8 *e, size_t e_len);
+
 /**
  * crypto_private_key_import - Import an RSA private key
  * @key: Key buffer (DER encoded RSA private key)
@@ -534,16 +538,6 @@
 			  struct crypto_bignum *d);
 
 /**
- * crypto_bignum_rshift - b = a >> n
- * @a: Bignum
- * @n: Number of bits to shift
- * @b: Bignum; used to store the result of a >> n
- * Returns: 0 on success, -1 on failure
- */
-int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-			 struct crypto_bignum *b);
-
-/**
  * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
  * @a: Bignum
  * @b: Bignum
diff --git a/src/crypto/crypto_internal-rsa.c b/src/crypto/crypto_internal-rsa.c
index 54209fa..dc7f350 100644
--- a/src/crypto/crypto_internal-rsa.c
+++ b/src/crypto/crypto_internal-rsa.c
@@ -26,6 +26,15 @@
 }
 
 
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+			       const u8 *e, size_t e_len)
+{
+	return (struct crypto_public_key *)
+		crypto_rsa_import_public_key_parts(n, n_len, e, e_len);
+}
+
+
 struct crypto_private_key * crypto_private_key_import(const u8 *key,
 						      size_t len,
 						      const char *passwd)
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 5215c00..f02aaac 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -26,6 +26,8 @@
 #include "common.h"
 #include "wpabuf.h"
 #include "dh_group5.h"
+#include "sha1.h"
+#include "sha256.h"
 #include "crypto.h"
 
 #if OPENSSL_VERSION_NUMBER < 0x00907000
@@ -338,10 +340,10 @@
 	ret = 0;
 
 error:
-	BN_free(bn_base);
-	BN_free(bn_exp);
-	BN_free(bn_modulus);
-	BN_free(bn_result);
+	BN_clear_free(bn_base);
+	BN_clear_free(bn_exp);
+	BN_clear_free(bn_modulus);
+	BN_clear_free(bn_result);
 	BN_CTX_free(ctx);
 	return ret;
 }
@@ -569,12 +571,12 @@
 	if (keylen < 0)
 		goto err;
 	wpabuf_put(res, keylen);
-	BN_free(pub_key);
+	BN_clear_free(pub_key);
 
 	return res;
 
 err:
-	BN_free(pub_key);
+	BN_clear_free(pub_key);
 	wpabuf_free(res);
 	return NULL;
 }
@@ -914,13 +916,6 @@
 }
 
 
-int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-			 struct crypto_bignum *b)
-{
-	return BN_rshift((BIGNUM *) b, (const BIGNUM *) a, n) ? 0 : -1;
-}
-
-
 int crypto_bignum_inverse(const struct crypto_bignum *a,
 			  const struct crypto_bignum *b,
 			  struct crypto_bignum *c)
@@ -1071,7 +1066,7 @@
 {
 	if (e == NULL)
 		return;
-	BN_free(e->order);
+	BN_clear_free(e->order);
 	EC_GROUP_free(e->group);
 	BN_CTX_free(e->bnctx);
 	os_free(e);
@@ -1143,8 +1138,8 @@
 		ret = 0;
 	}
 
-	BN_free(x_bn);
-	BN_free(y_bn);
+	BN_clear_free(x_bn);
+	BN_clear_free(y_bn);
 	return ret;
 }
 
@@ -1160,20 +1155,20 @@
 	y = BN_bin2bn(val + len, len, NULL);
 	elem = EC_POINT_new(e->group);
 	if (x == NULL || y == NULL || elem == NULL) {
-		BN_free(x);
-		BN_free(y);
-		EC_POINT_free(elem);
+		BN_clear_free(x);
+		BN_clear_free(y);
+		EC_POINT_clear_free(elem);
 		return NULL;
 	}
 
 	if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
 						 e->bnctx)) {
-		EC_POINT_free(elem);
+		EC_POINT_clear_free(elem);
 		elem = NULL;
 	}
 
-	BN_free(x);
-	BN_free(y);
+	BN_clear_free(x);
+	BN_clear_free(y);
 
 	return (struct crypto_ec_point *) elem;
 }
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 3a675df..58e94c3 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1169,7 +1169,7 @@
 #endif /* ALL_DH_GROUPS */
 };
 
-#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0]))
+#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups)
 
 
 const struct dh_group * dh_groups_get(int id)
diff --git a/src/crypto/fips_prf_cryptoapi.c b/src/crypto/fips_prf_cryptoapi.c
deleted file mode 100644
index dca93a3..0000000
--- a/src/crypto/fips_prf_cryptoapi.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * FIPS 186-2 PRF for Microsoft CryptoAPI
- * Copyright (c) 2009, 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.h"
-
-
-int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-{
-	/* FIX: how to do this with CryptoAPI? */
-	return -1;
-}
diff --git a/src/crypto/fips_prf_gnutls.c b/src/crypto/fips_prf_gnutls.c
deleted file mode 100644
index 947e6f6..0000000
--- a/src/crypto/fips_prf_gnutls.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * FIPS 186-2 PRF for libgcrypt
- * Copyright (c) 2004-2009, 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 <gcrypt.h>
-
-#include "common.h"
-#include "crypto.h"
-
-
-int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-{
-	/* FIX: how to do this with libgcrypt? */
-	return -1;
-}
diff --git a/src/crypto/fips_prf_nss.c b/src/crypto/fips_prf_nss.c
deleted file mode 100644
index 2c962f4..0000000
--- a/src/crypto/fips_prf_nss.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * FIPS 186-2 PRF for NSS
- * Copyright (c) 2009, 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 <openssl/sha.h>
-
-#include "common.h"
-#include "crypto.h"
-
-
-int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-{
-	return -1;
-}
diff --git a/src/crypto/milenage.c b/src/crypto/milenage.c
index a7f9c6a..6edea57 100644
--- a/src/crypto/milenage.c
+++ b/src/crypto/milenage.c
@@ -217,7 +217,7 @@
 	for (i = 0; i < 6; i++)
 		sqn[i] = auts[i] ^ ak[i];
 	if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
-	    memcmp(mac_s, auts + 6, 8) != 0)
+	    os_memcmp_const(mac_s, auts + 6, 8) != 0)
 		return -1;
 	return 0;
 }
@@ -312,7 +312,7 @@
 
 	wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
 
-	if (os_memcmp(mac_a, autn + 8, 8) != 0) {
+	if (os_memcmp_const(mac_a, autn + 8, 8) != 0) {
 		wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch");
 		wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A",
 			    autn + 8, 8);
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index b2bbab2..49a5c1c 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -58,6 +58,7 @@
 				WPA_PUT_LE16(ucs2_buffer + j,
 					     ((c & 0xF) << 12) |
 					     ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+				j += 2;
 			}
 		}
 	}
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index 10bf153..24bc3ff 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -19,6 +19,7 @@
 void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
 
 
+#ifdef CONFIG_CRYPTO_INTERNAL
 /**
  * sha1_vector - SHA-1 hash for data vector
  * @num_elem: Number of elements in the data vector
@@ -38,6 +39,7 @@
 	SHA1Final(mac, &ctx);
 	return 0;
 }
+#endif /* CONFIG_CRYPTO_INTERNAL */
 
 
 /* ===== start - public domain SHA1 implementation ===== */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 2fdaa02..65e0f79 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -40,7 +40,8 @@
 	TLS_FAIL_SUBJECT_MISMATCH = 5,
 	TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
 	TLS_FAIL_BAD_CERTIFICATE = 7,
-	TLS_FAIL_SERVER_CHAIN_PROBE = 8
+	TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9
 };
 
 union tls_event_data {
@@ -84,6 +85,8 @@
 #define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
 #define TLS_CONN_REQUEST_OCSP BIT(3)
 #define TLS_CONN_REQUIRE_OCSP BIT(4)
+#define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
+#define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -96,6 +99,8 @@
  * %NULL to allow all subjects
  * @altsubject_match: String to match in the alternative subject of the peer
  * certificate or %NULL to allow all alternative subjects
+ * @suffix_match: String to suffix match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names
  * @client_cert: File or reference name for client X.509 certificate in PEM or
  * DER format
  * @client_cert_blob: client_cert as inlined data or %NULL if not used
@@ -137,6 +142,7 @@
 	const char *ca_path;
 	const char *subject_match;
 	const char *altsubject_match;
+	const char *suffix_match;
 	const char *client_cert;
 	const u8 *client_cert_blob;
 	size_t client_cert_blob_len;
@@ -531,4 +537,19 @@
 	void *tls_ctx, struct tls_connection *conn,
 	tls_session_ticket_cb cb, void *ctx);
 
+void tls_connection_set_log_cb(struct tls_connection *conn,
+			       void (*log_cb)(void *ctx, const char *msg),
+			       void *ctx);
+
+#define TLS_BREAK_VERIFY_DATA BIT(0)
+#define TLS_BREAK_SRV_KEY_X_HASH BIT(1)
+#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2)
+#define TLS_DHE_PRIME_511B BIT(3)
+#define TLS_DHE_PRIME_767B BIT(4)
+#define TLS_DHE_PRIME_15 BIT(5)
+#define TLS_DHE_PRIME_58B BIT(6)
+#define TLS_DHE_NON_PRIME BIT(7)
+
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index a5d72f4..cb23eb9 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -125,8 +125,6 @@
 }
 
 
-extern int wpa_debug_show_keys;
-
 void * tls_init(const struct tls_config *conf)
 {
 	struct tls_global *global;
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 91f0690..6563ed2 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -28,6 +28,7 @@
 struct tls_connection {
 	struct tlsv1_client *client;
 	struct tlsv1_server *server;
+	struct tls_global *global;
 };
 
 
@@ -85,6 +86,7 @@
 	conn = os_zalloc(sizeof(*conn));
 	if (conn == NULL)
 		return NULL;
+	conn->global = global;
 
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
 	if (!global->server) {
@@ -109,6 +111,28 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags)
+{
+	if (conn->server)
+		tlsv1_server_set_test_flags(conn->server, flags);
+}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+			       void (*log_cb)(void *ctx, const char *msg),
+			       void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		tlsv1_server_set_log_cb(conn->server, log_cb, ctx);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
 void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
 {
 	if (conn == NULL)
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index d27af0a..d2d6600 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -48,23 +48,6 @@
 #endif
 #endif
 
-#ifdef ANDROID
-#include <openssl/pem.h>
-#include <keystore/keystore_get.h>
-
-static BIO * BIO_from_keystore(const char *key)
-{
-    BIO *bio = NULL;
-    uint8_t *value = NULL;
-    int length = keystore_get(key, strlen(key), &value);
-    if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) {
-        BIO_write(bio, value, length);
-    }
-    free(value);
-    return bio;
-}
-#endif /* ANDROID */
-
 #ifdef SSL_set_tlsext_status_type
 #ifndef OPENSSL_NO_TLSEXT
 #define HAVE_OCSP
@@ -72,6 +55,22 @@
 #endif /* OPENSSL_NO_TLSEXT */
 #endif /* SSL_set_tlsext_status_type */
 
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include <keystore/keystore_get.h>
+
+static BIO * BIO_from_keystore(const char *key)
+{
+	BIO *bio = NULL;
+	uint8_t *value = NULL;
+	int length = keystore_get(key, strlen(key), &value);
+	if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+		BIO_write(bio, value, length);
+	free(value);
+	return bio;
+}
+#endif /* ANDROID */
+
 static int tls_openssl_ref_count = 0;
 
 struct tls_context {
@@ -93,7 +92,7 @@
 	ENGINE *engine;        /* functional reference to the engine */
 	EVP_PKEY *private_key; /* the private key if using engine */
 #endif /* OPENSSL_NO_ENGINE */
-	char *subject_match, *altsubject_match;
+	char *subject_match, *altsubject_match, *suffix_match;
 	int read_alerts, write_alerts, failed;
 
 	tls_session_ticket_cb session_ticket_cb;
@@ -106,6 +105,7 @@
 	unsigned int ca_cert_verify:1;
 	unsigned int cert_probe:1;
 	unsigned int server_cert_only:1;
+	unsigned int invalid_hb_used:1;
 
 	u8 srv_cert_hash[32];
 
@@ -113,6 +113,7 @@
 
 	X509 *peer_cert;
 	X509 *peer_issuer;
+	X509 *peer_issuer_issuer;
 };
 
 
@@ -785,12 +786,13 @@
 		PKCS12_PBE_add();
 #endif  /* PKCS12_FUNCS */
 	} else {
-		context = tls_global;
 #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
 		/* Newer OpenSSL can store app-data per-SSL */
 		context = tls_context_new(conf);
 		if (context == NULL)
 			return NULL;
+#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
+		context = tls_global;
 #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 	}
 	tls_openssl_ref_count++;
@@ -798,11 +800,13 @@
 	ssl = SSL_CTX_new(TLSv1_method());
 	if (ssl == NULL) {
 		tls_openssl_ref_count--;
+#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
+		if (context != tls_global)
+			os_free(context);
+#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 		if (tls_openssl_ref_count == 0) {
 			os_free(tls_global);
 			tls_global = NULL;
-		} else if (context != tls_global) {
-			os_free(context);
 		}
 		return NULL;
 	}
@@ -981,14 +985,35 @@
 	return count;
 }
 
+
+static void tls_msg_cb(int write_p, int version, int content_type,
+		       const void *buf, size_t len, SSL *ssl, void *arg)
+{
+	struct tls_connection *conn = arg;
+	const u8 *pos = buf;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
+		   write_p ? "TX" : "RX", version, content_type);
+	wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
+	if (content_type == 24 && len >= 3 && pos[0] == 1) {
+		size_t payload_len = WPA_GET_BE16(pos + 1);
+		if (payload_len + 3 > len) {
+			wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected");
+			conn->invalid_hb_used = 1;
+		}
+	}
+}
+
+
 struct tls_connection * tls_connection_init(void *ssl_ctx)
 {
 	SSL_CTX *ssl = ssl_ctx;
 	struct tls_connection *conn;
 	long options;
-	struct tls_context *context = tls_global;
 #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
-	context = SSL_CTX_get_app_data(ssl);
+	struct tls_context *context = SSL_CTX_get_app_data(ssl);
+#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
+	struct tls_context *context = tls_global;
 #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 
 	conn = os_zalloc(sizeof(*conn));
@@ -1004,16 +1029,13 @@
 
 	conn->context = context;
 	SSL_set_app_data(conn->ssl, conn);
+	SSL_set_msg_callback(conn->ssl, tls_msg_cb);
+	SSL_set_msg_callback_arg(conn->ssl, conn);
 	options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
 		SSL_OP_SINGLE_DH_USE;
 #ifdef SSL_OP_NO_COMPRESSION
 	options |= SSL_OP_NO_COMPRESSION;
 #endif /* SSL_OP_NO_COMPRESSION */
-#ifdef ANDROID
-	options |= SSL_OP_NO_TLSv1_1;
-	options |= SSL_OP_NO_TLSv1_2;
-	options |= SSL_OP_NO_TICKET;
-#endif /* ANDROID */
 	SSL_set_options(conn->ssl, options);
 
 	conn->ssl_in = BIO_new(BIO_s_mem());
@@ -1049,6 +1071,7 @@
 	tls_engine_deinit(conn);
 	os_free(conn->subject_match);
 	os_free(conn->altsubject_match);
+	os_free(conn->suffix_match);
 	os_free(conn->session_ticket);
 	os_free(conn);
 }
@@ -1139,6 +1162,104 @@
 }
 
 
+#ifndef CONFIG_NATIVE_WINDOWS
+static int domain_suffix_match(const u8 *val, size_t len, const char *match)
+{
+	size_t i, match_len;
+
+	/* Check for embedded nuls that could mess up suffix matching */
+	for (i = 0; i < len; i++) {
+		if (val[i] == '\0') {
+			wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject");
+			return 0;
+		}
+	}
+
+	match_len = os_strlen(match);
+	if (match_len > len)
+		return 0;
+
+	if (os_strncasecmp((const char *) val + len - match_len, match,
+			   match_len) != 0)
+		return 0; /* no match */
+
+	if (match_len == len)
+		return 1; /* exact match */
+
+	if (val[len - match_len - 1] == '.')
+		return 1; /* full label match completes suffix match */
+
+	wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+	return 0;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static int tls_match_suffix(X509 *cert, const char *match)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	/* wincrypt.h has conflicting X509_NAME definition */
+	return -1;
+#else /* CONFIG_NATIVE_WINDOWS */
+	GENERAL_NAME *gen;
+	void *ext;
+	int i;
+	int dns_name = 0;
+	X509_NAME *name;
+
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match);
+
+	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+		gen = sk_GENERAL_NAME_value(ext, i);
+		if (gen->type != GEN_DNS)
+			continue;
+		dns_name++;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+				  gen->d.dNSName->data,
+				  gen->d.dNSName->length);
+		if (domain_suffix_match(gen->d.dNSName->data,
+					gen->d.dNSName->length, match) == 1) {
+			wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found");
+			return 1;
+		}
+	}
+
+	if (dns_name) {
+		wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+		return 0;
+	}
+
+	name = X509_get_subject_name(cert);
+	i = -1;
+	for (;;) {
+		X509_NAME_ENTRY *e;
+		ASN1_STRING *cn;
+
+		i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
+		if (i == -1)
+			break;
+		e = X509_NAME_get_entry(name, i);
+		if (e == NULL)
+			continue;
+		cn = X509_NAME_ENTRY_get_data(e);
+		if (cn == NULL)
+			continue;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+				  cn->data, cn->length);
+		if (domain_suffix_match(cn->data, cn->length, match) == 1) {
+			wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found");
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found");
+	return 0;
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
 static enum tls_fail_reason openssl_tls_fail_reason(int err)
 {
 	switch (err) {
@@ -1267,10 +1388,13 @@
 	SSL *ssl;
 	struct tls_connection *conn;
 	struct tls_context *context;
-	char *match, *altmatch;
+	char *match, *altmatch, *suffix_match;
 	const char *err_str;
 
 	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+	if (!err_cert)
+		return 0;
+
 	err = X509_STORE_CTX_get_error(x509_ctx);
 	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
 	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
@@ -1285,10 +1409,13 @@
 		conn->peer_cert = err_cert;
 	else if (depth == 1)
 		conn->peer_issuer = err_cert;
+	else if (depth == 2)
+		conn->peer_issuer_issuer = err_cert;
 
 	context = conn->context;
 	match = conn->subject_match;
 	altmatch = conn->altsubject_match;
+	suffix_match = conn->suffix_match;
 
 	if (!preverify_ok && !conn->ca_cert_verify)
 		preverify_ok = 1;
@@ -1357,6 +1484,14 @@
 		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
 				       "AltSubject mismatch",
 				       TLS_FAIL_ALTSUBJECT_MISMATCH);
+	} else if (depth == 0 && suffix_match &&
+		   !tls_match_suffix(err_cert, suffix_match)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
+			   suffix_match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain suffix mismatch",
+				       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
 	} else
 		openssl_tls_cert_event(conn, err_cert, depth, buf);
 
@@ -1619,7 +1754,8 @@
 
 static int tls_connection_set_subject_match(struct tls_connection *conn,
 					    const char *subject_match,
-					    const char *altsubject_match)
+					    const char *altsubject_match,
+					    const char *suffix_match)
 {
 	os_free(conn->subject_match);
 	conn->subject_match = NULL;
@@ -1637,6 +1773,14 @@
 			return -1;
 	}
 
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (suffix_match) {
+		conn->suffix_match = os_strdup(suffix_match);
+		if (conn->suffix_match == NULL)
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -2521,10 +2665,25 @@
 	out_data = openssl_handshake(conn, in_data, server);
 	if (out_data == NULL)
 		return NULL;
+	if (conn->invalid_hb_used) {
+		wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+		wpabuf_free(out_data);
+		return NULL;
+	}
 
 	if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
 		*appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
 
+	if (conn->invalid_hb_used) {
+		wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+		if (appl_data) {
+			wpabuf_free(*appl_data);
+			*appl_data = NULL;
+		}
+		wpabuf_free(out_data);
+		return NULL;
+	}
+
 	return out_data;
 }
 
@@ -2626,6 +2785,12 @@
 	}
 	wpabuf_put(buf, res);
 
+	if (conn->invalid_hb_used) {
+		wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+		wpabuf_free(buf);
+		return NULL;
+	}
+
 	return buf;
 }
 
@@ -2772,7 +2937,6 @@
 static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
 {
 #ifndef CONFIG_NO_STDOUT_DEBUG
-	extern int wpa_debug_level;
 	BIO *out;
 	size_t rlen;
 	char *txt;
@@ -2804,6 +2968,41 @@
 }
 
 
+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;
@@ -2813,6 +3012,8 @@
 	OCSP_BASICRESP *basic;
 	OCSP_CERTID *id;
 	ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
+	X509_STORE *store;
+	STACK_OF(X509) *certs = NULL;
 
 	len = SSL_get_tlsext_status_ocsp_resp(s, &p);
 	if (!p) {
@@ -2843,8 +3044,40 @@
 		return 0;
 	}
 
-	status = OCSP_basic_verify(basic, NULL, SSL_CTX_get_cert_store(s->ctx),
-				   0);
+	store = SSL_CTX_get_cert_store(s->ctx);
+	if (conn->peer_issuer) {
+		debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
+
+		if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to certificate store\n");
+		}
+		certs = sk_X509_new_null();
+		if (certs) {
+			X509 *cert;
+			cert = X509_dup(conn->peer_issuer);
+			if (cert && !sk_X509_push(certs, cert)) {
+				tls_show_errors(
+					MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+				X509_free(cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (conn->peer_issuer_issuer) {
+				cert = X509_dup(conn->peer_issuer_issuer);
+				if (cert && !sk_X509_push(certs, cert)) {
+					tls_show_errors(
+						MSG_INFO, __func__,
+						"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+					X509_free(cert);
+				}
+			}
+		}
+	}
+
+	status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
+	sk_X509_pop_free(certs, X509_free);
 	if (status <= 0) {
 		tls_show_errors(MSG_INFO, __func__,
 				"OpenSSL: OCSP response failed verification");
@@ -2855,8 +3088,15 @@
 
 	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
 
-	if (!conn->peer_cert || !conn->peer_issuer) {
-		wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate or issue certificate not available for OCSP status check");
+	if (!conn->peer_cert) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return 0;
+	}
+
+	if (!conn->peer_issuer) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
 		OCSP_BASICRESP_free(basic);
 		OCSP_RESPONSE_free(rsp);
 		return 0;
@@ -2902,7 +3142,7 @@
 		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
 		return 0;
 	}
-		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
 	return 1;
 }
 
@@ -2947,7 +3187,6 @@
 {
 	int ret;
 	unsigned long err;
-	SSL_CTX *ssl_ctx = tls_ctx;
 
 	if (conn == NULL)
 		return -1;
@@ -2967,7 +3206,8 @@
 	}
 	if (tls_connection_set_subject_match(conn,
 					     params->subject_match,
-					     params->altsubject_match))
+					     params->altsubject_match,
+					     params->suffix_match))
 		return -1;
 
 	if (params->engine && params->ca_cert_id) {
@@ -3017,8 +3257,22 @@
 #endif /* SSL_clear_options */
 #endif /*  SSL_OP_NO_TICKET */
 
+#ifdef SSL_OP_NO_TLSv1_1
+	if (params->flags & TLS_CONN_DISABLE_TLSv1_1)
+		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1);
+	else
+		SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1);
+#endif /* SSL_OP_NO_TLSv1_1 */
+#ifdef SSL_OP_NO_TLSv1_2
+	if (params->flags & TLS_CONN_DISABLE_TLSv1_2)
+		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2);
+	else
+		SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2);
+#endif /* SSL_OP_NO_TLSv1_2 */
+
 #ifdef HAVE_OCSP
 	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		SSL_CTX *ssl_ctx = tls_ctx;
 		SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
 		SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
 		SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
diff --git a/src/drivers/Makefile b/src/drivers/Makefile
index 07600e5..5721154 100644
--- a/src/drivers/Makefile
+++ b/src/drivers/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 	rm -f build.wpa_supplicant build.hostapd
 
 install:
diff --git a/src/drivers/android_drv.h b/src/drivers/android_drv.h
index 5906527..31d9440 100644
--- a/src/drivers/android_drv.h
+++ b/src/drivers/android_drv.h
@@ -1,12 +1,8 @@
 /*
  * Android driver interface
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef ANDROID_DRV_H
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index b8c9829..352c163 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1,6 +1,6 @@
 /*
  * Driver interface definition
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -37,6 +37,27 @@
 #define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300
 #define HOSTAPD_CHAN_DFS_MASK 0x00000300
 
+#define HOSTAPD_CHAN_VHT_10_70 0x00000800
+#define HOSTAPD_CHAN_VHT_30_50 0x00001000
+#define HOSTAPD_CHAN_VHT_50_30 0x00002000
+#define HOSTAPD_CHAN_VHT_70_10 0x00004000
+
+enum reg_change_initiator {
+	REGDOM_SET_BY_CORE,
+	REGDOM_SET_BY_USER,
+	REGDOM_SET_BY_DRIVER,
+	REGDOM_SET_BY_COUNTRY_IE,
+	REGDOM_BEACON_HINT,
+};
+
+enum reg_type {
+	REGDOM_TYPE_UNKNOWN,
+	REGDOM_TYPE_COUNTRY,
+	REGDOM_TYPE_WORLD,
+	REGDOM_TYPE_CUSTOM_WORLD,
+	REGDOM_TYPE_INTERSECTION,
+};
+
 /**
  * struct hostapd_channel_data - Channel information
  */
@@ -57,7 +78,7 @@
 	int flag;
 
 	/**
-	 * max_tx_power - Maximum transmit power in dBm
+	 * max_tx_power - Regulatory transmit power limit in dBm
 	 */
 	u8 max_tx_power;
 
@@ -80,6 +101,9 @@
 	 */
 	long double interference_factor;
 #endif /* CONFIG_ACS */
+
+	/* DFS CAC time in milliseconds */
+	unsigned int dfs_cac_ms;
 };
 
 #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
@@ -221,7 +245,7 @@
 struct wpa_scan_results {
 	struct wpa_scan_res **res;
 	size_t num;
-	struct os_time fetch_time;
+	struct os_reltime fetch_time;
 };
 
 /**
@@ -327,7 +351,30 @@
 	 * Mbps from the support rates element(s) in the Probe Request frames
 	 * and not to transmit the frames at any of those rates.
 	 */
-	u8 p2p_probe;
+	unsigned int p2p_probe:1;
+
+	/**
+	 * only_new_results - Request driver to report only new results
+	 *
+	 * This is used to request the driver to report only BSSes that have
+	 * been detected after this scan request has been started, i.e., to
+	 * flush old cached BSS entries.
+	 */
+	unsigned int only_new_results:1;
+
+	/**
+	 * low_priority - Requests driver to use a lower scan priority
+	 *
+	 * This is used to request the driver to use a lower scan priority
+	 * if it supports such a thing.
+	 */
+	unsigned int low_priority:1;
+
+	/*
+	 * NOTE: Whenever adding new parameters here, please make sure
+	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+	 * matching changes.
+	 */
 };
 
 /**
@@ -376,6 +423,16 @@
 	const u8 *bssid;
 
 	/**
+	 * bssid_hint - BSSID of a proposed AP
+	 *
+	 * This indicates which BSS has been found a suitable candidate for
+	 * initial association for drivers that use driver/firmwate-based BSS
+	 * selection. Unlike the @bssid parameter, @bssid_hint does not limit
+	 * the driver from selecting other BSSes in the ESS.
+	 */
+	const u8 *bssid_hint;
+
+	/**
 	 * ssid - The selected SSID
 	 */
 	const u8 *ssid;
@@ -393,6 +450,16 @@
 	int freq;
 
 	/**
+	 * freq_hint - Frequency of the channel the proposed AP is using
+	 *
+	 * This provides a channel on which a suitable BSS has been found as a
+	 * hint for the driver. Unlike the @freq parameter, @freq_hint does not
+	 * limit the driver from selecting other channels for
+	 * driver/firmware-based BSS selection.
+	 */
+	int freq_hint;
+
+	/**
 	 * bg_scan_period - Background scan period in seconds, 0 to disable
 	 * background scan, or -1 to indicate no change to default driver
 	 * configuration
@@ -400,6 +467,11 @@
 	int bg_scan_period;
 
 	/**
+	 * beacon_int - Beacon interval for IBSS or 0 to use driver default
+	 */
+	int beacon_int;
+
+	/**
 	 * wpa_ie - WPA information element for (Re)Association Request
 	 * WPA information element to be included in (Re)Association
 	 * Request (including information element id and length). Use
@@ -430,25 +502,25 @@
 	unsigned int wpa_proto;
 
 	/**
-	 * pairwise_suite - Selected pairwise cipher suite
+	 * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
 	 *
 	 * This is usually ignored if @wpa_ie is used.
 	 */
-	enum wpa_cipher pairwise_suite;
+	unsigned int pairwise_suite;
 
 	/**
-	 * group_suite - Selected group cipher suite
+	 * group_suite - Selected group cipher suite (WPA_CIPHER_*)
 	 *
 	 * This is usually ignored if @wpa_ie is used.
 	 */
-	enum wpa_cipher group_suite;
+	unsigned int group_suite;
 
 	/**
-	 * key_mgmt_suite - Selected key management suite
+	 * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
 	 *
 	 * This is usually ignored if @wpa_ie is used.
 	 */
-	enum wpa_key_mgmt key_mgmt_suite;
+	unsigned int key_mgmt_suite;
 
 	/**
 	 * auth_alg - Allowed authentication algorithms
@@ -618,11 +690,21 @@
 	HIDDEN_SSID_ZERO_CONTENTS
 };
 
+struct wowlan_triggers {
+	u8 any;
+	u8 disconnect;
+	u8 magic_pkt;
+	u8 gtk_rekey_failure;
+	u8 eap_identity_req;
+	u8 four_way_handshake;
+	u8 rfkill_release;
+};
+
 struct wpa_driver_ap_params {
 	/**
 	 * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
 	 */
-	const u8 *head;
+	u8 *head;
 
 	/**
 	 * head_len - Length of the head buffer in octets
@@ -632,7 +714,7 @@
 	/**
 	 * tail - Beacon tail following TIM IE
 	 */
-	const u8 *tail;
+	u8 *tail;
 
 	/**
 	 * tail_len - Length of the tail buffer in octets
@@ -663,7 +745,7 @@
 	 * This is used by drivers that reply to Probe Requests internally in
 	 * AP mode and require the full Probe Response template.
 	 */
-	const u8 *proberesp;
+	u8 *proberesp;
 
 	/**
 	 * proberesp_len - Length of the proberesp buffer in octets
@@ -799,6 +881,16 @@
 	 * disable_dgaf - Whether group-addressed frames are disabled
 	 */
 	int disable_dgaf;
+
+	/**
+	 * osen - Whether OSEN security is enabled
+	 */
+	int osen;
+
+	/**
+	 * freq - Channel parameters for dynamic bandwidth changes
+	 */
+	struct hostapd_freq_params *freq;
 };
 
 /**
@@ -821,6 +913,13 @@
 #define WPA_DRIVER_CAPA_ENC_CCMP	0x00000008
 #define WPA_DRIVER_CAPA_ENC_WEP128	0x00000010
 #define WPA_DRIVER_CAPA_ENC_GCMP	0x00000020
+#define WPA_DRIVER_CAPA_ENC_GCMP_256	0x00000040
+#define WPA_DRIVER_CAPA_ENC_CCMP_256	0x00000080
+#define WPA_DRIVER_CAPA_ENC_BIP		0x00000100
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128	0x00000200
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256	0x00000400
+#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256	0x00000800
+#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED	0x00001000
 	unsigned int enc;
 
 #define WPA_DRIVER_AUTH_OPEN		0x00000001
@@ -832,7 +931,8 @@
 #define WPA_DRIVER_FLAGS_DRIVER_IE	0x00000001
 /* Driver needs static WEP key setup after association command */
 #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
-/* unused: 0x00000004 */
+/* Driver takes care of all DFS operations */
+#define WPA_DRIVER_FLAGS_DFS_OFFLOAD			0x00000004
 /* Driver takes care of RSN 4-way handshake internally; PMK is configured with
  * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
 #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
@@ -844,8 +944,8 @@
 #define WPA_DRIVER_FLAGS_AP		0x00000040
 /* Driver needs static WEP key setup after association has been completed */
 #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE	0x00000080
-/* Driver takes care of P2P management operations */
-#define WPA_DRIVER_FLAGS_P2P_MGMT	0x00000100
+/* Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
+#define WPA_DRIVER_FLAGS_HT_2040_COEX			0x00000100
 /* Driver supports concurrent P2P operations */
 #define WPA_DRIVER_FLAGS_P2P_CONCURRENT	0x00000200
 /*
@@ -855,7 +955,8 @@
 #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE	0x00000400
 /* This interface is P2P capable (P2P GO or P2P Client) */
 #define WPA_DRIVER_FLAGS_P2P_CAPABLE	0x00000800
-/* unused: 0x00001000 */
+/* Driver supports station and key removal when stopping an AP */
+#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT		0x00001000
 /*
  * Driver uses the initial interface for P2P management interface and non-P2P
  * purposes (e.g., connect to infra AP), but this interface cannot be used for
@@ -898,6 +999,10 @@
 #define WPA_DRIVER_FLAGS_RADAR				0x10000000
 /* Driver supports a dedicated interface for P2P Device */
 #define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE		0x20000000
+/* Driver supports QoS Mapping */
+#define WPA_DRIVER_FLAGS_QOS_MAPPING			0x40000000
+/* Driver supports CSA in AP mode */
+#define WPA_DRIVER_FLAGS_AP_CSA				0x80000000
 	unsigned int flags;
 
 	int max_scan_ssids;
@@ -945,6 +1050,8 @@
 	 */
 	const u8 *extended_capa, *extended_capa_mask;
 	unsigned int extended_capa_len;
+
+	struct wowlan_triggers wowlan_triggers;
 };
 
 
@@ -971,11 +1078,17 @@
 	u16 listen_interval;
 	const struct ieee80211_ht_capabilities *ht_capabilities;
 	const struct ieee80211_vht_capabilities *vht_capabilities;
+	int vht_opmode_enabled;
+	u8 vht_opmode;
 	u32 flags; /* bitmask of WPA_STA_* flags */
 	int set; /* Set STA parameters instead of add */
 	u8 qosinfo;
 	const u8 *ext_capab;
 	size_t ext_capab_len;
+	const u8 *supp_channels;
+	size_t supp_channels_len;
+	const u8 *supp_oper_classes;
+	size_t supp_oper_classes_len;
 };
 
 struct hostapd_freq_params {
@@ -1087,17 +1200,6 @@
 #define WPA_STA_MFP BIT(3)
 #define WPA_STA_TDLS_PEER BIT(4)
 
-/**
- * struct p2p_params - P2P parameters for driver-based P2P management
- */
-struct p2p_params {
-	const char *dev_name;
-	u8 pri_dev_type[8];
-#define DRV_MAX_SEC_DEV_TYPES 5
-	u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8];
-	size_t num_sec_dev_types;
-};
-
 enum tdls_oper {
 	TDLS_DISCOVERY_REQ,
 	TDLS_SETUP,
@@ -1152,6 +1254,75 @@
 };
 
 /**
+ * struct beacon_data - Beacon data
+ * @head: Head portion of Beacon frame (before TIM IE)
+ * @tail: Tail portion of Beacon frame (after TIM IE)
+ * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL
+ * @proberesp_ies: Extra information element(s) to add into Probe Response
+ *	frames or %NULL
+ * @assocresp_ies: Extra information element(s) to add into (Re)Association
+ *	Response frames or %NULL
+ * @probe_resp: Probe Response frame template
+ * @head_len: Length of @head
+ * @tail_len: Length of @tail
+ * @beacon_ies_len: Length of beacon_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @probe_resp_len: Length of probe response template (@probe_resp)
+ */
+struct beacon_data {
+	u8 *head, *tail;
+	u8 *beacon_ies;
+	u8 *proberesp_ies;
+	u8 *assocresp_ies;
+	u8 *probe_resp;
+
+	size_t head_len, tail_len;
+	size_t beacon_ies_len;
+	size_t proberesp_ies_len;
+	size_t assocresp_ies_len;
+	size_t probe_resp_len;
+};
+
+/**
+ * struct csa_settings - Settings for channel switch command
+ * @cs_count: Count in Beacon frames (TBTT) to perform the switch
+ * @block_tx: 1 - block transmission for CSA period
+ * @freq_params: Next channel frequency parameter
+ * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period
+ * @beacon_after: Next beacon/probe resp/asooc resp info
+ * @counter_offset_beacon: Offset to the count field in beacon's tail
+ * @counter_offset_presp: Offset to the count field in probe resp.
+ */
+struct csa_settings {
+	u8 cs_count;
+	u8 block_tx;
+
+	struct hostapd_freq_params freq_params;
+	struct beacon_data beacon_csa;
+	struct beacon_data beacon_after;
+
+	u16 counter_offset_beacon;
+	u16 counter_offset_presp;
+};
+
+/* TDLS peer capabilities for send_tdls_mgmt() */
+enum tdls_peer_capability {
+	TDLS_PEER_HT = BIT(0),
+	TDLS_PEER_VHT = BIT(1),
+	TDLS_PEER_WMM = BIT(2),
+};
+
+#ifdef CONFIG_MACSEC
+struct macsec_init_params {
+	Boolean always_include_sci;
+	Boolean use_es;
+	Boolean use_scb;
+};
+#endif /* CONFIG_MACSEC */
+
+
+/**
  * struct wpa_driver_ops - Driver interface API definition
  *
  * This structure defines the API that each driver interface needs to implement
@@ -1200,7 +1371,9 @@
 	 * @priv: private driver interface data
 	 * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
 	 *	%WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK,
-	 *	%WPA_ALG_GCMP);
+	 *	%WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256,
+	 *	%WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+	 *	%WPA_ALG_BIP_CMAC_256);
 	 *	%WPA_ALG_NONE clears the key.
 	 * @addr: Address of the peer STA (BSSID of the current AP when setting
 	 *	pairwise key in station mode), ff:ff:ff:ff:ff:ff for
@@ -1565,6 +1738,14 @@
 	int (*set_country)(void *priv, const char *alpha2);
 
 	/**
+	 * get_country - Get country
+	 * @priv: Private driver interface data
+	 * @alpha2: Buffer for returning country code (at least 3 octets)
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*get_country)(void *priv, char *alpha2);
+
+	/**
 	 * global_init - Global driver initialization
 	 * Returns: Pointer to private data (global), %NULL on failure
 	 *
@@ -1950,12 +2131,13 @@
 	 *	(this may differ from the requested addr if the driver cannot
 	 *	change interface address)
 	 * @bridge: Bridge interface to use or %NULL if no bridge configured
+	 * @use_existing: Whether to allow existing interface to be used
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*if_add)(void *priv, enum wpa_driver_if_type type,
 		      const char *ifname, const u8 *addr, void *bss_ctx,
 		      void **drv_priv, char *force_ifname, u8 *if_addr,
-		      const char *bridge);
+		      const char *bridge, int use_existing);
 
 	/**
 	 * if_remove - Remove a virtual interface
@@ -2017,7 +2199,7 @@
 	 * @session_timeout: Session timeout for the station
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, 
+	int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted,
 				   u32 session_timeout);
 
 	/**
@@ -2081,7 +2263,7 @@
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
-	                   const char *bridge_ifname, char *ifname_wds);
+			   const char *bridge_ifname, char *ifname_wds);
 
 	/**
 	 * send_action - Transmit an Action frame
@@ -2132,7 +2314,7 @@
 	 *
 	 * This command is used to request the driver to remain awake on the
 	 * specified channel for the specified duration and report received
-	 * Action frames with EVENT_RX_ACTION events. Optionally, received
+	 * Action frames with EVENT_RX_MGMT events. Optionally, received
 	 * Probe Request frames may also be requested to be reported by calling
 	 * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
 	 *
@@ -2314,228 +2496,13 @@
 	const char * (*get_radio_name)(void *priv);
 
 	/**
-	 * p2p_find - Start P2P Device Discovery
-	 * @priv: Private driver interface data
-	 * @timeout: Timeout for find operation in seconds or 0 for no timeout
-	 * @type: Device Discovery type (enum p2p_discovery_type)
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_find)(void *priv, unsigned int timeout, int type);
-
-	/**
-	 * p2p_stop_find - Stop P2P Device Discovery
-	 * @priv: Private driver interface data
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_stop_find)(void *priv);
-
-	/**
-	 * p2p_listen - Start P2P Listen state for specified duration
-	 * @priv: Private driver interface data
-	 * @timeout: Listen state duration in milliseconds
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function can be used to request the P2P module to keep the
-	 * device discoverable on the listen channel for an extended set of
-	 * time. At least in its current form, this is mainly used for testing
-	 * purposes and may not be of much use for normal P2P operations.
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_listen)(void *priv, unsigned int timeout);
-
-	/**
-	 * p2p_connect - Start P2P group formation (GO negotiation)
-	 * @priv: Private driver interface data
-	 * @peer_addr: MAC address of the peer P2P client
-	 * @wps_method: enum p2p_wps_method value indicating config method
-	 * @go_intent: Local GO intent value (1..15)
-	 * @own_interface_addr: Intended interface address to use with the
-	 *	group
-	 * @force_freq: The only allowed channel frequency in MHz or 0
-	 * @persistent_group: Whether to create persistent group
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method,
-			   int go_intent, const u8 *own_interface_addr,
-			   unsigned int force_freq, int persistent_group);
-
-	/**
-	 * wps_success_cb - Report successfully completed WPS provisioning
-	 * @priv: Private driver interface data
-	 * @peer_addr: Peer address
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is used to report successfully completed WPS
-	 * provisioning during group formation in both GO/Registrar and
-	 * client/Enrollee roles.
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*wps_success_cb)(void *priv, const u8 *peer_addr);
-
-	/**
-	 * p2p_group_formation_failed - Report failed WPS provisioning
-	 * @priv: Private driver interface data
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is used to report failed group formation. This can
-	 * happen either due to failed WPS provisioning or due to 15 second
-	 * timeout during the provisioning phase.
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_group_formation_failed)(void *priv);
-
-	/**
-	 * p2p_set_params - Set P2P parameters
-	 * @priv: Private driver interface data
-	 * @params: P2P parameters
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_set_params)(void *priv, const struct p2p_params *params);
-
-	/**
-	 * p2p_prov_disc_req - Send Provision Discovery Request
-	 * @priv: Private driver interface data
-	 * @peer_addr: MAC address of the peer P2P client
-	 * @config_methods: WPS Config Methods value (only one bit set)
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function can be used to request a discovered P2P peer to
-	 * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared
-	 * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The
-	 * Provision Discovery Request frame is transmitted once immediately
-	 * and if no response is received, the frame will be sent again
-	 * whenever the target device is discovered during device dsicovery
-	 * (start with a p2p_find() call). Response from the peer is indicated
-	 * with the EVENT_P2P_PROV_DISC_RESPONSE event.
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr,
-				 u16 config_methods, int join);
-
-	/**
-	 * p2p_sd_request - Schedule a service discovery query
-	 * @priv: Private driver interface data
-	 * @dst: Destination peer or %NULL to apply for all peers
-	 * @tlvs: P2P Service Query TLV(s)
-	 * Returns: Reference to the query or 0 on failure
-	 *
-	 * Response to the query is indicated with the
-	 * EVENT_P2P_SD_RESPONSE driver event.
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	u64 (*p2p_sd_request)(void *priv, const u8 *dst,
-			      const struct wpabuf *tlvs);
-
-	/**
-	 * p2p_sd_cancel_request - Cancel a pending service discovery query
-	 * @priv: Private driver interface data
-	 * @req: Query reference from p2p_sd_request()
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_sd_cancel_request)(void *priv, u64 req);
-
-	/**
-	 * p2p_sd_response - Send response to a service discovery query
-	 * @priv: Private driver interface data
-	 * @freq: Frequency from EVENT_P2P_SD_REQUEST event
-	 * @dst: Destination address from EVENT_P2P_SD_REQUEST event
-	 * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event
-	 * @resp_tlvs: P2P Service Response TLV(s)
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function is called as a response to the request indicated with
-	 * the EVENT_P2P_SD_REQUEST driver event.
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_sd_response)(void *priv, int freq, const u8 *dst,
-			       u8 dialog_token,
-			       const struct wpabuf *resp_tlvs);
-
-	/**
-	 * p2p_service_update - Indicate a change in local services
-	 * @priv: Private driver interface data
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This function needs to be called whenever there is a change in
-	 * availability of the local services. This will increment the
-	 * Service Update Indicator value which will be used in SD Request and
-	 * Response frames.
-	 *
-	 * This function is only used if the driver implements P2P management,
-	 * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-	 * struct wpa_driver_capa.
-	 */
-	int (*p2p_service_update)(void *priv);
-
-	/**
-	 * p2p_reject - Reject peer device (explicitly block connections)
-	 * @priv: Private driver interface data
-	 * @addr: MAC address of the peer
-	 * Returns: 0 on success, -1 on failure
-	 */
-	int (*p2p_reject)(void *priv, const u8 *addr);
-
-	/**
-	 * p2p_invite - Invite a P2P Device into a group
-	 * @priv: Private driver interface data
-	 * @peer: Device Address of the peer P2P Device
-	 * @role: Local role in the group
-	 * @bssid: Group BSSID or %NULL if not known
-	 * @ssid: Group SSID
-	 * @ssid_len: Length of ssid in octets
-	 * @go_dev_addr: Forced GO Device Address or %NULL if none
-	 * @persistent_group: Whether this is to reinvoke a persistent group
-	 * Returns: 0 on success, -1 on failure
-	 */
-	int (*p2p_invite)(void *priv, const u8 *peer, int role,
-			  const u8 *bssid, const u8 *ssid, size_t ssid_len,
-			  const u8 *go_dev_addr, int persistent_group);
-
-	/**
 	 * send_tdls_mgmt - for sending TDLS management packets
 	 * @priv: private driver interface data
 	 * @dst: Destination (peer) MAC address
 	 * @action_code: TDLS action code for the mssage
 	 * @dialog_token: Dialog Token to use in the message (if needed)
 	 * @status_code: Status Code or Reason Code to use (if needed)
+	 * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield)
 	 * @buf: TDLS IEs to add to the message
 	 * @len: Length of buf in octets
 	 * Returns: 0 on success, negative (<0) on failure
@@ -2544,7 +2511,7 @@
 	 * responsible for receiving and sending all TDLS packets.
 	 */
 	int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
-			      u8 dialog_token, u16 status_code,
+			      u8 dialog_token, u16 status_code, u32 peer_capab,
 			      const u8 *buf, size_t len);
 
 	/**
@@ -2572,10 +2539,26 @@
 			u8 *buf, u16 *buf_len);
 
 	/**
+	 * set_qos_map - Set QoS Map
+	 * @priv: Private driver interface data
+	 * @qos_map_set: QoS Map
+	 * @qos_map_set_len: Length of QoS Map
+	 */
+	int (*set_qos_map)(void *priv, const u8 *qos_map_set,
+			   u8 qos_map_set_len);
+
+	/**
+	 * set_wowlan - Set wake-on-wireless triggers
+	 * @priv: Private driver interface data
+	 * @triggers: wowlan triggers
+	 */
+	int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
+
+	/**
 	 * signal_poll - Get current connection information
 	 * @priv: Private driver interface data
 	 * @signal_info: Connection info structure
-         */
+	 */
 	int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
 
 	/**
@@ -2591,18 +2574,43 @@
 	 * DEPRECATED - use set_ap() instead
 	 */
 	int (*set_authmode)(void *priv, int authmode);
+
 #ifdef ANDROID
 	/**
-	 * driver_cmd - execute driver-specific command
-	 * @priv: private driver interface data
-	 * @cmd: command to execute
-	 * @buf: return buffer
-	 * @buf_len: buffer length
-	 *
+	 * driver_cmd - Execute driver-specific command
+	 * @priv: Private driver interface data
+	 * @cmd: Command to execute
+	 * @buf: Return buffer
+	 * @buf_len: Buffer length
 	 * Returns: 0 on success, -1 on failure
 	 */
-	 int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
-#endif
+	int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
+#endif /* ANDROID */
+
+	/**
+	 * vendor_cmd - Execute vendor specific command
+	 * @priv: Private driver interface data
+	 * @vendor_id: Vendor id
+	 * @subcmd: Vendor command id
+	 * @data: Vendor command parameters (%NULL if no parameters)
+	 * @data_len: Data length
+	 * @buf: Return buffer (%NULL to ignore reply)
+	 * Returns: 0 on success, negative (<0) on failure
+	 *
+	 * This function handles vendor specific commands that are passed to
+	 * the driver/device. The command is identified by vendor id and
+	 * command id. Parameters can be passed as argument to the command
+	 * in the data buffer. Reply (if any) will be filled in the supplied
+	 * return buffer.
+	 *
+	 * The exact driver behavior is driver interface and vendor specific. As
+	 * an example, this will be converted to a vendor specific cfg80211
+	 * command in case of the nl80211 driver interface.
+	 */
+	int (*vendor_cmd)(void *priv, unsigned int vendor_id,
+			  unsigned int subcmd, const u8 *data, size_t data_len,
+			  struct wpabuf *buf);
+
 	/**
 	 * set_rekey_info - Set rekey information
 	 * @priv: Private driver interface data
@@ -2735,21 +2743,21 @@
 	 * switch_channel - Announce channel switch and migrate the GO to the
 	 * given frequency
 	 * @priv: Private driver interface data
-	 * @freq: Frequency in MHz
+	 * @settings: Settings for CSA period and new channel
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This function is used to move the GO to the legacy STA channel to
 	 * avoid frequency conflict in single channel concurrency.
 	 */
-	int (*switch_channel)(void *priv, unsigned int freq);
+	int (*switch_channel)(void *priv, struct csa_settings *settings);
 
 	/**
 	 * start_dfs_cac - Listen for radar interference on the channel
 	 * @priv: Private driver interface data
-	 * @freq: Frequency (in MHz) of the channel
+	 * @freq: Channel parameters
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*start_dfs_cac)(void *priv, int freq);
+	int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
 
 	/**
 	 * stop_ap - Removes beacon from AP
@@ -2786,6 +2794,212 @@
 	 * survey.
 	 */
 	int (*get_survey)(void *priv, unsigned int freq);
+
+	/**
+	 * status - Get driver interface status information
+	 * @priv: Private driver interface data
+	 * @buf: Buffer for printing tou the status information
+	 * @buflen: Maximum length of the buffer
+	 * Returns: Length of written status information or -1 on failure
+	 */
+	int (*status)(void *priv, char *buf, size_t buflen);
+
+#ifdef CONFIG_MACSEC
+	int (*macsec_init)(void *priv, struct macsec_init_params *params);
+
+	int (*macsec_deinit)(void *priv);
+
+	/**
+	 * enable_protect_frames - Set protect frames status
+	 * @priv: Private driver interface data
+	 * @enabled: TRUE = protect frames enabled
+	 *           FALSE = protect frames disabled
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*enable_protect_frames)(void *priv, Boolean enabled);
+
+	/**
+	 * set_replay_protect - Set replay protect status and window size
+	 * @priv: Private driver interface data
+	 * @enabled: TRUE = replay protect enabled
+	 *           FALSE = replay protect disabled
+	 * @window: replay window size, valid only when replay protect enabled
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_replay_protect)(void *priv, Boolean enabled, u32 window);
+
+	/**
+	 * set_current_cipher_suite - Set current cipher suite
+	 * @priv: Private driver interface data
+	 * @cs: EUI64 identifier
+	 * @cs_len: Length of the cs buffer in octets
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_current_cipher_suite)(void *priv, const u8 *cs,
+					size_t cs_len);
+
+	/**
+	 * enable_controlled_port - Set controlled port status
+	 * @priv: Private driver interface data
+	 * @enabled: TRUE = controlled port enabled
+	 *           FALSE = controlled port disabled
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*enable_controlled_port)(void *priv, Boolean enabled);
+
+	/**
+	 * get_receive_lowest_pn - Get receive lowest pn
+	 * @priv: Private driver interface data
+	 * @channel: secure channel
+	 * @an: association number
+	 * @lowest_pn: lowest accept pn
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
+				     u32 *lowest_pn);
+
+	/**
+	 * get_transmit_next_pn - Get transmit next pn
+	 * @priv: Private driver interface data
+	 * @channel: secure channel
+	 * @an: association number
+	 * @next_pn: next pn
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
+				    u32 *next_pn);
+
+	/**
+	 * set_transmit_next_pn - Set transmit next pn
+	 * @priv: Private driver interface data
+	 * @channel: secure channel
+	 * @an: association number
+	 * @next_pn: next pn
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
+				    u32 next_pn);
+
+	/**
+	 * get_available_receive_sc - get available receive channel
+	 * @priv: Private driver interface data
+	 * @channel: secure channel
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*get_available_receive_sc)(void *priv, u32 *channel);
+
+	/**
+	 * create_receive_sc - create secure channel for receiving
+	 * @priv: Private driver interface data
+	 * @channel: secure channel
+	 * @sci_addr: secure channel identifier - address
+	 * @sci_port: secure channel identifier - port
+	 * @conf_offset: confidentiality offset (0, 30, or 50)
+	 * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+	 *	2 = Strict)
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
+				 u16 sci_port, unsigned int conf_offset,
+				 int validation);
+
+	/**
+	 * delete_receive_sc - delete secure connection for receiving
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_receive_sc)(void *priv, u32 channel);
+
+	/**
+	 * create_receive_sa - create secure association for receive
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel
+	 * @an: association number
+	 * @lowest_pn: the lowest packet number can be received
+	 * @sak: the secure association key
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*create_receive_sa)(void *priv, u32 channel, u8 an,
+				 u32 lowest_pn, const u8 *sak);
+
+	/**
+	 * enable_receive_sa - enable the SA for receive
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel
+	 * @an: association number
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
+
+	/**
+	 * disable_receive_sa - disable SA for receive
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel index
+	 * @an: association number
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
+
+	/**
+	 * get_available_transmit_sc - get available transmit channel
+	 * @priv: Private driver interface data
+	 * @channel: secure channel
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*get_available_transmit_sc)(void *priv, u32 *channel);
+
+	/**
+	 * create_transmit_sc - create secure connection for transmit
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel
+	 * @sci_addr: secure channel identifier - address
+	 * @sci_port: secure channel identifier - port
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
+				  u16 sci_port, unsigned int conf_offset);
+
+	/**
+	 * delete_transmit_sc - delete secure connection for transmit
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_transmit_sc)(void *priv, u32 channel);
+
+	/**
+	 * create_transmit_sa - create secure association for transmit
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel index
+	 * @an: association number
+	 * @next_pn: the packet number used as next transmit packet
+	 * @confidentiality: True if the SA is to provide confidentiality
+	 *                   as well as integrity
+	 * @sak: the secure association key
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
+				  Boolean confidentiality, const u8 *sak);
+
+	/**
+	 * enable_transmit_sa - enable SA for transmit
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel
+	 * @an: association number
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
+
+	/**
+	 * disable_transmit_sa - disable SA for transmit
+	 * @priv: private driver interface data from init()
+	 * @channel: secure channel
+	 * @an: association number
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
+#endif /* CONFIG_MACSEC */
 };
 
 
@@ -2999,15 +3213,6 @@
 	EVENT_RX_MGMT,
 
 	/**
-	 * EVENT_RX_ACTION - Action frame received
-	 *
-	 * This event is used to indicate when an Action frame has been
-	 * received. Information about the received frame is included in
-	 * union wpa_event_data::rx_action.
-	 */
-	EVENT_RX_ACTION,
-
-	/**
 	 * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
 	 *
 	 * This event is used to indicate when the driver has started the
@@ -3111,7 +3316,8 @@
 	 * the driver does not support radar detection and another virtual
 	 * interfaces caused the operating channel to change. Other similar
 	 * resource conflicts could also trigger this for station mode
-	 * interfaces.
+	 * interfaces. This event can be propagated when channel switching
+	 * fails.
 	 */
 	EVENT_INTERFACE_UNAVAILABLE,
 
@@ -3154,38 +3360,6 @@
 	EVENT_STATION_LOW_ACK,
 
 	/**
-	 * EVENT_P2P_DEV_FOUND - Report a discovered P2P device
-	 *
-	 * This event is used only if the driver implements P2P management
-	 * internally. Event data is stored in
-	 * union wpa_event_data::p2p_dev_found.
-	 */
-	EVENT_P2P_DEV_FOUND,
-
-	/**
-	 * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request
-	 *
-	 * This event is used only if the driver implements P2P management
-	 * internally. Event data is stored in
-	 * union wpa_event_data::p2p_go_neg_req_rx.
-	 */
-	EVENT_P2P_GO_NEG_REQ_RX,
-
-	/**
-	 * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation
-	 *
-	 * This event is used only if the driver implements P2P management
-	 * internally. Event data is stored in
-	 * union wpa_event_data::p2p_go_neg_completed.
-	 */
-	EVENT_P2P_GO_NEG_COMPLETED,
-
-	EVENT_P2P_PROV_DISC_REQUEST,
-	EVENT_P2P_PROV_DISC_RESPONSE,
-	EVENT_P2P_SD_REQUEST,
-	EVENT_P2P_SD_RESPONSE,
-
-	/**
 	 * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
 	 */
 	EVENT_IBSS_PEER_LOST,
@@ -3272,17 +3446,36 @@
 	 */
 	EVENT_DFS_NOP_FINISHED,
 
-	/*
-	* EVENT_SURVEY - Received survey data
-	*
-	* This event gets triggered when a driver query is issued for survey
-	* data and the requested data becomes available. The returned data is
-	* stored in struct survey_results. The results provide at most one
-	* survey entry for each frequency and at minimum will provide one survey
-	* entry for one frequency. The survey data can be os_malloc()'d and
-	* then os_free()'d, so the event callback must only copy data.
-	*/
-	EVENT_SURVEY
+	/**
+	 * EVENT_SURVEY - Received survey data
+	 *
+	 * This event gets triggered when a driver query is issued for survey
+	 * data and the requested data becomes available. The returned data is
+	 * stored in struct survey_results. The results provide at most one
+	 * survey entry for each frequency and at minimum will provide one
+	 * survey entry for one frequency. The survey data can be os_malloc()'d
+	 * and then os_free()'d, so the event callback must only copy data.
+	 */
+	EVENT_SURVEY,
+
+	/**
+	 * EVENT_SCAN_STARTED - Scan started
+	 *
+	 * This indicates that driver has started a scan operation either based
+	 * on a request from wpa_supplicant/hostapd or from another application.
+	 * EVENT_SCAN_RESULTS is used to indicate when the scan has been
+	 * completed (either successfully or by getting cancelled).
+	 */
+	EVENT_SCAN_STARTED,
+
+	/**
+	 * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+	 *
+	 * This event indicates a set of frequency ranges that should be avoided
+	 * to reduce issues due to interference or internal co-existence
+	 * information in the driver.
+	 */
+	EVENT_AVOID_FREQUENCIES
 };
 
 
@@ -3650,48 +3843,26 @@
 		const u8 *frame;
 		size_t frame_len;
 		u32 datarate;
-		int ssi_signal; /* dBm */
-	} rx_mgmt;
-
-	/**
-	 * struct rx_action - Data for EVENT_RX_ACTION events
-	 */
-	struct rx_action {
-		/**
-		 * da - Destination address of the received Action frame
-		 */
-		const u8 *da;
 
 		/**
-		 * sa - Source address of the received Action frame
+		 * drv_priv - Pointer to store driver private BSS information
+		 *
+		 * If not set to NULL, this is used for comparison with
+		 * hostapd_data->drv_priv to determine which BSS should process
+		 * the frame.
 		 */
-		const u8 *sa;
-
-		/**
-		 * bssid - Address 3 of the received Action frame
-		 */
-		const u8 *bssid;
-
-		/**
-		 * category - Action frame category
-		 */
-		u8 category;
-
-		/**
-		 * data - Action frame body after category field
-		 */
-		const u8 *data;
-
-		/**
-		 * len - Length of data in octets
-		 */
-		size_t len;
+		void *drv_priv;
 
 		/**
 		 * freq - Frequency (in MHz) on which the frame was received
 		 */
 		int freq;
-	} rx_action;
+
+		/**
+		 * ssi_signal - Signal strength in dBm (or 0 if not available)
+		 */
+		int ssi_signal;
+	} rx_mgmt;
 
 	/**
 	 * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
@@ -3831,66 +4002,6 @@
 	} low_ack;
 
 	/**
-	 * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND
-	 */
-	struct p2p_dev_found {
-		const u8 *addr;
-		const u8 *dev_addr;
-		const u8 *pri_dev_type;
-		const char *dev_name;
-		u16 config_methods;
-		u8 dev_capab;
-		u8 group_capab;
-	} p2p_dev_found;
-
-	/**
-	 * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX
-	 */
-	struct p2p_go_neg_req_rx {
-		const u8 *src;
-		u16 dev_passwd_id;
-	} p2p_go_neg_req_rx;
-
-	/**
-	 * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED
-	 */
-	struct p2p_go_neg_completed {
-		struct p2p_go_neg_results *res;
-	} p2p_go_neg_completed;
-
-	struct p2p_prov_disc_req {
-		const u8 *peer;
-		u16 config_methods;
-		const u8 *dev_addr;
-		const u8 *pri_dev_type;
-		const char *dev_name;
-		u16 supp_config_methods;
-		u8 dev_capab;
-		u8 group_capab;
-	} p2p_prov_disc_req;
-
-	struct p2p_prov_disc_resp {
-		const u8 *peer;
-		u16 config_methods;
-	} p2p_prov_disc_resp;
-
-	struct p2p_sd_req {
-		int freq;
-		const u8 *sa;
-		u8 dialog_token;
-		u16 update_indic;
-		const u8 *tlvs;
-		size_t tlvs_len;
-	} p2p_sd_req;
-
-	struct p2p_sd_resp {
-		const u8 *sa;
-		u16 update_indic;
-		const u8 *tlvs;
-		size_t tlvs_len;
-	} p2p_sd_resp;
-
-	/**
 	 * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
 	 */
 	struct ibss_peer_lost {
@@ -3935,11 +4046,17 @@
 	 * @freq: Frequency of new channel in MHz
 	 * @ht_enabled: Whether this is an HT channel
 	 * @ch_offset: Secondary channel offset
+	 * @ch_width: Channel width
+	 * @cf1: Center frequency 1
+	 * @cf2: Center frequency 2
 	 */
 	struct ch_switch {
 		int freq;
 		int ht_enabled;
 		int ch_offset;
+		enum chan_width ch_width;
+		int cf1;
+		int cf2;
 	} ch_switch;
 
 	/**
@@ -3961,6 +4078,11 @@
 	 */
 	struct dfs_event {
 		int freq;
+		int ht_enabled;
+		int chan_offset;
+		enum chan_width chan_width;
+		int cf1;
+		int cf2;
 	} dfs_event;
 
 	/**
@@ -3973,6 +4095,25 @@
 		unsigned int freq_filter;
 		struct dl_list survey_list; /* struct freq_survey */
 	} survey_results;
+
+	/**
+	 * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED
+	 * @initiator: Initiator of the regulatory change
+	 * @type: Regulatory change type
+	 * @alpha2: Country code (or "" if not available)
+	 */
+	struct channel_list_changed {
+		enum reg_change_initiator initiator;
+		enum reg_type type;
+		char alpha2[3];
+	} channel_list_changed;
+
+	/**
+	 * freq_range - List of frequency ranges
+	 *
+	 * This is used as the data with EVENT_AVOID_FREQUENCIES.
+	 */
+	struct wpa_freq_range_list freq_range;
 };
 
 /**
@@ -4031,4 +4172,7 @@
 /* Convert wpa_event_type to a string for logging */
 const char * event_to_string(enum wpa_event_type event);
 
+/* NULL terminated array of linked in driver wrappers */
+extern struct wpa_driver_ops *wpa_drivers[];
+
 #endif /* DRIVER_H */
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index c2f5934..b569a0a 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -260,6 +260,17 @@
 	case WPA_CIPHER_CCMP:
 		v = IEEE80211_CIPHER_AES_CCM;
 		break;
+#ifdef ATH_GCM_SUPPORT
+	case WPA_CIPHER_CCMP_256:
+		v = IEEE80211_CIPHER_AES_CCM_256;
+		break;
+	case WPA_CIPHER_GCMP:
+		v = IEEE80211_CIPHER_AES_GCM;
+		break;
+	case WPA_CIPHER_GCMP_256:
+		v = IEEE80211_CIPHER_AES_GCM_256;
+		break;
+#endif /* ATH_GCM_SUPPORT */
 	case WPA_CIPHER_TKIP:
 		v = IEEE80211_CIPHER_TKIP;
 		break;
@@ -294,6 +305,14 @@
 	v = 0;
 	if (params->wpa_pairwise & WPA_CIPHER_CCMP)
 		v |= 1<<IEEE80211_CIPHER_AES_CCM;
+#ifdef ATH_GCM_SUPPORT
+	if (params->wpa_pairwise & WPA_CIPHER_CCMP_256)
+		v |= 1<<IEEE80211_CIPHER_AES_CCM_256;
+	if (params->wpa_pairwise & WPA_CIPHER_GCMP)
+		v |= 1<<IEEE80211_CIPHER_AES_GCM;
+	if (params->wpa_pairwise & WPA_CIPHER_GCMP_256)
+		v |= 1<<IEEE80211_CIPHER_AES_GCM_256;
+#endif /* ATH_GCM_SUPPORT */
 	if (params->wpa_pairwise & WPA_CIPHER_TKIP)
 		v |= 1<<IEEE80211_CIPHER_TKIP;
 	if (params->wpa_pairwise & WPA_CIPHER_NONE)
@@ -354,19 +373,16 @@
 		return atheros_set_privacy(drv, 0);
 	}
 	if (!params->wpa && !params->ieee802_1x) {
-		hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-			HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+		wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
 		return -1;
 	}
 	if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
-		hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-			HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+		wpa_printf(MSG_WARNING, "Error configuring WPA state!");
 		return -1;
 	}
 	if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
 		(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
-		hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-			HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+		wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
 		return -1;
 	}
 
@@ -474,10 +490,32 @@
 	case WPA_ALG_CCMP:
 		cipher = IEEE80211_CIPHER_AES_CCM;
 		break;
+#ifdef ATH_GCM_SUPPORT
+	case WPA_ALG_CCMP_256:
+		cipher = IEEE80211_CIPHER_AES_CCM_256;
+		break;
+	case WPA_ALG_GCMP:
+		cipher = IEEE80211_CIPHER_AES_GCM;
+		break;
+	case WPA_ALG_GCMP_256:
+		cipher = IEEE80211_CIPHER_AES_GCM_256;
+		break;
+#endif /* ATH_GCM_SUPPORT */
 #ifdef CONFIG_IEEE80211W
 	case WPA_ALG_IGTK:
 		cipher = IEEE80211_CIPHER_AES_CMAC;
 		break;
+#ifdef ATH_GCM_SUPPORT
+	case WPA_ALG_BIP_CMAC_256:
+		cipher = IEEE80211_CIPHER_AES_CMAC_256;
+		break;
+	case WPA_ALG_BIP_GMAC_128:
+		cipher = IEEE80211_CIPHER_AES_GMAC;
+		break;
+	case WPA_ALG_BIP_GMAC_256:
+		cipher = IEEE80211_CIPHER_AES_GMAC_256;
+		break;
+#endif /* ATH_GCM_SUPPORT */
 #endif /* CONFIG_IEEE80211W */
 	default:
 		printf("%s: unknown/unsupported algorithm %d\n",
@@ -809,16 +847,10 @@
 		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
 		break;
 	case WLAN_FC_STYPE_ACTION:
-		if (&mgmt->u.action.category > buf + len)
-			break;
 		os_memset(&event, 0, sizeof(event));
-		event.rx_action.da = mgmt->da;
-		event.rx_action.sa = mgmt->sa;
-		event.rx_action.bssid = mgmt->bssid;
-		event.rx_action.category = mgmt->u.action.category;
-		event.rx_action.data = &mgmt->u.action.category;
-		event.rx_action.len = buf + len - event.rx_action.data;
-		wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = len;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
 		break;
 	case WLAN_FC_STYPE_AUTH:
 		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth))
@@ -872,8 +904,59 @@
 	event.rx_mgmt.frame_len = len;
 	wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
 }
+
 #endif /* CONFIG_HS20 */
 
+
+static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set,
+			       u8 qos_map_set_len)
+{
+#ifdef CONFIG_ATHEROS_QOS_MAP
+	struct atheros_driver_data *drv = ctx;
+	struct ieee80211req_athdbg req;
+	struct ieee80211_qos_map *qos_map = &req.data.qos_map;
+	struct iwreq iwr;
+	int i, up_start;
+
+	if (qos_map_set_len < 16 || qos_map_set_len > 58 ||
+	    qos_map_set_len & 1) {
+		wpa_printf(MSG_ERROR, "Invalid QoS Map");
+		return -1;
+	} else {
+		memset(&req, 0, sizeof(struct ieee80211req_athdbg));
+		req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF;
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name));
+		iwr.u.data.pointer = (void *) &req;
+		iwr.u.data.length = sizeof(struct ieee80211req_athdbg);
+	}
+
+	qos_map->valid = 1;
+	qos_map->num_dscp_except = (qos_map_set_len - 16) / 2;
+	if (qos_map->num_dscp_except) {
+		for (i = 0; i < qos_map->num_dscp_except; i++) {
+			qos_map->dscp_exception[i].dscp	= qos_map_set[i * 2];
+			qos_map->dscp_exception[i].up =	qos_map_set[i * 2 + 1];
+		}
+	}
+
+	up_start = qos_map_set_len - 16;
+	for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) {
+		qos_map->up[i].low = qos_map_set[up_start + (i * 2)];
+		qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1];
+	}
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) {
+		perror("ioctl[IEEE80211_IOCTL_DBGREQ]");
+		wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map",
+			   __func__, drv->iface);
+		return -1;
+	}
+#endif /* CONFIG_ATHEROS_QOS_MAP */
+
+	return 0;
+}
+
 #if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
 static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
 				 size_t len)
@@ -906,16 +989,10 @@
 
 	switch (stype) {
 	case WLAN_FC_STYPE_ACTION:
-		if (&mgmt->u.action.category > buf + len)
-			break;
 		os_memset(&event, 0, sizeof(event));
-		event.rx_action.da = mgmt->da;
-		event.rx_action.sa = mgmt->sa;
-		event.rx_action.bssid = mgmt->bssid;
-		event.rx_action.category = mgmt->u.action.category;
-		event.rx_action.data = &mgmt->u.action.category;
-		event.rx_action.len = buf + len - event.rx_action.data;
-		wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = len;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
 		break;
 	default:
 		break;
@@ -1334,7 +1411,7 @@
 
 	while (1) {
 		os_memset(&iwr, 0, sizeof(iwr));
-		os_strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+		os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
 
 		iwr.u.data.pointer = (void *) tbuf;
 		iwr.u.data.length = sizeof(tbuf);
@@ -1832,6 +1909,25 @@
 	wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
 			params->assocresp_ies);
 
+#if defined(CONFIG_HS20) && defined(IEEE80211_PARAM_OSEN)
+	if (params->osen) {
+		struct wpa_bss_params bss_params;
+
+		os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
+		bss_params.enabled = 1;
+		bss_params.wpa = 2;
+		bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
+		bss_params.wpa_group = WPA_CIPHER_CCMP;
+		bss_params.ieee802_1x = 1;
+
+		if (atheros_set_privacy(priv, 1) ||
+		    set80211param(priv, IEEE80211_PARAM_OSEN, 1))
+			return -1;
+
+		return atheros_set_ieee8021x(priv, &bss_params);
+	}
+#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
+
 	return 0;
 }
 
@@ -1963,7 +2059,7 @@
 }
 
 
-#ifdef CONFIG_WNM
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
 static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
 			u8 *ie, u16 *len, enum wnm_oper oper)
 {
@@ -2116,7 +2212,7 @@
 		return -1;
 	}
 }
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
 
 
 const struct wpa_driver_ops wpa_driver_atheros_ops = {
@@ -2150,7 +2246,8 @@
 	.add_sta_node    	= atheros_add_sta_node,
 #endif /* CONFIG_IEEE80211R */
 	.send_action		= atheros_send_action,
-#ifdef CONFIG_WNM
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
 	.wnm_oper		= atheros_wnm_oper,
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+	.set_qos_map		= atheros_set_qos_map,
 };
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 4acb12b..ca64d5c 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -62,36 +62,12 @@
 	int	prev_privacy;	/* privacy state to restore on deinit */
 	int	prev_wpa;	/* wpa state to restore on deinit */
 	enum ieee80211_opmode opmode;	/* operation mode */
+	char	*event_buf;
+	size_t	event_buf_len;
 };
 
 /* Generic functions for hostapd and wpa_supplicant */
 
-static enum ieee80211_opmode
-get80211opmode(struct bsd_driver_data *drv)
-{
-	struct ifmediareq ifmr;
-
-	(void) memset(&ifmr, 0, sizeof(ifmr));
-	(void) strncpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
-
-	if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
-		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
-			if (ifmr.ifm_current & IFM_FLAG0)
-				return IEEE80211_M_AHDEMO;
-			else
-				return IEEE80211_M_IBSS;
-		}
-		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
-			return IEEE80211_M_HOSTAP;
-		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
-			return IEEE80211_M_MONITOR;
-		if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
-			return IEEE80211_M_MBSS;
-	}
-	return IEEE80211_M_STA;
-}
-
-
 static int
 bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
 {
@@ -310,21 +286,15 @@
 	return 0;
 }
 
-#ifdef HOSTAPD
-static int
-bsd_commit(void *priv)
-{
-	return bsd_ctrl_iface(priv, 1);
-}
-#endif /* HOSTAPD */
-
 static int
 bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 	    const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
 	    size_t seq_len, const u8 *key, size_t key_len)
 {
 	struct ieee80211req_key wk;
+#ifdef IEEE80211_KEY_NOREPLAY
 	struct bsd_driver_data *drv = priv;
+#endif /* IEEE80211_KEY_NOREPLAY */
 
 	wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
 		   "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
@@ -380,12 +350,14 @@
 	if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
 		wk.ik_flags |= IEEE80211_KEY_DEFAULT;
 #ifndef HOSTAPD
+#ifdef IEEE80211_KEY_NOREPLAY
 	/*
 	 * Ignore replay failures in IBSS and AHDEMO mode.
 	 */
 	if (drv->opmode == IEEE80211_M_IBSS ||
 	    drv->opmode == IEEE80211_M_AHDEMO)
 		wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
+#endif /* IEEE80211_KEY_NOREPLAY */
 #endif /* HOSTAPD */
 	wk.ik_keylen = key_len;
 	if (seq) {
@@ -522,28 +494,6 @@
 	return bsd_ctrl_iface(priv, 1);
 }
 
-#ifdef HOSTAPD
-static int
-bsd_set_sta_authorized(void *priv, const u8 *addr,
-		       int total_flags, int flags_or, int flags_and)
-{
-	int authorized = -1;
-
-	/* For now, only support setting Authorized flag */
-	if (flags_or & WPA_STA_AUTHORIZED)
-		authorized = 1;
-	if (!(flags_and & WPA_STA_AUTHORIZED))
-		authorized = 0;
-
-	if (authorized < 0)
-		return 0;
-
-	return bsd_send_mlme_param(priv, authorized ?
-				   IEEE80211_MLME_AUTHORIZE :
-				   IEEE80211_MLME_UNAUTHORIZE, 0, addr);
-}
-#endif /* HOSTAPD */
-
 static void
 bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
 {
@@ -636,7 +586,7 @@
 	return 0;
 }
 
-static int
+static size_t
 rtbuf_len(void)
 {
 	size_t len;
@@ -773,37 +723,26 @@
 bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
 {
 	struct bsd_driver_data *drv = ctx;
-	char *buf;
 	struct if_announcemsghdr *ifan;
 	struct rt_msghdr *rtm;
 	struct ieee80211_michael_event *mic;
 	struct ieee80211_join_event *join;
 	struct ieee80211_leave_event *leave;
-	int n, len;
+	int n;
 	union wpa_event_data data;
 
-	len = rtbuf_len();
-
-	buf = os_malloc(len);
-	if (buf == NULL) {
-		wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
-		return;
-	}
-
-	n = read(sock, buf, len);
+	n = read(sock, drv->event_buf, drv->event_buf_len);
 	if (n < 0) {
 		if (errno != EINTR && errno != EAGAIN)
 			wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
 				   __func__, strerror(errno));
-		os_free(buf);
 		return;
 	}
 
-	rtm = (struct rt_msghdr *) buf;
+	rtm = (struct rt_msghdr *) drv->event_buf;
 	if (rtm->rtm_version != RTM_VERSION) {
 		wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
 			   rtm->rtm_version);
-		os_free(buf);
 		return;
 	}
 	ifan = (struct if_announcemsghdr *) rtm;
@@ -844,7 +783,6 @@
 		}
 		break;
 	}
-	os_free(buf);
 }
 
 static void
@@ -861,7 +799,15 @@
 
 	drv = os_zalloc(sizeof(struct bsd_driver_data));
 	if (drv == NULL) {
-		printf("Could not allocate memory for bsd driver data\n");
+		wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data");
+		return NULL;
+	}
+
+	drv->event_buf_len = rtbuf_len();
+
+	drv->event_buf = os_malloc(drv->event_buf_len);
+	if (drv->event_buf == NULL) {
+		wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
 		goto bad;
 	}
 
@@ -904,6 +850,7 @@
 		l2_packet_deinit(drv->sock_xmit);
 	if (drv->sock >= 0)
 		close(drv->sock);
+	os_free(drv->event_buf);
 	if (drv != NULL)
 		os_free(drv);
 	return NULL;
@@ -924,9 +871,37 @@
 		close(drv->sock);
 	if (drv->sock_xmit != NULL)
 		l2_packet_deinit(drv->sock_xmit);
+	os_free(drv->event_buf);
 	os_free(drv);
 }
 
+
+static int
+bsd_commit(void *priv)
+{
+	return bsd_ctrl_iface(priv, 1);
+}
+
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+		       int total_flags, int flags_or, int flags_and)
+{
+	int authorized = -1;
+
+	/* For now, only support setting Authorized flag */
+	if (flags_or & WPA_STA_AUTHORIZED)
+		authorized = 1;
+	if (!(flags_and & WPA_STA_AUTHORIZED))
+		authorized = 0;
+
+	if (authorized < 0)
+		return 0;
+
+	return bsd_send_mlme_param(priv, authorized ?
+				   IEEE80211_MLME_AUTHORIZE :
+				   IEEE80211_MLME_UNAUTHORIZE, 0, addr);
+}
 #else /* HOSTAPD */
 
 static int
@@ -1104,9 +1079,9 @@
 	if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
 		return -1;
 
-	privacy = !(params->pairwise_suite == CIPHER_NONE &&
-	    params->group_suite == CIPHER_NONE &&
-	    params->key_mgmt_suite == KEY_MGMT_NONE &&
+	privacy = !(params->pairwise_suite == WPA_CIPHER_NONE &&
+	    params->group_suite == WPA_CIPHER_NONE &&
+	    params->key_mgmt_suite == WPA_KEY_MGMT_NONE &&
 	    params->wpa_ie_len == 0);
 	wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
 
@@ -1202,7 +1177,6 @@
 wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
 {
 	struct bsd_driver_data *drv = sock_ctx;
-	char *buf;
 	struct if_announcemsghdr *ifan;
 	struct if_msghdr *ifm;
 	struct rt_msghdr *rtm;
@@ -1210,30 +1184,20 @@
 	struct ieee80211_michael_event *mic;
 	struct ieee80211_leave_event *leave;
 	struct ieee80211_join_event *join;
-	int n, len;
+	int n;
 
-	len = rtbuf_len();
-
-	buf = os_malloc(len);
-	if (buf == NULL) {
-		wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
-		return;
-	}
-
-	n = read(sock, buf, len);
+	n = read(sock, drv->event_buf, drv->event_buf_len);
 	if (n < 0) {
 		if (errno != EINTR && errno != EAGAIN)
 			wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
 				   __func__, strerror(errno));
-		os_free(buf);
 		return;
 	}
 
-	rtm = (struct rt_msghdr *) buf;
+	rtm = (struct rt_msghdr *) drv->event_buf;
 	if (rtm->rtm_version != RTM_VERSION) {
 		wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
 			   rtm->rtm_version);
-		os_free(buf);
 		return;
 	}
 	os_memset(&event, 0, sizeof(event));
@@ -1248,7 +1212,6 @@
 		case IFAN_DEPARTURE:
 			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
 		default:
-			os_free(buf);
 			return;
 		}
 		wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
@@ -1321,7 +1284,6 @@
 		}
 		break;
 	}
-	os_free(buf);
 }
 
 static void
@@ -1492,6 +1454,33 @@
 	return 0;
 }
 
+static enum ieee80211_opmode
+get80211opmode(struct bsd_driver_data *drv)
+{
+	struct ifmediareq ifmr;
+
+	(void) memset(&ifmr, 0, sizeof(ifmr));
+	(void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+	if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
+			if (ifmr.ifm_current & IFM_FLAG0)
+				return IEEE80211_M_AHDEMO;
+			else
+				return IEEE80211_M_IBSS;
+		}
+		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+			return IEEE80211_M_HOSTAP;
+		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+			return IEEE80211_M_MONITOR;
+#ifdef IEEE80211_M_MBSS
+		if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
+			return IEEE80211_M_MBSS;
+#endif /* IEEE80211_M_MBSS */
+	}
+	return IEEE80211_M_STA;
+}
+
 static void *
 wpa_driver_bsd_init(void *ctx, const char *ifname)
 {
@@ -1502,6 +1491,15 @@
 	drv = os_zalloc(sizeof(*drv));
 	if (drv == NULL)
 		return NULL;
+
+	drv->event_buf_len = rtbuf_len();
+
+	drv->event_buf = os_malloc(drv->event_buf_len);
+	if (drv->event_buf == NULL) {
+		wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
+		goto fail1;
+	}
+
 	/*
 	 * NB: We require the interface name be mappable to an index.
 	 *     This implies we do not support having wpa_supplicant
@@ -1556,6 +1554,7 @@
 fail:
 	close(drv->sock);
 fail1:
+	os_free(drv->event_buf);
 	os_free(drv);
 	return NULL;
 #undef GETPARAM
@@ -1581,6 +1580,7 @@
 		l2_packet_deinit(drv->sock_xmit);
 	(void) close(drv->route);		/* ioctl socket */
 	(void) close(drv->sock);		/* event socket */
+	os_free(drv->event_buf);
 	os_free(drv);
 }
 
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 8d1d22e..3058cd5 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -49,7 +49,6 @@
 	E2S(TX_STATUS);
 	E2S(RX_FROM_UNKNOWN);
 	E2S(RX_MGMT);
-	E2S(RX_ACTION);
 	E2S(REMAIN_ON_CHANNEL);
 	E2S(CANCEL_REMAIN_ON_CHANNEL);
 	E2S(MLME_RX);
@@ -65,13 +64,6 @@
 	E2S(UNPROT_DEAUTH);
 	E2S(UNPROT_DISASSOC);
 	E2S(STATION_LOW_ACK);
-	E2S(P2P_DEV_FOUND);
-	E2S(P2P_GO_NEG_REQ_RX);
-	E2S(P2P_GO_NEG_COMPLETED);
-	E2S(P2P_PROV_DISC_REQUEST);
-	E2S(P2P_PROV_DISC_RESPONSE);
-	E2S(P2P_SD_REQUEST);
-	E2S(P2P_SD_RESPONSE);
 	E2S(IBSS_PEER_LOST);
 	E2S(DRIVER_GTK_REKEY);
 	E2S(SCHED_SCAN_STOPPED);
@@ -85,6 +77,8 @@
 	E2S(DFS_CAC_ABORTED);
 	E2S(DFS_NOP_FINISHED);
 	E2S(SURVEY);
+	E2S(SCAN_STARTED);
+	E2S(AVOID_FREQUENCIES);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
new file mode 100644
index 0000000..cf24799
--- /dev/null
+++ b/src/drivers/driver_macsec_qca.c
@@ -0,0 +1,887 @@
+/*
+ * Wired Ethernet driver interface for QCA MACsec driver
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "driver.h"
+
+#include "nss_macsec_secy.h"
+#include "nss_macsec_secy_rx.h"
+#include "nss_macsec_secy_tx.h"
+
+#define MAXSC 16
+
+/* TCI field definition */
+#define TCI_ES                0x40
+#define TCI_SC                0x20
+#define TCI_SCB               0x10
+#define TCI_E                 0x08
+#define TCI_C                 0x04
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct macsec_qca_data {
+	char ifname[IFNAMSIZ + 1];
+	u32 secy_id;
+	void *ctx;
+
+	int sock; /* raw packet socket for driver access */
+	int pf_sock;
+	int membership, multi, iff_allmulti, iff_up;
+
+	/* shadow */
+	Boolean always_include_sci;
+	Boolean use_es;
+	Boolean use_scb;
+	Boolean protect_frames;
+	Boolean replay_protect;
+	u32 replay_window;
+};
+
+
+static int macsec_qca_multicast_membership(int sock, int ifindex,
+					   const u8 *addr, int add)
+{
+#ifdef __linux__
+	struct packet_mreq mreq;
+
+	if (sock < 0)
+		return -1;
+
+	os_memset(&mreq, 0, sizeof(mreq));
+	mreq.mr_ifindex = ifindex;
+	mreq.mr_type = PACKET_MR_MULTICAST;
+	mreq.mr_alen = ETH_ALEN;
+	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+	if (setsockopt(sock, SOL_PACKET,
+		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+		       &mreq, sizeof(mreq)) < 0) {
+		perror("setsockopt");
+		return -1;
+	}
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+static int macsec_qca_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+static int macsec_qca_get_bssid(void *priv, u8 *bssid)
+{
+	/* Report PAE group address as the "BSSID" for macsec connection. */
+	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+	return 0;
+}
+
+
+static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_WIRED;
+	return 0;
+}
+
+
+static int macsec_qca_get_ifflags(const char *ifname, int *flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+		perror("ioctl[SIOCGIFFLAGS]");
+		close(s);
+		return -1;
+	}
+	close(s);
+	*flags = ifr.ifr_flags & 0xffff;
+	return 0;
+}
+
+
+static int macsec_qca_set_ifflags(const char *ifname, int flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	ifr.ifr_flags = flags & 0xffff;
+	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+		perror("ioctl[SIOCSIFFLAGS]");
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int macsec_qca_get_ifstatus(const char *ifname, int *status)
+{
+	struct ifmediareq ifmr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&ifmr, 0, sizeof(ifmr));
+	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+		perror("ioctl[SIOCGIFMEDIA]");
+		close(s);
+		return -1;
+	}
+	close(s);
+	*status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+		(IFM_ACTIVE | IFM_AVALID);
+
+	return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
+static int macsec_qca_multi(const char *ifname, const u8 *addr, int add)
+{
+	struct ifreq ifr;
+	int s;
+
+#ifdef __sun__
+	return -1;
+#endif /* __sun__ */
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+	{
+		struct sockaddr_dl *dlp;
+		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+		dlp->sdl_len = sizeof(struct sockaddr_dl);
+		dlp->sdl_family = AF_LINK;
+		dlp->sdl_index = 0;
+		dlp->sdl_nlen = 0;
+		dlp->sdl_alen = ETH_ALEN;
+		dlp->sdl_slen = 0;
+		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+	}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+	{
+		struct sockaddr *sap;
+		sap = (struct sockaddr *) &ifr.ifr_addr;
+		sap->sa_len = sizeof(struct sockaddr);
+		sap->sa_family = AF_UNSPEC;
+		os_memcpy(sap->sa_data, addr, ETH_ALEN);
+	}
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+		perror("ioctl[SIOC{ADD/DEL}MULTI]");
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+static void __macsec_drv_init(struct macsec_qca_data *drv)
+{
+	int ret = 0;
+	fal_rx_ctl_filt_t rx_ctl_filt;
+	fal_tx_ctl_filt_t tx_ctl_filt;
+
+	wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id);
+
+	/* Enable Secy and Let EAPoL bypass */
+	ret = nss_macsec_secy_en_set(drv->secy_id, TRUE);
+	if (ret)
+		wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL");
+
+	ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id,
+						     FAL_SC_SA_MAP_1_4);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL");
+
+	os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt));
+	rx_ctl_filt.bypass = 1;
+	rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE;
+	rx_ctl_filt.match_mask = 0xffff;
+	rx_ctl_filt.ether_type_da_range = 0x888e;
+	ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt);
+	if (ret)
+		wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL");
+
+	os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt));
+	tx_ctl_filt.bypass = 1;
+	tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE;
+	tx_ctl_filt.match_mask = 0xffff;
+	tx_ctl_filt.ether_type_da_range = 0x888e;
+	ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt);
+	if (ret)
+		wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL");
+}
+
+
+static void __macsec_drv_deinit(struct macsec_qca_data *drv)
+{
+	nss_macsec_secy_en_set(drv->secy_id, FALSE);
+	nss_macsec_secy_rx_sc_del_all(drv->secy_id);
+	nss_macsec_secy_tx_sc_del_all(drv->secy_id);
+}
+
+
+static void * macsec_qca_init(void *ctx, const char *ifname)
+{
+	struct macsec_qca_data *drv;
+	int flags;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+	drv->ctx = ctx;
+
+	/* Board specific settings */
+	if (os_memcmp("eth2", drv->ifname, 4) == 0)
+		drv->secy_id = 1;
+	else if (os_memcmp("eth3", drv->ifname, 4) == 0)
+		drv->secy_id = 2;
+	else
+		drv->secy_id = -1;
+
+#ifdef __linux__
+	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (drv->pf_sock < 0)
+		perror("socket(PF_PACKET)");
+#else /* __linux__ */
+	drv->pf_sock = -1;
+#endif /* __linux__ */
+
+	if (macsec_qca_get_ifflags(ifname, &flags) == 0 &&
+	    !(flags & IFF_UP) &&
+	    macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) {
+		drv->iff_up = 1;
+	}
+
+	if (macsec_qca_multicast_membership(drv->pf_sock,
+					    if_nametoindex(drv->ifname),
+					    pae_group_addr, 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Added multicast membership with packet socket",
+			   __func__);
+		drv->membership = 1;
+	} else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Added multicast membership with SIOCADDMULTI",
+			   __func__);
+		drv->multi = 1;
+	} else if (macsec_qca_get_ifflags(ifname, &flags) < 0) {
+		wpa_printf(MSG_INFO, "%s: Could not get interface flags",
+			   __func__);
+		os_free(drv);
+		return NULL;
+	} else if (flags & IFF_ALLMULTI) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Interface is already configured for multicast",
+			   __func__);
+	} else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) {
+		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
+			   __func__);
+		os_free(drv);
+		return NULL;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
+		drv->iff_allmulti = 1;
+	}
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+	{
+		int status;
+		wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+			   __func__);
+		while (macsec_qca_get_ifstatus(ifname, &status) == 0 &&
+		       status == 0)
+			sleep(1);
+	}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+	return drv;
+}
+
+
+static void macsec_qca_deinit(void *priv)
+{
+	struct macsec_qca_data *drv = priv;
+	int flags;
+
+	if (drv->membership &&
+	    macsec_qca_multicast_membership(drv->pf_sock,
+					    if_nametoindex(drv->ifname),
+					    pae_group_addr, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to remove PAE multicast group (PACKET)",
+			   __func__);
+	}
+
+	if (drv->multi &&
+	    macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
+			   __func__);
+	}
+
+	if (drv->iff_allmulti &&
+	    (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 ||
+	     macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+			   __func__);
+	}
+
+	if (drv->iff_up &&
+	    macsec_qca_get_ifflags(drv->ifname, &flags) == 0 &&
+	    (flags & IFF_UP) &&
+	    macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+			   __func__);
+	}
+
+	if (drv->pf_sock != -1)
+		close(drv->pf_sock);
+
+	os_free(drv);
+}
+
+
+static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	struct macsec_qca_data *drv = priv;
+
+	drv->always_include_sci = params->always_include_sci;
+	drv->use_es = params->use_es;
+	drv->use_scb = params->use_scb;
+
+	wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d",
+		   __func__, drv->use_es, drv->use_scb,
+		   drv->always_include_sci);
+
+	__macsec_drv_init(drv);
+
+	return 0;
+}
+
+
+static int macsec_qca_macsec_deinit(void *priv)
+{
+	struct macsec_qca_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	__macsec_drv_deinit(drv);
+
+	return 0;
+}
+
+
+static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+	drv->protect_frames = enabled;
+
+	return ret;
+}
+
+
+static int macsec_qca_set_replay_protect(void *priv, Boolean enabled,
+					 unsigned int window)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u",
+		   __func__, enabled, window);
+
+	drv->replay_protect = enabled;
+	drv->replay_window = window;
+
+	return ret;
+}
+
+
+static int macsec_qca_set_current_cipher_suite(void *priv, const u8 *cs,
+					       size_t cs_len)
+{
+	u8 default_cs_id[] = CS_ID_GCM_AES_128;
+
+	if (cs_len != CS_ID_LEN ||
+	    os_memcmp(cs, default_cs_id, cs_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "macsec: NOT supported CipherSuite",
+			    cs, cs_len);
+		return -1;
+	}
+
+	/* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */
+	wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__);
+
+	return 0;
+}
+
+
+static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled);
+
+	ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled);
+
+	return ret;
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an,
+					    u32 *lowest_pn)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u32 next_pn = 0;
+	bool enabled = FALSE;
+	u32 win;
+
+	ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an,
+						 &next_pn);
+	ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
+							&enabled);
+	ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id,
+							    channel, &win);
+
+	if (enabled)
+		*lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+	else
+		*lowest_pn = next_pn;
+
+	wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn);
+
+	return ret;
+}
+
+
+static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an,
+					   u32 *next_pn)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an,
+						 next_pn);
+
+	wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn);
+
+	return ret;
+}
+
+
+int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
+						 next_pn);
+
+	wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn);
+
+	return ret;
+}
+
+
+static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u32 sc_ch = 0;
+	bool in_use = FALSE;
+
+	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+		ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch,
+							&in_use);
+		if (ret)
+			continue;
+
+		if (!in_use) {
+			*channel = sc_ch;
+			wpa_printf(MSG_DEBUG, "%s: channel=%d",
+				   __func__, *channel);
+			return 0;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
+
+	return -1;
+}
+
+
+static int macsec_qca_create_receive_sc(void *priv, u32 channel,
+					const u8 *sci_addr, u16 sci_port,
+					unsigned int conf_offset,
+					int validation)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	fal_rx_prc_lut_t entry;
+	fal_rx_sc_validate_frame_e vf;
+	enum validate_frames validate_frames = validation;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* rx prc lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	os_memcpy(entry.sci, sci_addr, ETH_ALEN);
+	entry.sci[6] = (sci_port >> 8) & 0xf;
+	entry.sci[7] = sci_port & 0xf;
+	entry.sci_mask = 0xf;
+
+	entry.valid = 1;
+	entry.channel = channel;
+	entry.action = FAL_RX_PRC_ACTION_PROCESS;
+	entry.offset = conf_offset;
+
+	/* rx validate frame  */
+	if (validate_frames == Strict)
+		vf = FAL_RX_SC_VALIDATE_FRAME_STRICT;
+	else if (validate_frames == Checked)
+		vf = FAL_RX_SC_VALIDATE_FRAME_CHECK;
+	else
+		vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED;
+
+	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+	ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel);
+	ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel,
+							vf);
+	ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel,
+							drv->replay_protect);
+	ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id,
+							    channel,
+							    drv->replay_window);
+
+	return ret;
+}
+
+
+static int macsec_qca_delete_receive_sc(void *priv, u32 channel)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	fal_rx_prc_lut_t entry;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* rx prc lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel);
+	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+
+	return ret;
+}
+
+
+static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an,
+					u32 lowest_pn, const u8 *sak)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	fal_rx_sak_t rx_sak;
+	int i = 0;
+
+	wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
+		   __func__, channel, an, lowest_pn);
+
+	os_memset(&rx_sak, 0, sizeof(rx_sak));
+	for (i = 0; i < 16; i++)
+		rx_sak.sak[i] = sak[15 - i];
+
+	ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an);
+	ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak);
+
+	return ret;
+}
+
+
+static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE);
+
+	return ret;
+}
+
+
+static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE);
+
+	return ret;
+}
+
+
+static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u32 sc_ch = 0;
+	bool in_use = FALSE;
+
+	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+		ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+							&in_use);
+		if (ret)
+			continue;
+
+		if (!in_use) {
+			*channel = sc_ch;
+			wpa_printf(MSG_DEBUG, "%s: channel=%d",
+				   __func__, *channel);
+			return 0;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__);
+
+	return -1;
+}
+
+
+static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
+					 const u8 *sci_addr, u16 sci_port,
+					 unsigned int conf_offset)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	fal_tx_class_lut_t entry;
+	u8 psci[ETH_ALEN + 2];
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* class lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	entry.valid = 1;
+	entry.action = FAL_TX_CLASS_ACTION_FORWARD;
+	entry.channel = channel;
+
+	os_memcpy(psci, sci_addr, ETH_ALEN);
+	psci[6] = (sci_port >> 8) & 0xf;
+	psci[7] = sci_port & 0xf;
+
+	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+	ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
+	ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel,
+						 drv->protect_frames);
+	ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+								channel,
+								conf_offset);
+
+	return ret;
+}
+
+
+static int macsec_qca_delete_transmit_sc(void *priv, u32 channel)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	fal_tx_class_lut_t entry;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* class lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+	ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel);
+
+	return ret;
+}
+
+
+static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an,
+					 u32 next_pn, Boolean confidentiality,
+					 const u8 *sak)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u8 tci = 0;
+	fal_tx_sak_t tx_sak;
+	int i;
+
+	wpa_printf(MSG_DEBUG,
+		   "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
+		   __func__, channel, an, next_pn, confidentiality);
+
+	if (drv->always_include_sci)
+		tci |= TCI_SC;
+	else if (drv->use_es)
+		tci |= TCI_ES;
+	else if (drv->use_scb)
+		tci |= TCI_SCB;
+
+	if (confidentiality)
+		tci |= TCI_E | TCI_C;
+
+	os_memset(&tx_sak, 0, sizeof(tx_sak));
+	for (i = 0; i < 16; i++)
+		tx_sak.sak[i] = sak[15 - i];
+
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
+						 next_pn);
+	ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak);
+	ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
+						 (tci >> 2));
+	ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an);
+
+	return ret;
+}
+
+
+static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE);
+
+	return ret;
+}
+
+
+static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE);
+
+	return ret;
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
+	.name = "macsec_qca",
+	.desc = "QCA MACsec Ethernet driver",
+	.get_ssid = macsec_qca_get_ssid,
+	.get_bssid = macsec_qca_get_bssid,
+	.get_capa = macsec_qca_get_capa,
+	.init = macsec_qca_init,
+	.deinit = macsec_qca_deinit,
+
+	.macsec_init = macsec_qca_macsec_init,
+	.macsec_deinit = macsec_qca_macsec_deinit,
+	.enable_protect_frames = macsec_qca_enable_protect_frames,
+	.set_replay_protect = macsec_qca_set_replay_protect,
+	.set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
+	.enable_controlled_port = macsec_qca_enable_controlled_port,
+	.get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
+	.get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
+	.set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
+	.get_available_receive_sc = macsec_qca_get_available_receive_sc,
+	.create_receive_sc = macsec_qca_create_receive_sc,
+	.delete_receive_sc = macsec_qca_delete_receive_sc,
+	.create_receive_sa = macsec_qca_create_receive_sa,
+	.enable_receive_sa = macsec_qca_enable_receive_sa,
+	.disable_receive_sa = macsec_qca_disable_receive_sa,
+	.get_available_transmit_sc = macsec_qca_get_available_transmit_sc,
+	.create_transmit_sc = macsec_qca_create_transmit_sc,
+	.delete_transmit_sc = macsec_qca_delete_transmit_sc,
+	.create_transmit_sa = macsec_qca_create_transmit_sa,
+	.enable_transmit_sa = macsec_qca_enable_transmit_sa,
+	.disable_transmit_sa = macsec_qca_disable_transmit_sa,
+};
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
index bb48011..1635c1f 100644
--- a/src/drivers/driver_madwifi.c
+++ b/src/drivers/driver_madwifi.c
@@ -1,5 +1,5 @@
 /*
- * WPA Supplicant - driver interaction with MADWIFI 802.11 driver
+ * hostapd - driver interaction with MADWIFI 802.11 driver
  * Copyright (c) 2004, Sam Leffler <sam@errno.com>
  * Copyright (c) 2004, Video54 Technologies
  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
@@ -7,10 +7,9 @@
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  *
- * While this driver wrapper supports both AP (hostapd) and station
- * (wpa_supplicant) operations, the station side is deprecated and
- * driver_wext.c should be used instead. This driver wrapper should only be
- * used with hostapd for AP mode functionality.
+ * This driver wrapper is only for hostapd AP mode functionality. Station
+ * (wpa_supplicant) operations with madwifi are supported by the driver_wext.c
+ * wrapper.
  */
 
 #include "includes.h"
@@ -182,7 +181,7 @@
 #endif /* MADWIFI_NG */
 		int idx = op - first;
 		if (first <= op &&
-		    idx < (int) (sizeof(opnames) / sizeof(opnames[0])) &&
+		    idx < (int) ARRAY_SIZE(opnames) &&
 		    opnames[idx])
 			perror(opnames[idx]);
 		else
@@ -322,19 +321,16 @@
 			IEEE80211_AUTH_AUTO);
 	}
 	if (!params->wpa && !params->ieee802_1x) {
-		hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-			HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+		wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
 		return -1;
 	}
 	if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
-		hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-			HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+		wpa_printf(MSG_WARNING, "Error configuring WPA state!");
 		return -1;
 	}
 	if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
 		(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
-		hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-			HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+		wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
 		return -1;
 	}
 
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 4656c1b..4953af6 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -1074,8 +1074,8 @@
 		/* Try to continue anyway */
 	}
 
-	if (params->key_mgmt_suite == KEY_MGMT_NONE ||
-	    params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 		/* Re-set WEP keys if static WEP configuration is used. */
 		int i;
 		for (i = 0; i < 4; i++) {
@@ -1102,12 +1102,12 @@
 		priv_mode = Ndis802_11PrivFilterAcceptAll;
 	} else if (params->wpa_ie[0] == WLAN_EID_RSN) {
 		priv_mode = Ndis802_11PrivFilter8021xWEP;
-		if (params->key_mgmt_suite == KEY_MGMT_PSK)
+		if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
 			auth_mode = Ndis802_11AuthModeWPA2PSK;
 		else
 			auth_mode = Ndis802_11AuthModeWPA2;
 #ifdef CONFIG_WPS
-	} else if (params->key_mgmt_suite == KEY_MGMT_WPS) {
+	} else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) {
 		auth_mode = Ndis802_11AuthModeOpen;
 		priv_mode = Ndis802_11PrivFilterAcceptAll;
 		if (params->wps == WPS_MODE_PRIVACY) {
@@ -1129,35 +1129,35 @@
 #endif /* CONFIG_WPS */
 	} else {
 		priv_mode = Ndis802_11PrivFilter8021xWEP;
-		if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE)
+		if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
 			auth_mode = Ndis802_11AuthModeWPANone;
-		else if (params->key_mgmt_suite == KEY_MGMT_PSK)
+		else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
 			auth_mode = Ndis802_11AuthModeWPAPSK;
 		else
 			auth_mode = Ndis802_11AuthModeWPA;
 	}
 
 	switch (params->pairwise_suite) {
-	case CIPHER_CCMP:
+	case WPA_CIPHER_CCMP:
 		encr = Ndis802_11Encryption3Enabled;
 		break;
-	case CIPHER_TKIP:
+	case WPA_CIPHER_TKIP:
 		encr = Ndis802_11Encryption2Enabled;
 		break;
-	case CIPHER_WEP40:
-	case CIPHER_WEP104:
+	case WPA_CIPHER_WEP40:
+	case WPA_CIPHER_WEP104:
 		encr = Ndis802_11Encryption1Enabled;
 		break;
-	case CIPHER_NONE:
+	case WPA_CIPHER_NONE:
 #ifdef CONFIG_WPS
 		if (params->wps == WPS_MODE_PRIVACY) {
 			encr = Ndis802_11Encryption1Enabled;
 			break;
 		}
 #endif /* CONFIG_WPS */
-		if (params->group_suite == CIPHER_CCMP)
+		if (params->group_suite == WPA_CIPHER_CCMP)
 			encr = Ndis802_11Encryption3Enabled;
-		else if (params->group_suite == CIPHER_TKIP)
+		else if (params->group_suite == WPA_CIPHER_TKIP)
 			encr = Ndis802_11Encryption2Enabled;
 		else
 			encr = Ndis802_11EncryptionDisabled;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index a3ff189..8ebf7d9 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
@@ -28,6 +28,8 @@
 #include "common.h"
 #include "eloop.h"
 #include "utils/list.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "l2_packet/l2_packet.h"
@@ -109,6 +111,17 @@
 #endif /* CONFIG_LIBNL20 */
 
 
+#ifdef ANDROID
+/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */
+static int android_nl_socket_set_nonblocking(struct nl_handle *handle)
+{
+	return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
+}
+#undef nl_socket_set_nonblocking
+#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h)
+#endif /* ANDROID */
+
+
 static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg)
 {
 	struct nl_handle *handle;
@@ -140,6 +153,31 @@
 }
 
 
+#if __WORDSIZE == 64
+#define ELOOP_SOCKET_INVALID	(intptr_t) 0x8888888888888889ULL
+#else
+#define ELOOP_SOCKET_INVALID	(intptr_t) 0x88888889ULL
+#endif
+
+static void nl80211_register_eloop_read(struct nl_handle **handle,
+					eloop_sock_handler handler,
+					void *eloop_data)
+{
+	nl_socket_set_nonblocking(*handle);
+	eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
+				 eloop_data, *handle);
+	*handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+}
+
+
+static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
+{
+	*handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+	eloop_unregister_read_sock(nl_socket_get_fd(*handle));
+	nl_destroy_handles(handle);
+}
+
+
 #ifndef IFF_LOWER_UP
 #define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
 #endif
@@ -193,10 +231,13 @@
 	unsigned int added_bridge:1;
 	unsigned int in_deinit:1;
 	unsigned int wdev_id_set:1;
+	unsigned int added_if:1;
+	unsigned int static_ap:1;
 
 	u8 addr[ETH_ALEN];
 
 	int freq;
+	int bandwidth;
 	int if_dynamic;
 
 	void *ctx;
@@ -226,6 +267,11 @@
 	int operstate;
 
 	int scan_complete_events;
+	enum scan_states {
+		NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
+		SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
+		SCHED_SCAN_RESULTS
+	} scan_state;
 
 	struct nl_cb *nl_cb;
 
@@ -254,7 +300,15 @@
 	unsigned int retry_auth:1;
 	unsigned int use_monitor:1;
 	unsigned int ignore_next_local_disconnect:1;
+	unsigned int ignore_next_local_deauth:1;
 	unsigned int allow_p2p_device:1;
+	unsigned int hostapd:1;
+	unsigned int start_mode_ap:1;
+	unsigned int start_iface_up:1;
+	unsigned int test_use_roc_tx:1;
+	unsigned int ignore_deauth_event:1;
+	unsigned int dfs_vendor_cmd_avail:1;
+	unsigned int have_low_prio_scan:1;
 
 	u64 remain_on_chan_cookie;
 	u64 send_action_cookie;
@@ -264,21 +318,16 @@
 	struct wpa_driver_scan_filter *filter_ssids;
 	size_t num_filter_ssids;
 
-	struct i802_bss first_bss;
+	struct i802_bss *first_bss;
 
 	int eapol_tx_sock;
 
-#ifdef HOSTAPD
 	int eapol_sock; /* socket for EAPOL frames */
 
 	int default_if_indices[16];
 	int *if_indices;
 	int num_if_indices;
 
-	int last_freq;
-	int last_freq_ht;
-#endif /* HOSTAPD */
-
 	/* From failed authentication command */
 	int auth_freq;
 	u8 auth_bssid_[ETH_ALEN];
@@ -300,8 +349,11 @@
 					    void *timeout_ctx);
 static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
 				       enum nl80211_iftype nlmode);
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq);
+
 static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+				   const u8 *set_addr, int first);
 static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
 				   const u8 *addr, int cmd, u16 reason_code,
 				   int local_state_change);
@@ -320,44 +372,44 @@
 static int android_pno_start(struct i802_bss *bss,
 			     struct wpa_driver_scan_params *params);
 static int android_pno_stop(struct i802_bss *bss);
+extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+					 size_t buf_len);
 #endif /* ANDROID */
 #ifdef ANDROID_P2P
+#ifdef ANDROID_P2P_STUB
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) {
+	return 0;
+}
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) {
+	return 0;
+}
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) {
+	return -1;
+}
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+				 const struct wpabuf *proberesp,
+				 const struct wpabuf *assocresp) {
+	return 0;
+}
+#else /* ANDROID_P2P_STUB */
 int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
 int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
 int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
 int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
-				  const struct wpabuf *proberesp,
-				  const struct wpabuf *assocresp);
+				 const struct wpabuf *proberesp,
+				 const struct wpabuf *assocresp);
+#endif /* ANDROID_P2P_STUB */
+#endif /* ANDROID_P2P */
 
-#endif
-#ifdef HOSTAPD
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
 					enum wpa_driver_if_type type,
 					const char *ifname);
-#else /* HOSTAPD */
-static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-}
 
-static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-}
-
-static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-	return 0;
-}
-#endif /* HOSTAPD */
-#ifdef ANDROID
-extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
-					 size_t buf_len);
-#endif
-
-static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
-				       struct hostapd_freq_params *freq);
+static int nl80211_set_channel(struct i802_bss *bss,
+			       struct hostapd_freq_params *freq, int set_chan);
 static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
 				     int ifindex, int disabled);
 
@@ -365,6 +417,7 @@
 static int wpa_driver_nl80211_authenticate_retry(
 	struct wpa_driver_nl80211_data *drv);
 
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
 static int i802_set_iface_flags(struct i802_bss *bss, int up);
 
 
@@ -472,6 +525,11 @@
 	C2S(NL80211_CMD_FT_EVENT)
 	C2S(NL80211_CMD_CRIT_PROTOCOL_START)
 	C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
+	C2S(NL80211_CMD_GET_COALESCE)
+	C2S(NL80211_CMD_SET_COALESCE)
+	C2S(NL80211_CMD_CHANNEL_SWITCH)
+	C2S(NL80211_CMD_VENDOR)
+	C2S(NL80211_CMD_SET_QOS_MAP)
 	default:
 		return "NL80211_CMD_UNKNOWN";
 	}
@@ -479,24 +537,45 @@
 }
 
 
+/* Converts nl80211_chan_width to a common format */
+static enum chan_width convert2width(int width)
+{
+	switch (width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+		return CHAN_WIDTH_20_NOHT;
+	case NL80211_CHAN_WIDTH_20:
+		return CHAN_WIDTH_20;
+	case NL80211_CHAN_WIDTH_40:
+		return CHAN_WIDTH_40;
+	case NL80211_CHAN_WIDTH_80:
+		return CHAN_WIDTH_80;
+	case NL80211_CHAN_WIDTH_80P80:
+		return CHAN_WIDTH_80P80;
+	case NL80211_CHAN_WIDTH_160:
+		return CHAN_WIDTH_160;
+	}
+	return CHAN_WIDTH_UNKNOWN;
+}
+
+
 static int is_ap_interface(enum nl80211_iftype nlmode)
 {
-	return (nlmode == NL80211_IFTYPE_AP ||
-		nlmode == NL80211_IFTYPE_P2P_GO);
+	return nlmode == NL80211_IFTYPE_AP ||
+		nlmode == NL80211_IFTYPE_P2P_GO;
 }
 
 
 static int is_sta_interface(enum nl80211_iftype nlmode)
 {
-	return (nlmode == NL80211_IFTYPE_STATION ||
-		nlmode == NL80211_IFTYPE_P2P_CLIENT);
+	return nlmode == NL80211_IFTYPE_STATION ||
+		nlmode == NL80211_IFTYPE_P2P_CLIENT;
 }
 
 
 static int is_p2p_net_interface(enum nl80211_iftype nlmode)
 {
-	return (nlmode == NL80211_IFTYPE_P2P_CLIENT ||
-		nlmode == NL80211_IFTYPE_P2P_GO);
+	return nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+		nlmode == NL80211_IFTYPE_P2P_GO;
 }
 
 
@@ -513,6 +592,7 @@
 	struct wpa_driver_nl80211_data *drv;
 	struct wpa_scan_results *res;
 	unsigned int assoc_freq;
+	unsigned int ibss_freq;
 	u8 assoc_bssid[ETH_ALEN];
 };
 
@@ -575,8 +655,14 @@
 		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
 			  valid_handler, valid_data);
 
-	while (err > 0)
-		nl_recvmsgs(nl_handle, cb);
+	while (err > 0) {
+		int res = nl_recvmsgs(nl_handle, cb);
+		if (res < 0) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: %s->nl_recvmsgs failed: %d",
+				   __func__, res);
+		}
+	}
  out:
 	nl_cb_put(cb);
 	nlmsg_free(msg);
@@ -594,10 +680,7 @@
 }
 
 
-#ifndef ANDROID
-static
-#endif
-int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
 			      struct nl_msg *msg,
 			      int (*valid_handler)(struct nl_msg *, void *),
 			      void *valid_data)
@@ -772,7 +855,6 @@
 }
 
 
-#ifndef HOSTAPD
 static int nl80211_get_macaddr(struct i802_bss *bss)
 {
 	struct nl_msg *msg;
@@ -794,7 +876,6 @@
 	nlmsg_free(msg);
 	return NL80211_IFTYPE_UNSPECIFIED;
 }
-#endif /* HOSTAPD */
 
 
 static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
@@ -829,10 +910,15 @@
 static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
 {
 	struct nl80211_wiphy_data *w = eloop_ctx;
+	int res;
 
 	wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
 
-	nl_recvmsgs(handle, w->nl_cb);
+	res = nl_recvmsgs(handle, w->nl_cb);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+			   __func__, res);
+	}
 }
 
 
@@ -916,8 +1002,7 @@
 		return NULL;
 	}
 
-	eloop_register_read_sock(nl_socket_get_fd(w->nl_beacons),
-				 nl80211_recv_beacons, w, w->nl_beacons);
+	nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w);
 
 	dl_list_add(&nl80211_wiphys, &w->list);
 
@@ -964,10 +1049,9 @@
 	if (!dl_list_empty(&w->bsss))
 		return;
 
-	eloop_unregister_read_sock(nl_socket_get_fd(w->nl_beacons));
+	nl80211_destroy_eloop_handle(&w->nl_beacons);
 
 	nl_cb_put(w->nl_cb);
-	nl_destroy_handles(&w->nl_beacons);
 	dl_list_del(&w->list);
 	os_free(w);
 }
@@ -995,48 +1079,55 @@
 }
 
 
-static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
-					  char *buf, size_t len, int del)
+static void wpa_driver_nl80211_event_newlink(
+	struct wpa_driver_nl80211_data *drv, char *ifname)
 {
 	union wpa_event_data event;
 
-	os_memset(&event, 0, sizeof(event));
-	if (len > sizeof(event.interface_status.ifname))
-		len = sizeof(event.interface_status.ifname) - 1;
-	os_memcpy(event.interface_status.ifname, buf, len);
-	event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
-		EVENT_INTERFACE_ADDED;
-
-	wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
-		   del ? "DEL" : "NEW",
-		   event.interface_status.ifname,
-		   del ? "removed" : "added");
-
-	if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
-		if (del) {
-			if (drv->if_removed) {
-				wpa_printf(MSG_DEBUG, "nl80211: if_removed "
-					   "already set - ignore event");
-				return;
-			}
-			drv->if_removed = 1;
-		} else {
-			if (if_nametoindex(drv->first_bss.ifname) == 0) {
-				wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
-					   "does not exist - ignore "
-					   "RTM_NEWLINK",
-					   drv->first_bss.ifname);
-				return;
-			}
-			if (!drv->if_removed) {
-				wpa_printf(MSG_DEBUG, "nl80211: if_removed "
-					   "already cleared - ignore event");
-				return;
-			}
-			drv->if_removed = 0;
+	if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+		if (if_nametoindex(drv->first_bss->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK",
+				   drv->first_bss->ifname);
+			return;
 		}
+		if (!drv->if_removed)
+			return;
+		wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event",
+			   drv->first_bss->ifname);
+		drv->if_removed = 0;
 	}
 
+	os_memset(&event, 0, sizeof(event));
+	os_strlcpy(event.interface_status.ifname, ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static void wpa_driver_nl80211_event_dellink(
+	struct wpa_driver_nl80211_data *drv, char *ifname)
+{
+	union wpa_event_data event;
+
+	if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+		if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s",
+				   ifname);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1",
+			   ifname);
+		drv->if_removed = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed",
+			   ifname);
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	os_strlcpy(event.interface_status.ifname, ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
 	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
 }
 
@@ -1053,8 +1144,8 @@
 	rta_len = RTA_ALIGN(sizeof(struct rtattr));
 	while (RTA_OK(attr, attrlen)) {
 		if (attr->rta_type == IFLA_IFNAME) {
-			if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname)
-			    == 0)
+			if (os_strcmp(((char *) attr) + rta_len,
+				      drv->first_bss->ifname) == 0)
 				return 1;
 			else
 				break;
@@ -1075,7 +1166,7 @@
 	if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
 		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
 			   "interface");
-		wpa_driver_nl80211_finish_drv_init(drv);
+		wpa_driver_nl80211_finish_drv_init(drv, NULL, 0);
 		return 1;
 	}
 
@@ -1103,21 +1194,57 @@
 {
 	struct nl80211_global *global = ctx;
 	struct wpa_driver_nl80211_data *drv;
-	int attrlen, rta_len;
+	int attrlen;
 	struct rtattr *attr;
 	u32 brid = 0;
 	char namebuf[IFNAMSIZ];
+	char ifname[IFNAMSIZ + 1];
+	char extra[100], *pos, *end;
 
 	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
 	if (!drv) {
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
-			   "ifindex %d", ifi->ifi_index);
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d",
+			   ifi->ifi_index);
 		return;
 	}
 
-	wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
-		   "(%s%s%s%s)",
-		   drv->operstate, ifi->ifi_flags,
+	extra[0] = '\0';
+	pos = extra;
+	end = pos + sizeof(extra);
+	ifname[0] = '\0';
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+	while (RTA_OK(attr, attrlen)) {
+		switch (attr->rta_type) {
+		case IFLA_IFNAME:
+			if (RTA_PAYLOAD(attr) >= IFNAMSIZ)
+				break;
+			os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+			ifname[RTA_PAYLOAD(attr)] = '\0';
+			break;
+		case IFLA_MASTER:
+			brid = nla_get_u32((struct nlattr *) attr);
+			pos += os_snprintf(pos, end - pos, " master=%u", brid);
+			break;
+		case IFLA_WIRELESS:
+			pos += os_snprintf(pos, end - pos, " wext");
+			break;
+		case IFLA_OPERSTATE:
+			pos += os_snprintf(pos, end - pos, " operstate=%u",
+					   nla_get_u32((struct nlattr *) attr));
+			break;
+		case IFLA_LINKMODE:
+			pos += os_snprintf(pos, end - pos, " linkmode=%u",
+					   nla_get_u32((struct nlattr *) attr));
+			break;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+	extra[sizeof(extra) - 1] = '\0';
+
+	wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_flags=0x%x (%s%s%s%s)",
+		   ifi->ifi_index, ifname, extra, ifi->ifi_flags,
 		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
 		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
@@ -1126,7 +1253,7 @@
 	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
 		    linux_iface_up(drv->global->ioctl_sock,
-				   drv->first_bss.ifname) > 0) {
+				   drv->first_bss->ifname) > 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
 				   "event since interface %s is up", namebuf);
 			return;
@@ -1140,24 +1267,34 @@
 			drv->if_disabled = 1;
 			wpa_supplicant_event(drv->ctx,
 					     EVENT_INTERFACE_DISABLED, NULL);
+
+			/*
+			 * Try to get drv again, since it may be removed as
+			 * part of the EVENT_INTERFACE_DISABLED handling for
+			 * dynamic interfaces
+			 */
+			drv = nl80211_find_drv(global, ifi->ifi_index,
+					       buf, len);
+			if (!drv)
+				return;
 		}
 	}
 
 	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
 		    linux_iface_up(drv->global->ioctl_sock,
-				   drv->first_bss.ifname) == 0) {
+				   drv->first_bss->ifname) == 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
 				   "event since interface %s is down",
 				   namebuf);
-		} else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+		} else if (if_nametoindex(drv->first_bss->ifname) == 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
 				   "event since interface %s does not exist",
-				   drv->first_bss.ifname);
+				   drv->first_bss->ifname);
 		} else if (drv->if_removed) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
 				   "event since interface %s is marked "
-				   "removed", drv->first_bss.ifname);
+				   "removed", drv->first_bss->ifname);
 		} else {
 			wpa_printf(MSG_DEBUG, "nl80211: Interface up");
 			drv->if_disabled = 0;
@@ -1174,24 +1311,15 @@
 	 */
 	if (drv->operstate == 1 &&
 	    (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
-	    !(ifi->ifi_flags & IFF_RUNNING))
+	    !(ifi->ifi_flags & IFF_RUNNING)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate");
 		netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
 				       -1, IF_OPER_UP);
-
-	attrlen = len;
-	attr = (struct rtattr *) buf;
-	rta_len = RTA_ALIGN(sizeof(struct rtattr));
-	while (RTA_OK(attr, attrlen)) {
-		if (attr->rta_type == IFLA_IFNAME) {
-			wpa_driver_nl80211_event_link(
-				drv,
-				((char *) attr) + rta_len,
-				attr->rta_len - rta_len, 0);
-		} else if (attr->rta_type == IFLA_MASTER)
-			brid = nla_get_u32((struct nlattr *) attr);
-		attr = RTA_NEXT(attr, attrlen);
 	}
 
+	if (ifname[0])
+		wpa_driver_nl80211_event_newlink(drv, ifname);
+
 	if (ifi->ifi_family == AF_BRIDGE && brid) {
 		/* device has been added to bridge */
 		if_indextoname(brid, namebuf);
@@ -1208,32 +1336,40 @@
 {
 	struct nl80211_global *global = ctx;
 	struct wpa_driver_nl80211_data *drv;
-	int attrlen, rta_len;
+	int attrlen;
 	struct rtattr *attr;
 	u32 brid = 0;
+	char ifname[IFNAMSIZ + 1];
 
 	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
 	if (!drv) {
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for "
-			   "foreign ifindex %d", ifi->ifi_index);
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d",
+			   ifi->ifi_index);
 		return;
 	}
 
+	ifname[0] = '\0';
+
 	attrlen = len;
 	attr = (struct rtattr *) buf;
-
-	rta_len = RTA_ALIGN(sizeof(struct rtattr));
 	while (RTA_OK(attr, attrlen)) {
-		if (attr->rta_type == IFLA_IFNAME) {
-			wpa_driver_nl80211_event_link(
-				drv,
-				((char *) attr) + rta_len,
-				attr->rta_len - rta_len, 1);
-		} else if (attr->rta_type == IFLA_MASTER)
+		switch (attr->rta_type) {
+		case IFLA_IFNAME:
+			if (RTA_PAYLOAD(attr) >= IFNAMSIZ)
+				break;
+			os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+			ifname[RTA_PAYLOAD(attr)] = '\0';
+			break;
+		case IFLA_MASTER:
 			brid = nla_get_u32((struct nlattr *) attr);
+			break;
+		}
 		attr = RTA_NEXT(attr, attrlen);
 	}
 
+	if (ifname[0])
+		wpa_driver_nl80211_event_dellink(drv, ifname);
+
 	if (ifi->ifi_family == AF_BRIDGE && brid) {
 		/* device has been removed from bridge */
 		char namebuf[IFNAMSIZ];
@@ -1294,11 +1430,12 @@
 	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
 	msg = NULL;
 	if (ret == 0) {
+		unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
+			arg.ibss_freq : arg.assoc_freq;
 		wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
-			   "associated BSS from scan results: %u MHz",
-			   arg.assoc_freq);
-		if (arg.assoc_freq)
-			drv->assoc_freq = arg.assoc_freq;
+			   "associated BSS from scan results: %u MHz", freq);
+		if (freq)
+			drv->assoc_freq = freq;
 		return drv->assoc_freq;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
@@ -1453,37 +1590,98 @@
 }
 
 
-static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
-				 struct nlattr *freq, struct nlattr *type)
+static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
 {
+	int freq1 = 0;
+
+	switch (convert2width(width)) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		return 0;
+	case CHAN_WIDTH_40:
+		freq1 = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		freq1 = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		freq1 = cf1 - 70;
+		break;
+	case CHAN_WIDTH_UNKNOWN:
+	case CHAN_WIDTH_80P80:
+		/* FIXME: implement this */
+		return 0;
+	}
+
+	return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
+}
+
+
+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 i802_bss *bss;
 	union wpa_event_data data;
 	int ht_enabled = 1;
 	int chan_offset = 0;
+	int ifidx;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
 
-	if (!freq || !type)
+	if (!freq)
 		return;
 
-	switch (nla_get_u32(type)) {
-	case NL80211_CHAN_NO_HT:
-		ht_enabled = 0;
-		break;
-	case NL80211_CHAN_HT20:
-		break;
-	case NL80211_CHAN_HT40PLUS:
-		chan_offset = 1;
-		break;
-	case NL80211_CHAN_HT40MINUS:
-		chan_offset = -1;
-		break;
+	ifidx = nla_get_u32(ifindex);
+	for (bss = drv->first_bss; bss; bss = bss->next)
+		if (bss->ifindex == ifidx)
+			break;
+
+	if (bss == NULL) {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+			   ifidx);
+		return;
 	}
 
+	if (type) {
+		switch (nla_get_u32(type)) {
+		case NL80211_CHAN_NO_HT:
+			ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			chan_offset = -1;
+			break;
+		}
+	} else if (bw && cf1) {
+		/* This can happen for example with VHT80 ch switch */
+		chan_offset = calculate_chan_offset(nla_get_u32(bw),
+						    nla_get_u32(freq),
+						    nla_get_u32(cf1),
+						    cf2 ? nla_get_u32(cf2) : 0);
+	} else {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
+	}
+
+	os_memset(&data, 0, sizeof(data));
 	data.ch_switch.freq = nla_get_u32(freq);
 	data.ch_switch.ht_enabled = ht_enabled;
 	data.ch_switch.ch_offset = chan_offset;
+	if (bw)
+		data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+	if (cf1)
+		data.ch_switch.cf1 = nla_get_u32(cf1);
+	if (cf2)
+		data.ch_switch.cf2 = nla_get_u32(cf2);
 
-	wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
+	bss->freq = data.ch_switch.freq;
+
+	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
 }
 
 
@@ -1512,19 +1710,21 @@
 }
 
 
-static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
+static void mlme_event_mgmt(struct i802_bss *bss,
 			    struct nlattr *freq, struct nlattr *sig,
 			    const u8 *frame, size_t len)
 {
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 	const struct ieee80211_mgmt *mgmt;
 	union wpa_event_data event;
 	u16 fc, stype;
 	int ssi_signal = 0;
+	int rx_freq = 0;
 
 	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
 	mgmt = (const struct ieee80211_mgmt *) frame;
 	if (len < 24) {
-		wpa_printf(MSG_DEBUG, "nl80211: Too short action frame");
+		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
 		return;
 	}
 
@@ -1536,23 +1736,19 @@
 
 	os_memset(&event, 0, sizeof(event));
 	if (freq) {
-		event.rx_action.freq = nla_get_u32(freq);
-		drv->last_mgmt_freq = event.rx_action.freq;
+		event.rx_mgmt.freq = nla_get_u32(freq);
+		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
 	}
-	if (stype == WLAN_FC_STYPE_ACTION) {
-		event.rx_action.da = mgmt->da;
-		event.rx_action.sa = mgmt->sa;
-		event.rx_action.bssid = mgmt->bssid;
-		event.rx_action.category = mgmt->u.action.category;
-		event.rx_action.data = &mgmt->u.action.category + 1;
-		event.rx_action.len = frame + len - event.rx_action.data;
-		wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
-	} else {
-		event.rx_mgmt.frame = frame;
-		event.rx_mgmt.frame_len = len;
-		event.rx_mgmt.ssi_signal = ssi_signal;
-		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-	}
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: RX frame sa=" MACSTR
+		   " freq=%d ssi_signal=%d stype=%u (%s) len=%u",
+		   MAC2STR(mgmt->sa), rx_freq, ssi_signal, stype, fc2str(fc),
+		   (unsigned int) len);
+	event.rx_mgmt.frame = frame;
+	event.rx_mgmt.frame_len = len;
+	event.rx_mgmt.ssi_signal = ssi_signal;
+	event.rx_mgmt.drv_priv = bss;
+	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
 }
 
 
@@ -1653,7 +1849,7 @@
 
 	if (type == EVENT_DISASSOC) {
 		event.disassoc_info.locally_generated =
-			!os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
 		event.disassoc_info.addr = bssid;
 		event.disassoc_info.reason_code = reason_code;
 		if (frame + len > mgmt->u.disassoc.variable) {
@@ -1662,8 +1858,21 @@
 				mgmt->u.disassoc.variable;
 		}
 	} else {
+		if (drv->ignore_deauth_event) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
+			drv->ignore_deauth_event = 0;
+			return;
+		}
 		event.deauth_info.locally_generated =
-			!os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+		if (drv->ignore_next_local_deauth) {
+			drv->ignore_next_local_deauth = 0;
+			if (event.deauth_info.locally_generated) {
+				wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
+				return;
+			}
+			wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
+		}
 		event.deauth_info.addr = bssid;
 		event.deauth_info.reason_code = reason_code;
 		if (frame + len > mgmt->u.deauth.variable) {
@@ -1776,7 +1985,7 @@
 					   nla_data(frame), nla_len(frame));
 		break;
 	case NL80211_CMD_FRAME:
-		mlme_event_mgmt(drv, freq, sig, nla_data(frame),
+		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
 				nla_len(frame));
 		break;
 	case NL80211_CMD_FRAME_TX_STATUS:
@@ -1836,7 +2045,7 @@
 static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
 				 struct nlattr *tb[])
 {
-	u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+	unsigned int freq;
 
 	if (tb[NL80211_ATTR_MAC] == NULL) {
 		wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
@@ -1845,14 +2054,17 @@
 	}
 	os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
 
-	/* register for any AUTH message */
-	nl80211_register_frame(&drv->first_bss, drv->first_bss.nl_mgmt,
-			       type, NULL, 0);
-
 	drv->associated = 1;
 	wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
 		   MAC2STR(drv->bssid));
 
+	freq = nl80211_get_assoc_freq(drv);
+	if (freq) {
+		wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
+			   freq);
+		drv->first_bss->freq = freq;
+	}
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
 }
 
@@ -1962,21 +2174,36 @@
 				&info->ssids[info->num_ssids];
 			s->ssid = nla_data(nl);
 			s->ssid_len = nla_len(nl);
+			wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'",
+				   wpa_ssid_txt(s->ssid, s->ssid_len));
 			info->num_ssids++;
 			if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
 				break;
 		}
 	}
 	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+		char msg[200], *pos, *end;
+		int res;
+
+		pos = msg;
+		end = pos + sizeof(msg);
+		*pos = '\0';
+
 		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
 		{
 			freqs[num_freqs] = nla_get_u32(nl);
+			res = os_snprintf(pos, end - pos, " %d",
+					  freqs[num_freqs]);
+			if (res > 0 && end - pos > res)
+				pos += res;
 			num_freqs++;
 			if (num_freqs == MAX_REPORT_FREQS - 1)
 				break;
 		}
 		info->freqs = freqs;
 		info->num_freqs = num_freqs;
+		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+			   msg);
 	}
 	wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
 }
@@ -2514,11 +2741,43 @@
 		return;
 
 	os_memset(&data, 0, sizeof(data));
-	data.dfs_event.freq = nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]);
-	event_type = nla_get_u8(tb[NL80211_ATTR_RADAR_EVENT]);
+	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
 
-	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
-		   data.dfs_event.freq);
+	/* Check HT params */
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+		data.dfs_event.ht_enabled = 1;
+		data.dfs_event.chan_offset = 0;
+
+		switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+		case NL80211_CHAN_NO_HT:
+			data.dfs_event.ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			data.dfs_event.chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			data.dfs_event.chan_offset = -1;
+			break;
+		}
+	}
+
+	/* Get VHT params */
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+		data.dfs_event.chan_width =
+			convert2width(nla_get_u32(
+					      tb[NL80211_ATTR_CHANNEL_WIDTH]));
+	if (tb[NL80211_ATTR_CENTER_FREQ1])
+		data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+	if (tb[NL80211_ATTR_CENTER_FREQ2])
+		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+		   data.dfs_event.freq, data.dfs_event.ht_enabled,
+		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
+		   data.dfs_event.cf1, data.dfs_event.cf2);
 
 	switch (event_type) {
 	case NL80211_RADAR_DETECTED:
@@ -2559,10 +2818,182 @@
 }
 
 
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	u32 i, count;
+	union wpa_event_data event;
+	struct wpa_freq_range *range = NULL;
+	const struct qca_avoid_freq_list *freq_range;
+
+	freq_range = (const struct qca_avoid_freq_list *) data;
+	if (len < sizeof(freq_range->count))
+		return;
+
+	count = freq_range->count;
+	if (len < sizeof(freq_range->count) +
+	    count * sizeof(struct qca_avoid_freq_range)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+			   (unsigned int) len);
+		return;
+	}
+
+	if (count > 0) {
+		range = os_calloc(count, sizeof(struct wpa_freq_range));
+		if (range == NULL)
+			return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < count; i++) {
+		unsigned int idx = event.freq_range.num;
+		range[idx].min = freq_range->range[i].start_freq;
+		range[idx].max = freq_range->range[i].end_freq;
+		wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+			   range[idx].min, range[idx].max);
+		if (range[idx].min > range[idx].max) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+			continue;
+		}
+		event.freq_range.num++;
+	}
+	event.freq_range.range = range;
+
+	wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+	os_free(range);
+}
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+				     u32 subcmd, u8 *data, size_t len)
+{
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+		qca_nl80211_avoid_freq(drv, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore unsupported QCA vendor event %u",
+			   subcmd);
+		break;
+	}
+}
+
+
+static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr **tb)
+{
+	u32 vendor_id, subcmd, wiphy = 0;
+	int wiphy_idx;
+	u8 *data = NULL;
+	size_t len = 0;
+
+	if (!tb[NL80211_ATTR_VENDOR_ID] ||
+	    !tb[NL80211_ATTR_VENDOR_SUBCMD])
+		return;
+
+	vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]);
+	subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]);
+
+	if (tb[NL80211_ATTR_WIPHY])
+		wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u",
+		   wiphy, vendor_id, subcmd);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]);
+		len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]);
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len);
+	}
+
+	wiphy_idx = nl80211_get_wiphy_index(drv->first_bss);
+	if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)",
+			   wiphy, wiphy_idx);
+		return;
+	}
+
+	switch (vendor_id) {
+	case OUI_QCA:
+		nl80211_vendor_event_qca(drv, subcmd, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+		break;
+	}
+}
+
+
+static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
+				     struct nlattr *tb[])
+{
+	union wpa_event_data data;
+	enum nl80211_reg_initiator init;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+
+	if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
+	wpa_printf(MSG_DEBUG, " * initiator=%d", init);
+	switch (init) {
+	case NL80211_REGDOM_SET_BY_CORE:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_CORE;
+		break;
+	case NL80211_REGDOM_SET_BY_USER:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+		break;
+	case NL80211_REGDOM_SET_BY_DRIVER:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER;
+		break;
+	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE;
+		break;
+	}
+
+	if (tb[NL80211_ATTR_REG_TYPE]) {
+		enum nl80211_reg_type type;
+		type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
+		wpa_printf(MSG_DEBUG, " * type=%d", type);
+		switch (type) {
+		case NL80211_REGDOM_TYPE_COUNTRY:
+			data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
+			break;
+		case NL80211_REGDOM_TYPE_WORLD:
+			data.channel_list_changed.type = REGDOM_TYPE_WORLD;
+			break;
+		case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
+			data.channel_list_changed.type =
+				REGDOM_TYPE_CUSTOM_WORLD;
+			break;
+		case NL80211_REGDOM_TYPE_INTERSECTION:
+			data.channel_list_changed.type =
+				REGDOM_TYPE_INTERSECTION;
+			break;
+		}
+	}
+
+	if (tb[NL80211_ATTR_REG_ALPHA2]) {
+		os_strlcpy(data.channel_list_changed.alpha2,
+			   nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
+			   sizeof(data.channel_list_changed.alpha2));
+		wpa_printf(MSG_DEBUG, " * alpha2=%s",
+			   data.channel_list_changed.alpha2);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
 static void do_process_drv_event(struct i802_bss *bss, int cmd,
 				 struct nlattr **tb)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	union wpa_event_data data;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
 		   cmd, nl80211_command_to_string(cmd), bss->ifname);
@@ -2570,7 +3001,7 @@
 	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
 	    (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
 	     cmd == NL80211_CMD_SCAN_ABORTED)) {
-		wpa_driver_nl80211_set_mode(&drv->first_bss,
+		wpa_driver_nl80211_set_mode(drv->first_bss,
 					    drv->ap_scan_as_station);
 		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
 	}
@@ -2578,17 +3009,32 @@
 	switch (cmd) {
 	case NL80211_CMD_TRIGGER_SCAN:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
+		drv->scan_state = SCAN_STARTED;
+		if (drv->scan_for_auth) {
+			/*
+			 * Cannot indicate EVENT_SCAN_STARTED here since we skip
+			 * EVENT_SCAN_RESULTS in scan_for_auth case and the
+			 * upper layer implementation could get confused about
+			 * scanning state.
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
+			break;
+		}
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
 		break;
 	case NL80211_CMD_START_SCHED_SCAN:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
+		drv->scan_state = SCHED_SCAN_STARTED;
 		break;
 	case NL80211_CMD_SCHED_SCAN_STOPPED:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped");
+		drv->scan_state = SCHED_SCAN_STOPPED;
 		wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
 		break;
 	case NL80211_CMD_NEW_SCAN_RESULTS:
 		wpa_dbg(drv->ctx, MSG_DEBUG,
 			"nl80211: New scan results available");
+		drv->scan_state = SCAN_COMPLETED;
 		drv->scan_complete_events = 1;
 		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
 				     drv->ctx);
@@ -2597,10 +3043,12 @@
 	case NL80211_CMD_SCHED_SCAN_RESULTS:
 		wpa_dbg(drv->ctx, MSG_DEBUG,
 			"nl80211: New sched scan results available");
+		drv->scan_state = SCHED_SCAN_RESULTS;
 		send_scan_event(drv, 0, tb);
 		break;
 	case NL80211_CMD_SCAN_ABORTED:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
+		drv->scan_state = SCAN_ABORTED;
 		/*
 		 * Need to indicate that scan results are available in order
 		 * not to make wpa_supplicant stop its scanning.
@@ -2631,8 +3079,13 @@
 				   tb[NL80211_ATTR_RESP_IE]);
 		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
-		mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
-				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+		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]);
 		break;
 	case NL80211_CMD_DISCONNECT:
 		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
@@ -2655,14 +3108,14 @@
 		nl80211_cqm_event(drv, tb);
 		break;
 	case NL80211_CMD_REG_CHANGE:
-		wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
-		wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
-				     NULL);
+		nl80211_reg_change_event(drv, tb);
 		break;
 	case NL80211_CMD_REG_BEACON_HINT:
 		wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+		os_memset(&data, 0, sizeof(data));
+		data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
 		wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
-				     NULL);
+				     &data);
 		break;
 	case NL80211_CMD_NEW_STATION:
 		nl80211_new_station_event(drv, tb);
@@ -2694,6 +3147,9 @@
 	case NL80211_CMD_STOP_AP:
 		nl80211_stop_ap(drv, tb);
 		break;
+	case NL80211_CMD_VENDOR:
+		nl80211_vendor_event(drv, tb);
+		break;
 	default:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
 			"(cmd=%d)", cmd);
@@ -2716,7 +3172,7 @@
 	if (tb[NL80211_ATTR_IFINDEX]) {
 		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
 
-		for (bss = &drv->first_bss; bss; bss = bss->next)
+		for (bss = drv->first_bss; bss; bss = bss->next)
 			if (ifidx == -1 || ifidx == bss->ifindex) {
 				do_process_drv_event(bss, gnlh->cmd, tb);
 				return NL_SKIP;
@@ -2727,7 +3183,7 @@
 	} else if (tb[NL80211_ATTR_WDEV]) {
 		u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
 		wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device");
-		for (bss = &drv->first_bss; bss; bss = bss->next) {
+		for (bss = drv->first_bss; bss; bss = bss->next) {
 			if (bss->wdev_id_set && wdev_id == bss->wdev_id) {
 				do_process_drv_event(bss, gnlh->cmd, tb);
 				return NL_SKIP;
@@ -2765,7 +3221,7 @@
 
 	dl_list_for_each_safe(drv, tmp, &global->interfaces,
 			      struct wpa_driver_nl80211_data, list) {
-		for (bss = &drv->first_bss; bss; bss = bss->next) {
+		for (bss = drv->first_bss; bss; bss = bss->next) {
 			if ((ifidx == -1 && !wdev_id_set) ||
 			    ifidx == bss->ifindex ||
 			    (wdev_id_set && bss->wdev_id_set &&
@@ -2822,10 +3278,15 @@
 					     void *handle)
 {
 	struct nl_cb *cb = eloop_ctx;
+	int res;
 
 	wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
 
-	nl_recvmsgs(handle, cb);
+	res = nl_recvmsgs(handle, cb);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+			   __func__, res);
+	}
 }
 
 
@@ -2865,6 +3326,44 @@
 }
 
 
+static int nl80211_get_country(struct nl_msg *msg, void *arg)
+{
+	char *alpha2 = arg;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No country information available");
+		return NL_SKIP;
+	}
+	os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3);
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_country(void *priv, char *alpha2)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+	alpha2[0] = '\0';
+	ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2);
+	if (!alpha2[0])
+		ret = -1;
+
+	return ret;
+}
+
+
 static int protocol_feature_handler(struct nl_msg *msg, void *arg)
 {
 	u32 *feat = arg;
@@ -2917,6 +3416,9 @@
 	unsigned int p2p_go_supported:1;
 	unsigned int p2p_client_supported:1;
 	unsigned int p2p_concurrent:1;
+	unsigned int channel_switch_supported:1;
+	unsigned int set_qos_map_supported:1;
+	unsigned int have_low_prio_scan:1;
 };
 
 
@@ -3025,10 +3527,12 @@
 	}
 
 	if (combination_has_p2p && combination_has_mgd) {
-		info->p2p_concurrent = 1;
-		info->num_multichan_concurrent =
+		unsigned int num_channels =
 			nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
-		return 1;
+
+		info->p2p_concurrent = 1;
+		if (info->num_multichan_concurrent < num_channels)
+			info->num_multichan_concurrent = num_channels;
 	}
 
 	return 0;
@@ -3074,6 +3578,71 @@
 		case NL80211_CMD_PROBE_CLIENT:
 			info->poll_command_supported = 1;
 			break;
+		case NL80211_CMD_CHANNEL_SWITCH:
+			info->channel_switch_supported = 1;
+			break;
+		case NL80211_CMD_SET_QOS_MAP:
+			info->set_qos_map_supported = 1;
+			break;
+		}
+	}
+}
+
+
+static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
+				     struct nlattr *tb)
+{
+	int i, num;
+	u32 *ciphers;
+
+	if (tb == NULL)
+		return;
+
+	num = nla_len(tb) / sizeof(u32);
+	ciphers = nla_data(tb);
+	for (i = 0; i < num; i++) {
+		u32 c = ciphers[i];
+
+		wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
+			   c >> 24, (c >> 16) & 0xff,
+			   (c >> 8) & 0xff, c & 0xff);
+		switch (c) {
+		case WLAN_CIPHER_SUITE_CCMP_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
+			break;
+		case WLAN_CIPHER_SUITE_GCMP_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
+			break;
+		case WLAN_CIPHER_SUITE_CCMP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+			break;
+		case WLAN_CIPHER_SUITE_GCMP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
+			break;
+		case WLAN_CIPHER_SUITE_TKIP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+			break;
+		case WLAN_CIPHER_SUITE_WEP104:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+			break;
+		case WLAN_CIPHER_SUITE_WEP40:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+			break;
+		case WLAN_CIPHER_SUITE_AES_CMAC:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
+			break;
+		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
+			break;
+		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
+			break;
+		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
+			break;
+		case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
+			break;
 		}
 	}
 }
@@ -3125,6 +3694,12 @@
 
 	if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
 		capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
+
+	if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+		capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+
+	if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
+		info->have_low_prio_scan = 1;
 }
 
 
@@ -3144,6 +3719,35 @@
 }
 
 
+static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
+				       struct nlattr *tb)
+{
+	struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
+
+	if (tb == NULL)
+		return;
+
+	if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
+			     tb, NULL))
+		return;
+
+	if (triggers[NL80211_WOWLAN_TRIG_ANY])
+		capa->wowlan_triggers.any = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
+		capa->wowlan_triggers.disconnect = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+		capa->wowlan_triggers.magic_pkt = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+		capa->wowlan_triggers.gtk_rekey_failure = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+		capa->wowlan_triggers.eap_identity_req = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+		capa->wowlan_triggers.four_way_handshake = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+		capa->wowlan_triggers.rfkill_release = 1;
+}
+
+
 static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -3156,7 +3760,7 @@
 		  genlmsg_attrlen(gnlh, 0), NULL);
 
 	if (tb[NL80211_ATTR_WIPHY_NAME])
-		os_strncpy(drv->phyname,
+		os_strlcpy(drv->phyname,
 			   nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
 			   sizeof(drv->phyname));
 	if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
@@ -3178,6 +3782,7 @@
 	wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
 	wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
 	wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
+	wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
 
 	if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
 		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
@@ -3230,6 +3835,49 @@
 		}
 	}
 
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl;
+		int rem;
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
+			struct nl80211_vendor_cmd_info *vinfo;
+			if (nla_len(nl) != sizeof(*vinfo)) {
+				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+				continue;
+			}
+			vinfo = nla_data(nl);
+			if (vinfo->subcmd ==
+			    QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)
+				drv->dfs_vendor_cmd_avail = 1;
+
+			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+				   vinfo->vendor_id, vinfo->subcmd);
+		}
+	}
+
+	if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
+		struct nlattr *nl;
+		int rem;
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
+			struct nl80211_vendor_cmd_info *vinfo;
+			if (nla_len(nl) != sizeof(*vinfo)) {
+				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+				continue;
+			}
+			vinfo = nla_data(nl);
+			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
+				   vinfo->vendor_id, vinfo->subcmd);
+		}
+	}
+
+	wiphy_info_wowlan_triggers(capa,
+				   tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
+
+	if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
+		capa->max_stations =
+			nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
+
 	return NL_SKIP;
 }
 
@@ -3255,7 +3903,7 @@
 		nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
 
 	NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
-	if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
+	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
 		goto nla_put_failure;
 
 	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
@@ -3288,6 +3936,9 @@
 	if (!drv->capa.max_remain_on_chan)
 		drv->capa.max_remain_on_chan = 5000;
 
+	if (info->channel_switch_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+
 	return 0;
 nla_put_failure:
 	nlmsg_free(msg);
@@ -3305,15 +3956,10 @@
 		return -1;
 
 	drv->has_capability = 1;
-	/* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
 	drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
-	drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
-		WPA_DRIVER_CAPA_ENC_WEP104 |
-		WPA_DRIVER_CAPA_ENC_TKIP |
-		WPA_DRIVER_CAPA_ENC_CCMP;
 	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
 		WPA_DRIVER_AUTH_SHARED |
 		WPA_DRIVER_AUTH_LEAP;
@@ -3322,6 +3968,15 @@
 	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
 	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 
+	/*
+	 * As all cfg80211 drivers must support cases where the AP interface is
+	 * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
+	 * case that the user space daemon has crashed, they must be able to
+	 * cleanup all stations and key entries in the AP tear down flow. Thus,
+	 * this flag can/should always be set for cfg80211 drivers.
+	 */
+	drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
+
 	if (!info.device_ap_sme) {
 		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
 
@@ -3335,21 +3990,15 @@
 	drv->device_ap_sme = info.device_ap_sme;
 	drv->poll_command_supported = info.poll_command_supported;
 	drv->data_tx_status = info.data_tx_status;
+	if (info.set_qos_map_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
+	drv->have_low_prio_scan = info.have_low_prio_scan;
 
-#ifdef ANDROID_P2P
-	if(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
-		/* Driver is new enough to support monitorless mode*/
-		wpa_printf(MSG_DEBUG, "nl80211: Driver is new "
-			  "enough to support monitor-less mode");
-		drv->use_monitor = 0;
-	}
-#else
 	/*
 	 * If poll command and tx status are supported, mac80211 is new enough
 	 * to have everything we need to not need monitor interfaces.
 	 */
 	drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
-#endif
 
 	if (drv->device_ap_sme && drv->use_monitor) {
 		/*
@@ -3469,14 +4118,24 @@
 		/* Continue without regulatory events */
 	}
 
+	ret = nl_get_multicast_id(global, "nl80211", "vendor");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(global->nl_event, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+			   "membership for vendor events: %d (%s)",
+			   ret, strerror(-ret));
+		/* Continue without vendor events */
+	}
+
 	nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
 		  no_seq_check, NULL);
 	nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
 		  process_global_event, global);
 
-	eloop_register_read_sock(nl_socket_get_fd(global->nl_event),
-				 wpa_driver_nl80211_event_receive,
-				 global->nl_cb, global->nl_event);
+	nl80211_register_eloop_read(&global->nl_event,
+				    wpa_driver_nl80211_event_receive,
+				    global->nl_cb);
 
 	return 0;
 
@@ -3520,7 +4179,7 @@
 {
 	struct wpa_driver_nl80211_data *drv = ctx;
 	wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
-	if (i802_set_iface_flags(&drv->first_bss, 1)) {
+	if (i802_set_iface_flags(drv->first_bss, 1)) {
 		wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
 			   "after rfkill unblock");
 		return;
@@ -3610,16 +4269,9 @@
 }
 
 
-/**
- * wpa_driver_nl80211_init - Initialize nl80211 driver interface
- * @ctx: context to be used when calling wpa_supplicant functions,
- * e.g., wpa_supplicant_event()
- * @ifname: interface name, e.g., wlan0
- * @global_priv: private driver global data from global_init()
- * Returns: Pointer to private data, %NULL on failure
- */
-static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
-				      void *global_priv)
+static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
+					  void *global_priv, int hostapd,
+					  const u8 *set_addr)
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct rfkill_config *rcfg;
@@ -3632,7 +4284,17 @@
 		return NULL;
 	drv->global = global_priv;
 	drv->ctx = ctx;
-	bss = &drv->first_bss;
+	drv->hostapd = !!hostapd;
+	drv->eapol_sock = -1;
+	drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
+	drv->if_indices = drv->default_if_indices;
+
+	drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
+	if (!drv->first_bss) {
+		os_free(drv);
+		return NULL;
+	}
+	bss = drv->first_bss;
 	bss->drv = drv;
 	bss->ctx = ctx;
 
@@ -3663,7 +4325,10 @@
 		os_free(rcfg);
 	}
 
-	if (wpa_driver_nl80211_finish_drv_init(drv))
+	if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0)
+		drv->start_iface_up = 1;
+
+	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1))
 		goto failed;
 
 	drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
@@ -3701,6 +4366,21 @@
 }
 
 
+/**
+ * wpa_driver_nl80211_init - Initialize nl80211 driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
+ * Returns: Pointer to private data, %NULL on failure
+ */
+static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
+				      void *global_priv)
+{
+	return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL);
+}
+
+
 static int nl80211_register_frame(struct i802_bss *bss,
 				  struct nl_handle *nl_handle,
 				  u16 type, const u8 *match, size_t match_len)
@@ -3708,15 +4388,16 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int ret = -1;
+	char buf[30];
 
 	msg = nlmsg_alloc();
 	if (!msg)
 		return -1;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p",
-		   type, nl_handle);
-	wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
-		    match, match_len);
+	buf[0] = '\0';
+	wpa_snprintf_hex(buf, sizeof(buf), match, match_len);
+	wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s",
+		   type, fc2str(type), nl_handle, buf);
 
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
 
@@ -3757,14 +4438,18 @@
 	if (bss->nl_mgmt == NULL)
 		return -1;
 
-	eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt),
-				 wpa_driver_nl80211_event_receive, bss->nl_cb,
-				 bss->nl_mgmt);
-
 	return 0;
 }
 
 
+static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss)
+{
+	nl80211_register_eloop_read(&bss->nl_mgmt,
+				    wpa_driver_nl80211_event_receive,
+				    bss->nl_cb);
+}
+
+
 static int nl80211_register_action_frame(struct i802_bss *bss,
 					 const u8 *match, size_t match_len)
 {
@@ -3777,67 +4462,100 @@
 static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = 0;
 
 	if (nl80211_alloc_mgmt_handle(bss))
 		return -1;
 	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
 		   "handle %p", bss->nl_mgmt);
 
+	if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+		u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+
+		/* register for any AUTH message */
+		nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0);
+	}
+
+#ifdef CONFIG_INTERWORKING
+	/* QoS Map Configure */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_INTERWORKING */
 #if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
 	/* GAS Initial Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
-		return -1;
+		ret = -1;
 	/* GAS Initial Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
-		return -1;
+		ret = -1;
 	/* GAS Comeback Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
-		return -1;
+		ret = -1;
 	/* GAS Comeback Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
-		return -1;
+		ret = -1;
+	/* Protected GAS Initial Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0)
+		ret = -1;
+	/* Protected GAS Initial Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0)
+		ret = -1;
+	/* Protected GAS Comeback Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0)
+		ret = -1;
+	/* Protected GAS Comeback Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
+		ret = -1;
 #endif /* CONFIG_P2P || CONFIG_INTERWORKING */
 #ifdef CONFIG_P2P
 	/* P2P Public Action */
 	if (nl80211_register_action_frame(bss,
 					  (u8 *) "\x04\x09\x50\x6f\x9a\x09",
 					  6) < 0)
-		return -1;
+		ret = -1;
 	/* P2P Action */
 	if (nl80211_register_action_frame(bss,
 					  (u8 *) "\x7f\x50\x6f\x9a\x09",
 					  5) < 0)
-		return -1;
+		ret = -1;
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_IEEE80211W
 	/* SA Query Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
-		return -1;
+		ret = -1;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_TDLS
 	if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
 		/* TDLS Discovery Response */
 		if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
 		    0)
-			return -1;
+			ret = -1;
 	}
 #endif /* CONFIG_TDLS */
 
 	/* FT Action frames */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
-		return -1;
+		ret = -1;
 	else
 		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
 			WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
 
 	/* WNM - BSS Transition Management Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
-		return -1;
+		ret = -1;
 	/* WNM-Sleep Mode Response */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
-		return -1;
+		ret = -1;
 
-	return 0;
+#ifdef CONFIG_HS20
+	/* WNM-Notification */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0)
+		return -1;
+#endif /* CONFIG_HS20 */
+
+	nl80211_mgmt_handle_register_eloop(bss);
+
+	return ret;
 }
 
 
@@ -3885,7 +4603,7 @@
  * it isn't per interface ... maybe just dump the scan
  * results periodically for OLBC?
  */
-//		WLAN_FC_STYPE_BEACON,
+		/* WLAN_FC_STYPE_BEACON, */
 	};
 	unsigned int i;
 
@@ -3894,7 +4612,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
 		   "handle %p", bss->nl_mgmt);
 
-	for (i = 0; i < sizeof(stypes) / sizeof(stypes[0]); i++) {
+	for (i = 0; i < ARRAY_SIZE(stypes); i++) {
 		if (nl80211_register_frame(bss, bss->nl_mgmt,
 					   (WLAN_FC_TYPE_MGMT << 2) |
 					   (stypes[i] << 4),
@@ -3909,10 +4627,10 @@
 	if (nl80211_get_wiphy_data_ap(bss) == NULL)
 		goto out_err;
 
+	nl80211_mgmt_handle_register_eloop(bss);
 	return 0;
 
 out_err:
-	eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
 	nl_destroy_handles(&bss->nl_mgmt);
 	return -1;
 }
@@ -3931,10 +4649,10 @@
 				   NULL, 0) < 0)
 		goto out_err;
 
+	nl80211_mgmt_handle_register_eloop(bss);
 	return 0;
 
 out_err:
-	eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
 	nl_destroy_handles(&bss->nl_mgmt);
 	return -1;
 }
@@ -3946,8 +4664,7 @@
 		return;
 	wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
 		   "(%s)", bss->nl_mgmt, reason);
-	eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
-	nl_destroy_handles(&bss->nl_mgmt);
+	nl80211_destroy_eloop_handle(&bss->nl_mgmt);
 
 	nl80211_put_wiphy_data_ap(bss);
 }
@@ -3977,7 +4694,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
 		   bss->ifname, (long long unsigned int) bss->wdev_id,
-		   strerror(ret));
+		   strerror(-ret));
 
 nla_put_failure:
 	nlmsg_free(msg);
@@ -4007,7 +4724,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
 		   start ? "Start" : "Stop",
 		   bss->ifname, (long long unsigned int) bss->wdev_id,
-		   strerror(ret));
+		   strerror(-ret));
 
 nla_put_failure:
 	nlmsg_free(msg);
@@ -4031,13 +4748,12 @@
 
 
 static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+				   const u8 *set_addr, int first)
 {
-#ifndef HOSTAPD
-	enum nl80211_iftype nlmode = NL80211_IFTYPE_STATION;
-#endif /* HOSTAPD */
-	struct i802_bss *bss = &drv->first_bss;
+	struct i802_bss *bss = drv->first_bss;
 	int send_rfkill_event = 0;
+	enum nl80211_iftype nlmode;
 
 	drv->ifindex = if_nametoindex(bss->ifname);
 	bss->ifindex = drv->ifindex;
@@ -4048,52 +4764,60 @@
 	bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
 	drv->global->if_add_wdevid_set = 0;
 
+	if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+		bss->static_ap = 1;
+
 	if (wpa_driver_nl80211_capa(drv))
 		return -1;
 
 	wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
 		   bss->ifname, drv->phyname);
 
-#ifndef HOSTAPD
-	if (bss->if_dynamic)
-		nlmode = nl80211_get_ifmode(bss);
+	if (set_addr &&
+	    (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) ||
+	     linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+				set_addr)))
+		return -1;
 
-	/*
-	 * Make sure the interface starts up in station mode unless this is a
-	 * dynamically added interface (e.g., P2P) that was already configured
-	 * with proper iftype.
-	 */
+	if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+		drv->start_mode_ap = 1;
+
+	if (drv->hostapd || bss->static_ap)
+		nlmode = NL80211_IFTYPE_AP;
+	else if (bss->if_dynamic)
+		nlmode = nl80211_get_ifmode(bss);
+	else
+		nlmode = NL80211_IFTYPE_STATION;
+
 	if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to use managed mode");
+		wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode");
 		return -1;
 	}
-	drv->nlmode = nlmode;
 
-	if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
-		int ret = nl80211_set_p2pdev(bss, 1);
-		if (ret < 0)
-			wpa_printf(MSG_ERROR, "nl80211: Could not start P2P device");
+	if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
 		nl80211_get_macaddr(bss);
-		return ret;
-	}
 
-	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
-		if (rfkill_is_blocked(drv->rfkill)) {
-			wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
-				   "interface '%s' due to rfkill",
-				   bss->ifname);
-			drv->if_disabled = 1;
-			send_rfkill_event = 1;
-		} else {
+	if (!rfkill_is_blocked(drv->rfkill)) {
+		int ret = i802_set_iface_flags(bss, 1);
+		if (ret) {
 			wpa_printf(MSG_ERROR, "nl80211: Could not set "
 				   "interface '%s' UP", bss->ifname);
-			return -1;
+			return ret;
 		}
+		if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+			return ret;
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+			   "interface '%s' due to rfkill", bss->ifname);
+		if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+			return 0;
+		drv->if_disabled = 1;
+		send_rfkill_event = 1;
 	}
 
-	netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
-			       1, IF_OPER_DORMANT);
-#endif /* HOSTAPD */
+	if (!drv->hostapd)
+		netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+				       1, IF_OPER_DORMANT);
 
 	if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
 			       bss->addr))
@@ -4116,6 +4840,8 @@
 	if (!msg)
 		return -ENOMEM;
 
+	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
+		   drv->ifindex);
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON);
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
@@ -4164,15 +4890,6 @@
 	if (is_ap_interface(drv->nlmode))
 		wpa_driver_nl80211_del_beacon(drv);
 
-#ifdef HOSTAPD
-	if (drv->last_freq_ht) {
-		/* Clear HT flags from the driver */
-		struct hostapd_freq_params freq;
-		os_memset(&freq, 0, sizeof(freq));
-		freq.freq = drv->last_freq;
-		wpa_driver_nl80211_set_freq(bss, &freq);
-	}
-
 	if (drv->eapol_sock >= 0) {
 		eloop_unregister_read_sock(drv->eapol_sock);
 		close(drv->eapol_sock);
@@ -4180,20 +4897,23 @@
 
 	if (drv->if_indices != drv->default_if_indices)
 		os_free(drv->if_indices);
-#endif /* HOSTAPD */
 
 	if (drv->disabled_11b_rates)
 		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
 
 	netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
 			       IF_OPER_UP);
+	eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx);
 	rfkill_deinit(drv->rfkill);
 
 	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 
-	(void) i802_set_iface_flags(bss, 0);
+	if (!drv->start_iface_up)
+		(void) i802_set_iface_flags(bss, 0);
 	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
-		wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
+		if (!drv->hostapd || !drv->start_mode_ap)
+			wpa_driver_nl80211_set_mode(bss,
+						    NL80211_IFTYPE_STATION);
 		nl80211_mgmt_unsubscribe(bss, "deinit");
 	} else {
 		nl80211_mgmt_unsubscribe(bss, "deinit");
@@ -4201,7 +4921,7 @@
 	}
 	nl_cb_put(drv->nl_cb);
 
-	nl80211_destroy_bss(&drv->first_bss);
+	nl80211_destroy_bss(drv->first_bss);
 
 	os_free(drv->filter_ssids);
 
@@ -4212,6 +4932,7 @@
 
 	os_free(drv->extended_capa);
 	os_free(drv->extended_capa_mask);
+	os_free(drv->first_bss);
 	os_free(drv);
 }
 
@@ -4228,7 +4949,7 @@
 {
 	struct wpa_driver_nl80211_data *drv = eloop_ctx;
 	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
-		wpa_driver_nl80211_set_mode(&drv->first_bss,
+		wpa_driver_nl80211_set_mode(drv->first_bss,
 					    drv->ap_scan_as_station);
 		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
 	}
@@ -4243,6 +4964,7 @@
 {
 	struct nl_msg *msg;
 	size_t i;
+	u32 scan_flags = 0;
 
 	msg = nlmsg_alloc();
 	if (!msg)
@@ -4299,6 +5021,20 @@
 	params->filter_ssids = NULL;
 	drv->num_filter_ssids = params->num_filter_ssids;
 
+	if (params->only_new_results) {
+		wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
+		scan_flags |= NL80211_SCAN_FLAG_FLUSH;
+	}
+
+	if (params->low_priority && drv->have_low_prio_scan) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+		scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+	}
+
+	if (scan_flags)
+		NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags);
+
 	return msg;
 
 fail:
@@ -4356,8 +5092,9 @@
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
-#ifdef HOSTAPD
-		if (is_ap_interface(drv->nlmode)) {
+		if (drv->hostapd && is_ap_interface(drv->nlmode)) {
+			enum nl80211_iftype old_mode = drv->nlmode;
+
 			/*
 			 * mac80211 does not allow scan requests in AP mode, so
 			 * try to do this in station mode.
@@ -4372,15 +5109,13 @@
 			}
 
 			/* Restore AP mode when processing scan results */
-			drv->ap_scan_as_station = drv->nlmode;
+			drv->ap_scan_as_station = old_mode;
 			ret = 0;
 		} else
 			goto nla_put_failure;
-#else /* HOSTAPD */
-		goto nla_put_failure;
-#endif /* HOSTAPD */
 	}
 
+	drv->scan_state = SCAN_REQUESTED;
 	/* Not all drivers generate "scan completed" wireless event, so try to
 	 * read results after a timeout. */
 	timeout = 10;
@@ -4456,10 +5191,20 @@
 			NLA_PUT(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
 				drv->filter_ssids[i].ssid_len,
 				drv->filter_ssids[i].ssid);
+			if (params->filter_rssi)
+				NLA_PUT_U32(msg,
+					    NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+					    params->filter_rssi);
 
 			nla_nest_end(msg, match_set_ssid);
 		}
 
+		/*
+		 * Due to backward compatibility code, newer kernels treat this
+		 * matchset (with only an RSSI filter) as the default for all
+		 * other matchsets, unless it's the only one, in which case the
+		 * matchset will actually allow all SSIDs above the RSSI.
+		 */
 		if (params->filter_rssi) {
 			struct nlattr *match_set_rssi;
 			match_set_rssi = nla_nest_start(msg, 0);
@@ -4627,6 +5372,13 @@
 			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
 				   _arg->assoc_freq);
 		}
+		if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+		    bss[NL80211_BSS_FREQUENCY]) {
+			_arg->ibss_freq =
+				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+			wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+				   _arg->ibss_freq);
+		}
 		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
 		    bss[NL80211_BSS_BSSID]) {
 			os_memcpy(_arg->assoc_bssid,
@@ -4712,7 +5464,8 @@
 	 * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
 	 * not use frequency as a separate key in the BSS table, so filter out
 	 * duplicated entries. Prefer associated BSS entry in such a case in
-	 * order to get the correct frequency into the BSS table.
+	 * order to get the correct frequency into the BSS table. Similarly,
+	 * prefer newer entries over older.
 	 */
 	for (i = 0; i < res->num; i++) {
 		const u8 *s1, *s2;
@@ -4730,8 +5483,9 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
 			   "for " MACSTR, MAC2STR(r->bssid));
 
-		if ((r->flags & WPA_SCAN_ASSOCIATED) &&
-		    !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) {
+		if (((r->flags & WPA_SCAN_ASSOCIATED) &&
+		     !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) ||
+		    r->age < res->res[i]->age) {
 			os_free(res->res[i]);
 			res->res[i] = r;
 		} else
@@ -4832,7 +5586,7 @@
 		goto nla_put_failure;
 
 	nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
-	if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
+	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
 		goto nla_put_failure;
 
 	arg.drv = drv;
@@ -4897,6 +5651,97 @@
 }
 
 
+static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
+{
+	switch (alg) {
+	case WPA_ALG_WEP:
+		if (key_len == 5)
+			return WLAN_CIPHER_SUITE_WEP40;
+		return WLAN_CIPHER_SUITE_WEP104;
+	case WPA_ALG_TKIP:
+		return WLAN_CIPHER_SUITE_TKIP;
+	case WPA_ALG_CCMP:
+		return WLAN_CIPHER_SUITE_CCMP;
+	case WPA_ALG_GCMP:
+		return WLAN_CIPHER_SUITE_GCMP;
+	case WPA_ALG_CCMP_256:
+		return WLAN_CIPHER_SUITE_CCMP_256;
+	case WPA_ALG_GCMP_256:
+		return WLAN_CIPHER_SUITE_GCMP_256;
+	case WPA_ALG_IGTK:
+		return WLAN_CIPHER_SUITE_AES_CMAC;
+	case WPA_ALG_BIP_GMAC_128:
+		return WLAN_CIPHER_SUITE_BIP_GMAC_128;
+	case WPA_ALG_BIP_GMAC_256:
+		return WLAN_CIPHER_SUITE_BIP_GMAC_256;
+	case WPA_ALG_BIP_CMAC_256:
+		return WLAN_CIPHER_SUITE_BIP_CMAC_256;
+	case WPA_ALG_SMS4:
+		return WLAN_CIPHER_SUITE_SMS4;
+	case WPA_ALG_KRK:
+		return WLAN_CIPHER_SUITE_KRK;
+	case WPA_ALG_NONE:
+	case WPA_ALG_PMK:
+		wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
+			   alg);
+		return 0;
+	}
+
+	wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d",
+		   alg);
+	return 0;
+}
+
+
+static u32 wpa_cipher_to_cipher_suite(unsigned int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP_256:
+		return WLAN_CIPHER_SUITE_CCMP_256;
+	case WPA_CIPHER_GCMP_256:
+		return WLAN_CIPHER_SUITE_GCMP_256;
+	case WPA_CIPHER_CCMP:
+		return WLAN_CIPHER_SUITE_CCMP;
+	case WPA_CIPHER_GCMP:
+		return WLAN_CIPHER_SUITE_GCMP;
+	case WPA_CIPHER_TKIP:
+		return WLAN_CIPHER_SUITE_TKIP;
+	case WPA_CIPHER_WEP104:
+		return WLAN_CIPHER_SUITE_WEP104;
+	case WPA_CIPHER_WEP40:
+		return WLAN_CIPHER_SUITE_WEP40;
+	case WPA_CIPHER_GTK_NOT_USED:
+		return WLAN_CIPHER_SUITE_NO_GROUP_ADDR;
+	}
+
+	return 0;
+}
+
+
+static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
+				       int max_suites)
+{
+	int num_suites = 0;
+
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
+		suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+
+	return num_suites;
+}
+
+
 static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
 				      enum wpa_alg alg, const u8 *addr,
 				      int key_idx, int set_tx,
@@ -4934,49 +5779,15 @@
 	} else {
 		nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY);
 		NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
-		switch (alg) {
-		case WPA_ALG_WEP:
-			if (key_len == 5)
-				NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-					    WLAN_CIPHER_SUITE_WEP40);
-			else
-				NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-					    WLAN_CIPHER_SUITE_WEP104);
-			break;
-		case WPA_ALG_TKIP:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_TKIP);
-			break;
-		case WPA_ALG_CCMP:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_CCMP);
-			break;
-		case WPA_ALG_GCMP:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_GCMP);
-			break;
-		case WPA_ALG_IGTK:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_AES_CMAC);
-			break;
-		case WPA_ALG_SMS4:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_SMS4);
-			break;
-		case WPA_ALG_KRK:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_KRK);
-			break;
-		default:
-			wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
-				   "algorithm %d", __func__, alg);
-			nlmsg_free(msg);
-			return -1;
-		}
+		wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
+		NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+			    wpa_alg_to_cipher_suite(alg, key_len));
 	}
 
-	if (seq && seq_len)
+	if (seq && seq_len) {
 		NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
+		wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len);
+	}
 
 	if (addr && !is_broadcast_ether_addr(addr)) {
 		wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
@@ -5077,33 +5888,8 @@
 
 	NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
 
-	switch (alg) {
-	case WPA_ALG_WEP:
-		if (key_len == 5)
-			NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_WEP40);
-		else
-			NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_WEP104);
-		break;
-	case WPA_ALG_TKIP:
-		NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP);
-		break;
-	case WPA_ALG_CCMP:
-		NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP);
-		break;
-	case WPA_ALG_GCMP:
-		NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP);
-		break;
-	case WPA_ALG_IGTK:
-		NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-			    WLAN_CIPHER_SUITE_AES_CMAC);
-		break;
-	default:
-		wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
-			   "algorithm %d", __func__, alg);
-		return -1;
-	}
+	NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+		    wpa_alg_to_cipher_suite(alg, key_len));
 
 	if (seq && seq_len)
 		NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq);
@@ -5238,15 +6024,25 @@
 					     const u8 *addr, int reason_code)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret;
+
+	if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+		nl80211_mark_disconnected(drv);
+		return nl80211_leave_ibss(drv);
+	}
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
 		return wpa_driver_nl80211_disconnect(drv, reason_code);
 	wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
 		   __func__, MAC2STR(addr), reason_code);
 	nl80211_mark_disconnected(drv);
-	if (drv->nlmode == NL80211_IFTYPE_ADHOC)
-		return nl80211_leave_ibss(drv);
-	return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
-				       reason_code, 0);
+	ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
+				      reason_code, 0);
+	/*
+	 * For locally generated deauthenticate, supplicant already generates a
+	 * DEAUTH event, so ignore the event from NL80211.
+	 */
+	drv->ignore_next_local_deauth = ret == 0;
+	return ret;
 }
 
 
@@ -5309,6 +6105,7 @@
 
 	is_retry = drv->retry_auth;
 	drv->retry_auth = 0;
+	drv->ignore_deauth_event = 0;
 
 	nl80211_mark_disconnected(drv);
 	os_memset(drv->auth_bssid, 0, ETH_ALEN);
@@ -5410,6 +6207,7 @@
 			 */
 			wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
 				   "after forced deauthentication");
+			drv->ignore_deauth_event = 1;
 			wpa_driver_nl80211_deauthenticate(
 				bss, params->bssid,
 				WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -5475,7 +6273,7 @@
 	struct wpa_driver_nl80211_data *drv)
 {
 	struct wpa_driver_auth_params params;
-	struct i802_bss *bss = &drv->first_bss;
+	struct i802_bss *bss = drv->first_bss;
 	int i;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
@@ -5560,22 +6358,17 @@
 	u8 channel;
 	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
 	chan->flag = 0;
+	chan->dfs_cac_ms = 0;
 	if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
 		chan->chan = channel;
 
 	if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
 		chan->flag |= HOSTAPD_CHAN_DISABLED;
-	if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
-		chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN;
-	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
-		chan->flag |= HOSTAPD_CHAN_NO_IBSS;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
+		chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_NO_IBSS;
 	if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
 		chan->flag |= HOSTAPD_CHAN_RADAR;
 
-	if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
-	    !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
-		chan->max_tx_power = nla_get_u32(
-			tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
 	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
 		enum nl80211_dfs_state state =
 			nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
@@ -5592,6 +6385,11 @@
 			break;
 		}
 	}
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
+		chan->dfs_cac_ms = nla_get_u32(
+			tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
+	}
 }
 
 
@@ -5601,8 +6399,7 @@
 	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
 		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
 		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
-		[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
-		[NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
 		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
 		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
 		[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
@@ -5898,25 +6695,29 @@
 }
 
 
-static void nl80211_reg_rule_ht40(struct nlattr *tb[],
-				  struct phy_info_arg *results)
+static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
+				      struct phy_info_arg *results)
 {
-	u32 start, end, max_bw;
 	u16 m;
 
-	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
-	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
-	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
-		return;
+	for (m = 0; m < *results->num_modes; m++) {
+		int c;
+		struct hostapd_hw_modes *mode = &results->modes[m];
 
-	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
-	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
-	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+			if ((u32) chan->freq - 10 >= start &&
+			    (u32) chan->freq + 10 <= end)
+				chan->max_tx_power = max_eirp;
+		}
+	}
+}
 
-	wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz",
-		   start, end, max_bw);
-	if (max_bw < 40)
-		return;
+
+static void nl80211_reg_rule_ht40(u32 start, u32 end,
+				  struct phy_info_arg *results)
+{
+	u16 m;
 
 	for (m = 0; m < *results->num_modes; m++) {
 		if (!(results->modes[m].ht_capab &
@@ -5954,6 +6755,76 @@
 }
 
 
+static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
+				 int end)
+{
+	int c;
+
+	for (c = 0; c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+		if (chan->freq - 10 >= start && chan->freq + 70 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_10_70;
+
+		if (chan->freq - 30 >= start && chan->freq + 50 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_30_50;
+
+		if (chan->freq - 50 >= start && chan->freq + 30 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_50_30;
+
+		if (chan->freq - 70 >= start && chan->freq + 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+	}
+}
+
+
+static void nl80211_reg_rule_vht(struct nlattr *tb[],
+				 struct phy_info_arg *results)
+{
+	u32 start, end, max_bw;
+	u16 m;
+
+	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+		return;
+
+	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+	if (max_bw < 80)
+		return;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		/* TODO: use a real VHT support indication */
+		if (!results->modes[m].vht_capab)
+			continue;
+
+		nl80211_set_vht_mode(&results->modes[m], start, end);
+	}
+}
+
+
+static const char * dfs_domain_name(enum nl80211_dfs_regions region)
+{
+	switch (region) {
+	case NL80211_DFS_UNSET:
+		return "DFS-UNSET";
+	case NL80211_DFS_FCC:
+		return "DFS-FCC";
+	case NL80211_DFS_ETSI:
+		return "DFS-ETSI";
+	case NL80211_DFS_JP:
+		return "DFS-JP";
+	default:
+		return "DFS-invalid";
+	}
+}
+
+
 static int nl80211_get_reg(struct nl_msg *msg, void *arg)
 {
 	struct phy_info_arg *results = arg;
@@ -5980,14 +6851,50 @@
 		return NL_SKIP;
 	}
 
-	wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
-		   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+	if (tb_msg[NL80211_ATTR_DFS_REGION]) {
+		enum nl80211_dfs_regions dfs_domain;
+		dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
+			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
+			   dfs_domain_name(dfs_domain));
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
+			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+	}
 
 	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
 	{
+		u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
 		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
 			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
-		nl80211_reg_rule_ht40(tb_rule, results);
+		if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+		    tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
+			continue;
+		start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+		end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+			max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
+		if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+			max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+		if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
+			flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
+
+		wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
+			   start, end, max_bw, max_eirp,
+			   flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
+			   flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
+			   flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
+			   flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
+			   "",
+			   flags & NL80211_RRF_DFS ? " (DFS)" : "",
+			   flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
+			   flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
+			   flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
+		if (max_bw >= 40)
+			nl80211_reg_rule_ht40(start, end, results);
+		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+			nl80211_reg_rule_max_eirp(start, end, max_eirp,
+						  results);
 	}
 
 	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
@@ -5997,12 +6904,19 @@
 		nl80211_reg_rule_sec(tb_rule, results);
 	}
 
+	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+	{
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		nl80211_reg_rule_vht(tb_rule, results);
+	}
+
 	return NL_SKIP;
 }
 
 
-static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv,
-				  struct phy_info_arg *results)
+static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
+					struct phy_info_arg *results)
 {
 	struct nl_msg *msg;
 
@@ -6042,10 +6956,11 @@
 		nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
 
 	NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	if (nl80211_set_iface_id(msg, bss) < 0)
+		goto nla_put_failure;
 
 	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
-		nl80211_set_ht40_flags(drv, &result);
+		nl80211_set_regulatory_flags(drv, &result);
 		return wpa_driver_nl80211_postprocess_modes(result.modes,
 							    num_modes);
 	}
@@ -6121,16 +7036,48 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	u64 cookie;
+	int res;
 
-	if (freq == 0)
+	if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) {
+		freq = nl80211_get_assoc_freq(drv);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: send_frame - Use assoc_freq=%u for IBSS",
+			   freq);
+	}
+	if (freq == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u",
+			   bss->freq);
 		freq = bss->freq;
+	}
 
-	if (drv->use_monitor)
+	if (drv->use_monitor) {
+		wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_mntr",
+			   freq, bss->freq);
 		return wpa_driver_nl80211_send_mntr(drv, data, len,
 						    encrypt, noack);
+	}
 
-	return nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
-				      &cookie, no_cck, noack, offchanok);
+	wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd");
+	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
+				     &cookie, no_cck, noack, offchanok);
+	if (res == 0 && !noack) {
+		const struct ieee80211_mgmt *mgmt;
+		u16 fc;
+
+		mgmt = (const struct ieee80211_mgmt *) data;
+		fc = le_to_host16(mgmt->frame_control);
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Update send_action_cookie from 0x%llx to 0x%llx",
+				   (long long unsigned int)
+				   drv->send_action_cookie,
+				   (long long unsigned int) cookie);
+			drv->send_action_cookie = cookie;
+		}
+	}
+
+	return res;
 }
 
 
@@ -6147,6 +7094,10 @@
 
 	mgmt = (struct ieee80211_mgmt *) data;
 	fc = le_to_host16(mgmt->frame_control);
+	wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da= " MACSTR
+		   " noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u fc=0x%x (%s) nlmode=%d",
+		   MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time,
+		   fc, fc2str(fc), drv->nlmode);
 
 	if ((is_sta_interface(drv->nlmode) ||
 	     drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
@@ -6157,16 +7108,22 @@
 		 * but it works due to the single-threaded nature
 		 * of wpa_supplicant.
 		 */
-		if (freq == 0)
+		if (freq == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d",
+				   drv->last_mgmt_freq);
 			freq = drv->last_mgmt_freq;
+		}
 		return nl80211_send_frame_cmd(bss, freq, 0,
 					      data, data_len, NULL, 1, noack,
 					      1);
 	}
 
 	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
-		if (freq == 0)
+		if (freq == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
+				   bss->freq);
 			freq = bss->freq;
+		}
 		return nl80211_send_frame_cmd(bss, freq,
 					      (int) freq == bss->freq ? 0 :
 					      wait_time,
@@ -6189,6 +7146,7 @@
 			encrypt = 0;
 	}
 
+	wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame");
 	return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
 					     noack, freq, no_cck, offchanok,
 					     wait_time);
@@ -6305,7 +7263,7 @@
 	int beacon_set;
 	int ifindex = if_nametoindex(bss->ifname);
 	int num_suites;
-	u32 suites[10];
+	u32 suites[10], suite;
 	u32 ver;
 
 	beacon_set = bss->beacon_set;
@@ -6400,17 +7358,8 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
 		   params->pairwise_ciphers);
-	num_suites = 0;
-	if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
-	if (params->pairwise_ciphers & WPA_CIPHER_GCMP)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
-	if (params->pairwise_ciphers & WPA_CIPHER_TKIP)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
-	if (params->pairwise_ciphers & WPA_CIPHER_WEP104)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
-	if (params->pairwise_ciphers & WPA_CIPHER_WEP40)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+	num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers,
+						 suites, ARRAY_SIZE(suites));
 	if (num_suites) {
 		NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
 			num_suites * sizeof(u32), suites);
@@ -6418,28 +7367,9 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
 		   params->group_cipher);
-	switch (params->group_cipher) {
-	case WPA_CIPHER_CCMP:
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-			    WLAN_CIPHER_SUITE_CCMP);
-		break;
-	case WPA_CIPHER_GCMP:
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-			    WLAN_CIPHER_SUITE_GCMP);
-		break;
-	case WPA_CIPHER_TKIP:
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-			    WLAN_CIPHER_SUITE_TKIP);
-		break;
-	case WPA_CIPHER_WEP104:
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-			    WLAN_CIPHER_SUITE_WEP104);
-		break;
-	case WPA_CIPHER_WEP40:
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-			    WLAN_CIPHER_SUITE_WEP40);
-		break;
-	}
+	suite = wpa_cipher_to_cipher_suite(params->group_cipher);
+	if (suite)
+		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite);
 
 	if (params->beacon_ies) {
 		wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
@@ -6478,6 +7408,30 @@
 		nl80211_set_bss(bss, params->cts_protect, params->preamble,
 				params->short_slot_time, params->ht_opmode,
 				params->isolate, params->basic_rates);
+		if (beacon_set && params->freq &&
+		    params->freq->bandwidth != bss->bandwidth) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Update BSS %s bandwidth: %d -> %d",
+				   bss->ifname, bss->bandwidth,
+				   params->freq->bandwidth);
+			ret = nl80211_set_channel(bss, params->freq, 1);
+			if (ret) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Frequency set failed: %d (%s)",
+					   ret, strerror(-ret));
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Frequency set succeeded for ht2040 coex");
+				bss->bandwidth = params->freq->bandwidth;
+			}
+		} else if (!beacon_set) {
+			/*
+			 * cfg80211 updates the driver on frequence change in AP
+			 * mode only at the point when beaconing is started, so
+			 * set the initial value here.
+			 */
+			bss->bandwidth = params->freq->bandwidth;
+		}
 	}
 	return ret;
  nla_put_failure:
@@ -6486,24 +7440,9 @@
 }
 
 
-static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
-				       struct hostapd_freq_params *freq)
+static int nl80211_put_freq_params(struct nl_msg *msg,
+				   struct hostapd_freq_params *freq)
 {
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct nl_msg *msg;
-	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,
-		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
 	if (freq->vht_enabled) {
 		switch (freq->bandwidth) {
@@ -6528,7 +7467,7 @@
 				    NL80211_CHAN_WIDTH_160);
 			break;
 		default:
-			return -1;
+			return -EINVAL;
 		}
 		NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
 		if (freq->center_freq2)
@@ -6550,6 +7489,34 @@
 			break;
 		}
 	}
+	return 0;
+
+nla_put_failure:
+	return -ENOBUFS;
+}
+
+
+static int nl80211_set_channel(struct i802_bss *bss,
+			       struct hostapd_freq_params *freq, int set_chan)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	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,
+		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	nl80211_cmd(drv, msg, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+		    NL80211_CMD_SET_WIPHY);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	if (nl80211_put_freq_params(msg, freq) < 0)
+		goto nla_put_failure;
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
@@ -6651,6 +7618,12 @@
 			params->vht_capabilities);
 	}
 
+	if (params->vht_opmode_enabled) {
+		wpa_printf(MSG_DEBUG, "  * opmode=%u", params->vht_opmode);
+		NLA_PUT_U8(msg, NL80211_ATTR_OPMODE_NOTIF,
+			   params->vht_opmode);
+	}
+
 	wpa_printf(MSG_DEBUG, "  * capability=0x%x", params->capability);
 	NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability);
 
@@ -6661,6 +7634,22 @@
 			params->ext_capab_len, params->ext_capab);
 	}
 
+	if (params->supp_channels) {
+		wpa_hexdump(MSG_DEBUG, "  * supported channels",
+			    params->supp_channels, params->supp_channels_len);
+		NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+			params->supp_channels_len, params->supp_channels);
+	}
+
+	if (params->supp_oper_classes) {
+		wpa_hexdump(MSG_DEBUG, "  * supported operating classes",
+			    params->supp_oper_classes,
+			    params->supp_oper_classes_len);
+		NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+			params->supp_oper_classes_len,
+			params->supp_oper_classes);
+	}
+
 	os_memset(&upd, 0, sizeof(upd));
 	upd.mask = sta_flags_nl80211(params->flags);
 	upd.set = upd.mask;
@@ -6730,11 +7719,14 @@
 				 int ifidx)
 {
 	struct nl_msg *msg;
+	struct wpa_driver_nl80211_data *drv2;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
 
 	/* stop listening for EAPOL on this interface */
-	del_ifidx(drv, ifidx);
+	dl_list_for_each(drv2, &drv->global->interfaces,
+			 struct wpa_driver_nl80211_data, list)
+		del_ifidx(drv2, ifidx);
 
 	msg = nlmsg_alloc();
 	if (!msg)
@@ -6800,7 +7792,7 @@
 		return -1;
 
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE);
-	if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
+	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
 		goto nla_put_failure;
 	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
 	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
@@ -6819,6 +7811,12 @@
 		NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
 	}
 
+	/*
+	 * Tell cfg80211 that the interface belongs to the socket that created
+	 * it, and the interface should be deleted when the socket is closed.
+	 */
+	NLA_PUT_FLAG(msg, NL80211_ATTR_IFACE_SOCKET_OWNER);
+
 	ret = send_and_recv_msgs(drv, msg, handler, arg);
 	msg = NULL;
 	if (ret) {
@@ -6839,8 +7837,17 @@
 	if (ifidx <= 0)
 		return -1;
 
-	/* start listening for EAPOL on this interface */
-	add_ifidx(drv, ifidx);
+	/*
+	 * Some virtual interfaces need to process EAPOL packets and events on
+	 * the parent interface. This is used mainly with hostapd.
+	 */
+	if (drv->hostapd ||
+	    iftype == NL80211_IFTYPE_AP_VLAN ||
+	    iftype == NL80211_IFTYPE_WDS ||
+	    iftype == NL80211_IFTYPE_MONITOR) {
+		/* start listening for EAPOL on this interface */
+		add_ifidx(drv, ifidx);
+	}
 
 	if (addr && iftype != NL80211_IFTYPE_MONITOR &&
 	    linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
@@ -6856,7 +7863,7 @@
 				const char *ifname, enum nl80211_iftype iftype,
 				const u8 *addr, int wds,
 				int (*handler)(struct nl_msg *, void *),
-				void *arg)
+				void *arg, int use_existing)
 {
 	int ret;
 
@@ -6865,6 +7872,21 @@
 
 	/* if error occurred and interface exists already */
 	if (ret == -ENFILE && if_nametoindex(ifname)) {
+		if (use_existing) {
+			wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s",
+				   ifname);
+			if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+			    linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+					       addr) < 0 &&
+			    (linux_set_iface_flags(drv->global->ioctl_sock,
+						   ifname, 0) < 0 ||
+			     linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+						addr) < 0 ||
+			     linux_set_iface_flags(drv->global->ioctl_sock,
+						   ifname, 1) < 0))
+					return -1;
+			return -ENFILE;
+		}
 		wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
 
 		/* Try to remove the interface that was already there. */
@@ -6966,12 +7988,13 @@
 
 	len = recv(sock, buf, sizeof(buf), 0);
 	if (len < 0) {
-		perror("recv");
+		wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
+			   strerror(errno));
 		return;
 	}
 
-	if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
-		printf("received invalid radiotap frame\n");
+	if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
+		wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
 		return;
 	}
 
@@ -6980,7 +8003,8 @@
 		if (ret == -ENOENT)
 			break;
 		if (ret) {
-			printf("received invalid radiotap frame (%d)\n", ret);
+			wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
+				   ret);
 			return;
 		}
 		switch (iter.this_arg_index) {
@@ -7014,11 +8038,11 @@
 		return;
 
 	if (!injected)
-		handle_frame(drv, buf + iter.max_length,
-			     len - iter.max_length, datarate, ssi_signal);
+		handle_frame(drv, buf + iter._max_length,
+			     len - iter._max_length, datarate, ssi_signal);
 	else
-		handle_tx_callback(drv->ctx, buf + iter.max_length,
-				   len - iter.max_length, !failed);
+		handle_tx_callback(drv->ctx, buf + iter._max_length,
+				   len - iter._max_length, !failed);
 }
 
 
@@ -7130,7 +8154,7 @@
 };
 
 static struct sock_fprog msock_filter = {
-	.len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]),
+	.len = ARRAY_SIZE(msock_filter_insns),
 	.filter = msock_filter_insns,
 };
 
@@ -7165,7 +8189,8 @@
 
 	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
 		       &msock_filter, sizeof(msock_filter))) {
-		perror("SO_ATTACH_FILTER");
+		wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -7176,7 +8201,10 @@
 static void nl80211_remove_monitor_interface(
 	struct wpa_driver_nl80211_data *drv)
 {
-	drv->monitor_refcount--;
+	if (drv->monitor_refcount > 0)
+		drv->monitor_refcount--;
+	wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d",
+		   drv->monitor_refcount);
 	if (drv->monitor_refcount > 0)
 		return;
 
@@ -7202,27 +8230,29 @@
 
 	if (drv->monitor_ifidx >= 0) {
 		drv->monitor_refcount++;
+		wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
+			   drv->monitor_refcount);
 		return 0;
 	}
 
-	if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) {
+	if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
 		/*
 		 * P2P interface name is of the format p2p-%s-%d. For monitor
 		 * interface name corresponding to P2P GO, replace "p2p-" with
 		 * "mon-" to retain the same interface name length and to
 		 * indicate that it is a monitor interface.
 		 */
-		snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4);
+		snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
 	} else {
 		/* Non-P2P interface with AP functionality. */
-		snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
+		snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname);
 	}
 
 	buf[IFNAMSIZ - 1] = '\0';
 
 	drv->monitor_ifidx =
 		nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
-				     0, NULL, NULL);
+				     0, NULL, NULL, 0);
 
 	if (drv->monitor_ifidx == -EOPNOTSUPP) {
 		/*
@@ -7247,7 +8277,8 @@
 	ll.sll_ifindex = drv->monitor_ifidx;
 	drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
 	if (drv->monitor_sock < 0) {
-		perror("socket[PF_PACKET,SOCK_RAW]");
+		wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
+			   strerror(errno));
 		goto error;
 	}
 
@@ -7258,7 +8289,8 @@
 	}
 
 	if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
-		perror("monitor socket bind");
+		wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
+			   strerror(errno));
 		goto error;
 	}
 
@@ -7266,16 +8298,18 @@
 	optval = 20;
 	if (setsockopt
 	    (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
-		perror("Failed to set socket priority");
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
+			   strerror(errno));
 		goto error;
 	}
 
 	if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
 				     drv, NULL)) {
-		printf("Could not register monitor read socket\n");
+		wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
 		goto error;
 	}
 
+	drv->monitor_refcount++;
 	return 0;
  error:
 	nl80211_remove_monitor_interface(drv);
@@ -7287,8 +8321,8 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Setup AP - device_ap_sme=%d "
-		   "use_monitor=%d", drv->device_ap_sme, drv->use_monitor);
+	wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
+		   bss->ifname, drv->device_ap_sme, drv->use_monitor);
 
 	/*
 	 * Disable Probe Request reporting unless we need it in this way for
@@ -7311,16 +8345,6 @@
 	    !drv->device_ap_sme)
 		return -1;
 
-#ifdef ANDROID_P2P
-	if (drv->device_ap_sme && drv->use_monitor)
-		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
-			return -1;
-
-	if (drv->use_monitor &&
-	    nl80211_create_monitor_interface(drv))
-		return -1;
-#endif
-
 	if (drv->device_ap_sme &&
 	    wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
@@ -7336,6 +8360,8 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
+	wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d",
+		   bss->ifname, drv->device_ap_sme, drv->use_monitor);
 	if (drv->device_ap_sme) {
 		wpa_driver_nl80211_probe_req_report(bss, 0);
 		if (!drv->use_monitor)
@@ -7390,19 +8416,16 @@
 	u8 *pos;
 	int res;
 	int qos = flags & WPA_STA_WMM;
-#ifndef ANDROID_P2P
+
 	if (drv->device_ap_sme || !drv->use_monitor)
-#else
-	if (drv->device_ap_sme && !drv->use_monitor)
-#endif
 		return nl80211_send_eapol_data(bss, addr, data, data_len);
 
 	len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
 		data_len;
 	hdr = os_zalloc(len);
 	if (hdr == NULL) {
-		printf("malloc() failed for i802_send_data(len=%lu)\n",
-		       (unsigned long) len);
+		wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
+			   (unsigned long) len);
 		return -1;
 	}
 
@@ -7519,14 +8542,14 @@
 		nlmode = NL80211_IFTYPE_AP;
 
 	old_mode = drv->nlmode;
-	if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode)) {
+	if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
 		nl80211_remove_monitor_interface(drv);
 		return -1;
 	}
 
-	if (wpa_driver_nl80211_set_freq(&drv->first_bss, &freq)) {
+	if (nl80211_set_channel(drv->first_bss, &freq, 0)) {
 		if (old_mode != nlmode)
-			wpa_driver_nl80211_set_mode(&drv->first_bss, old_mode);
+			wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
 		nl80211_remove_monitor_interface(drv);
 		return -1;
 	}
@@ -7558,6 +8581,12 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully");
 
 nla_put_failure:
+	if (wpa_driver_nl80211_set_mode(drv->first_bss,
+					NL80211_IFTYPE_STATION)) {
+		wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+			   "station mode");
+	}
+
 	nlmsg_free(msg);
 	return ret;
 }
@@ -7572,8 +8601,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
 
-	if (wpa_driver_nl80211_set_mode(&drv->first_bss,
-					NL80211_IFTYPE_ADHOC)) {
+	if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, params->freq)) {
 		wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
 			   "IBSS mode");
 		return -1;
@@ -7600,6 +8628,12 @@
 	wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
 
+	if (params->beacon_int > 0) {
+		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", params->beacon_int);
+		NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL,
+			    params->beacon_int);
+	}
+
 	ret = nl80211_set_conn_keys(params, msg);
 	if (ret)
 		goto nla_put_failure;
@@ -7610,10 +8644,10 @@
 		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
 	}
 
-	if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
-	    params->key_mgmt_suite == KEY_MGMT_PSK ||
-	    params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 ||
-	    params->key_mgmt_suite == KEY_MGMT_PSK_SHA256) {
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
 		wpa_printf(MSG_DEBUG, "  * control port");
 		NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
 	}
@@ -7651,40 +8685,45 @@
 }
 
 
-static int wpa_driver_nl80211_try_connect(
-	struct wpa_driver_nl80211_data *drv,
-	struct wpa_driver_associate_params *params)
+static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
+				  struct wpa_driver_associate_params *params,
+				  struct nl_msg *msg)
 {
-	struct nl_msg *msg;
-	enum nl80211_auth_type type;
-	int ret = 0;
-	int algs;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT);
-
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
 	if (params->bssid) {
 		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
 			   MAC2STR(params->bssid));
 		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
 	}
+
+	if (params->bssid_hint) {
+		wpa_printf(MSG_DEBUG, "  * bssid_hint=" MACSTR,
+			   MAC2STR(params->bssid_hint));
+		NLA_PUT(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
+			params->bssid_hint);
+	}
+
 	if (params->freq) {
 		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
 		drv->assoc_freq = params->freq;
 	} else
 		drv->assoc_freq = 0;
+
+	if (params->freq_hint) {
+		wpa_printf(MSG_DEBUG, "  * freq_hint=%d", params->freq_hint);
+		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
+			    params->freq_hint);
+	}
+
 	if (params->bg_scan_period >= 0) {
 		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
 			   params->bg_scan_period);
 		NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
 			    params->bg_scan_period);
 	}
+
 	if (params->ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 				  params->ssid, params->ssid_len);
@@ -7695,11 +8734,148 @@
 		os_memcpy(drv->ssid, params->ssid, params->ssid_len);
 		drv->ssid_len = params->ssid_len;
 	}
+
 	wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
 	if (params->wpa_ie)
 		NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
 			params->wpa_ie);
 
+	if (params->wpa_proto) {
+		enum nl80211_wpa_versions ver = 0;
+
+		if (params->wpa_proto & WPA_PROTO_WPA)
+			ver |= NL80211_WPA_VERSION_1;
+		if (params->wpa_proto & WPA_PROTO_RSN)
+			ver |= NL80211_WPA_VERSION_2;
+
+		wpa_printf(MSG_DEBUG, "  * WPA Versions 0x%x", ver);
+		NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+	}
+
+	if (params->pairwise_suite != WPA_CIPHER_NONE) {
+		u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite);
+		wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
+		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+	}
+
+	if (params->group_suite == WPA_CIPHER_GTK_NOT_USED &&
+	    !(drv->capa.enc & WPA_DRIVER_CAPA_ENC_GTK_NOT_USED)) {
+		/*
+		 * This is likely to work even though many drivers do not
+		 * advertise support for operations without GTK.
+		 */
+		wpa_printf(MSG_DEBUG, "  * skip group cipher configuration for GTK_NOT_USED due to missing driver support advertisement");
+	} else if (params->group_suite != WPA_CIPHER_NONE) {
+		u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite);
+		wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
+		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+	}
+
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
+		int mgmt = WLAN_AKM_SUITE_PSK;
+
+		switch (params->key_mgmt_suite) {
+		case WPA_KEY_MGMT_CCKM:
+			mgmt = WLAN_AKM_SUITE_CCKM;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X:
+			mgmt = WLAN_AKM_SUITE_8021X;
+			break;
+		case WPA_KEY_MGMT_FT_IEEE8021X:
+			mgmt = WLAN_AKM_SUITE_FT_8021X;
+			break;
+		case WPA_KEY_MGMT_FT_PSK:
+			mgmt = WLAN_AKM_SUITE_FT_PSK;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X_SHA256:
+			mgmt = WLAN_AKM_SUITE_8021X_SHA256;
+			break;
+		case WPA_KEY_MGMT_PSK_SHA256:
+			mgmt = WLAN_AKM_SUITE_PSK_SHA256;
+			break;
+		case WPA_KEY_MGMT_OSEN:
+			mgmt = WLAN_AKM_SUITE_OSEN;
+			break;
+		case WPA_KEY_MGMT_PSK:
+		default:
+			mgmt = WLAN_AKM_SUITE_PSK;
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "  * akm=0x%x", mgmt);
+		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
+	}
+
+	NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
+		NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
+
+	if (params->disable_ht)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+	if (params->htcaps && params->htcaps_mask) {
+		int sz = sizeof(struct ieee80211_ht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
+		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
+			    params->htcaps_mask, sz);
+		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+			params->htcaps_mask);
+	}
+
+#ifdef CONFIG_VHT_OVERRIDES
+	if (params->disable_vht) {
+		wpa_printf(MSG_DEBUG, "  * VHT disabled");
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
+	}
+
+	if (params->vhtcaps && params->vhtcaps_mask) {
+		int sz = sizeof(struct ieee80211_vht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
+		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
+			    params->vhtcaps_mask, sz);
+		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+			params->vhtcaps_mask);
+	}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	if (params->p2p)
+		wpa_printf(MSG_DEBUG, "  * P2P group");
+
+	return 0;
+nla_put_failure:
+	return -1;
+}
+
+
+static int wpa_driver_nl80211_try_connect(
+	struct wpa_driver_nl80211_data *drv,
+	struct wpa_driver_associate_params *params)
+{
+	struct nl_msg *msg;
+	enum nl80211_auth_type type;
+	int ret;
+	int algs;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT);
+
+	ret = nl80211_connect_common(drv, params, msg);
+	if (ret)
+		goto nla_put_failure;
+
 	algs = 0;
 	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
 		algs++;
@@ -7728,129 +8904,6 @@
 	NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
 
 skip_auth_type:
-	if (params->wpa_proto) {
-		enum nl80211_wpa_versions ver = 0;
-
-		if (params->wpa_proto & WPA_PROTO_WPA)
-			ver |= NL80211_WPA_VERSION_1;
-		if (params->wpa_proto & WPA_PROTO_RSN)
-			ver |= NL80211_WPA_VERSION_2;
-
-		wpa_printf(MSG_DEBUG, "  * WPA Versions 0x%x", ver);
-		NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
-	}
-
-	if (params->pairwise_suite != CIPHER_NONE) {
-		int cipher;
-
-		switch (params->pairwise_suite) {
-		case CIPHER_SMS4:
-			cipher = WLAN_CIPHER_SUITE_SMS4;
-			break;
-		case CIPHER_WEP40:
-			cipher = WLAN_CIPHER_SUITE_WEP40;
-			break;
-		case CIPHER_WEP104:
-			cipher = WLAN_CIPHER_SUITE_WEP104;
-			break;
-		case CIPHER_CCMP:
-			cipher = WLAN_CIPHER_SUITE_CCMP;
-			break;
-		case CIPHER_GCMP:
-			cipher = WLAN_CIPHER_SUITE_GCMP;
-			break;
-		case CIPHER_TKIP:
-		default:
-			cipher = WLAN_CIPHER_SUITE_TKIP;
-			break;
-		}
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
-	}
-
-	if (params->group_suite != CIPHER_NONE) {
-		int cipher;
-
-		switch (params->group_suite) {
-		case CIPHER_SMS4:
-			cipher = WLAN_CIPHER_SUITE_SMS4;
-			break;
-		case CIPHER_WEP40:
-			cipher = WLAN_CIPHER_SUITE_WEP40;
-			break;
-		case CIPHER_WEP104:
-			cipher = WLAN_CIPHER_SUITE_WEP104;
-			break;
-		case CIPHER_CCMP:
-			cipher = WLAN_CIPHER_SUITE_CCMP;
-			break;
-		case CIPHER_GCMP:
-			cipher = WLAN_CIPHER_SUITE_GCMP;
-			break;
-		case CIPHER_TKIP:
-		default:
-			cipher = WLAN_CIPHER_SUITE_TKIP;
-			break;
-		}
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
-	}
-
-	if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
-	    params->key_mgmt_suite == KEY_MGMT_PSK ||
-	    params->key_mgmt_suite == KEY_MGMT_FT_802_1X ||
-	    params->key_mgmt_suite == KEY_MGMT_FT_PSK ||
-	    params->key_mgmt_suite == KEY_MGMT_CCKM) {
-		int mgmt = WLAN_AKM_SUITE_PSK;
-
-		switch (params->key_mgmt_suite) {
-		case KEY_MGMT_CCKM:
-			mgmt = WLAN_AKM_SUITE_CCKM;
-			break;
-		case KEY_MGMT_802_1X:
-			mgmt = WLAN_AKM_SUITE_8021X;
-			break;
-		case KEY_MGMT_FT_802_1X:
-			mgmt = WLAN_AKM_SUITE_FT_8021X;
-			break;
-		case KEY_MGMT_FT_PSK:
-			mgmt = WLAN_AKM_SUITE_FT_PSK;
-			break;
-		case KEY_MGMT_PSK:
-		default:
-			mgmt = WLAN_AKM_SUITE_PSK;
-			break;
-		}
-		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
-	}
-
-#ifdef CONFIG_IEEE80211W
-	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
-		NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
-#endif /* CONFIG_IEEE80211W */
-
-	if (params->disable_ht)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
-
-	if (params->htcaps && params->htcaps_mask) {
-		int sz = sizeof(struct ieee80211_ht_capabilities);
-		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
-		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
-			params->htcaps_mask);
-	}
-
-#ifdef CONFIG_VHT_OVERRIDES
-	if (params->disable_vht) {
-		wpa_printf(MSG_DEBUG, "  * VHT disabled");
-		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
-	}
-
-	if (params->vhtcaps && params->vhtcaps_mask) {
-		int sz = sizeof(struct ieee80211_vht_capabilities);
-		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
-		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
-			params->vhtcaps_mask);
-	}
-#endif /* CONFIG_VHT_OVERRIDES */
-
 	ret = nl80211_set_conn_keys(params, msg);
 	if (ret)
 		goto nla_put_failure;
@@ -7900,7 +8953,7 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret = -1;
+	int ret;
 	struct nl_msg *msg;
 
 	if (params->mode == IEEE80211_MODE_AP)
@@ -7928,95 +8981,9 @@
 		   drv->ifindex);
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE);
 
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	if (params->bssid) {
-		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
-			   MAC2STR(params->bssid));
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
-	}
-	if (params->freq) {
-		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
-		drv->assoc_freq = params->freq;
-	} else
-		drv->assoc_freq = 0;
-	if (params->bg_scan_period >= 0) {
-		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
-			   params->bg_scan_period);
-		NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
-			    params->bg_scan_period);
-	}
-	if (params->ssid) {
-		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
-				  params->ssid, params->ssid_len);
-		NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-			params->ssid);
-		if (params->ssid_len > sizeof(drv->ssid))
-			goto nla_put_failure;
-		os_memcpy(drv->ssid, params->ssid, params->ssid_len);
-		drv->ssid_len = params->ssid_len;
-	}
-	wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
-	if (params->wpa_ie)
-		NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
-			params->wpa_ie);
-
-	if (params->pairwise_suite != CIPHER_NONE) {
-		int cipher;
-
-		switch (params->pairwise_suite) {
-		case CIPHER_WEP40:
-			cipher = WLAN_CIPHER_SUITE_WEP40;
-			break;
-		case CIPHER_WEP104:
-			cipher = WLAN_CIPHER_SUITE_WEP104;
-			break;
-		case CIPHER_CCMP:
-			cipher = WLAN_CIPHER_SUITE_CCMP;
-			break;
-		case CIPHER_GCMP:
-			cipher = WLAN_CIPHER_SUITE_GCMP;
-			break;
-		case CIPHER_TKIP:
-		default:
-			cipher = WLAN_CIPHER_SUITE_TKIP;
-			break;
-		}
-		wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
-	}
-
-	if (params->group_suite != CIPHER_NONE) {
-		int cipher;
-
-		switch (params->group_suite) {
-		case CIPHER_WEP40:
-			cipher = WLAN_CIPHER_SUITE_WEP40;
-			break;
-		case CIPHER_WEP104:
-			cipher = WLAN_CIPHER_SUITE_WEP104;
-			break;
-		case CIPHER_CCMP:
-			cipher = WLAN_CIPHER_SUITE_CCMP;
-			break;
-		case CIPHER_GCMP:
-			cipher = WLAN_CIPHER_SUITE_GCMP;
-			break;
-		case CIPHER_TKIP:
-		default:
-			cipher = WLAN_CIPHER_SUITE_TKIP;
-			break;
-		}
-		wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
-	}
-
-#ifdef CONFIG_IEEE80211W
-	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
-		NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
-#endif /* CONFIG_IEEE80211W */
-
-	NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+	ret = nl80211_connect_common(drv, params, msg);
+	if (ret)
+		goto nla_put_failure;
 
 	if (params->prev_bssid) {
 		wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
@@ -8025,33 +8992,6 @@
 			params->prev_bssid);
 	}
 
-	if (params->disable_ht)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
-
-	if (params->htcaps && params->htcaps_mask) {
-		int sz = sizeof(struct ieee80211_ht_capabilities);
-		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
-		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
-			params->htcaps_mask);
-	}
-
-#ifdef CONFIG_VHT_OVERRIDES
-	if (params->disable_vht) {
-		wpa_printf(MSG_DEBUG, "  * VHT disabled");
-		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
-	}
-
-	if (params->vhtcaps && params->vhtcaps_mask) {
-		int sz = sizeof(struct ieee80211_vht_capabilities);
-		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
-		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
-			params->vhtcaps_mask);
-	}
-#endif /* CONFIG_VHT_OVERRIDES */
-
-	if (params->p2p)
-		wpa_printf(MSG_DEBUG, "  * P2P group");
-
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -8085,7 +9025,7 @@
 		return -ENOMEM;
 
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE);
-	if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
+	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
 		goto nla_put_failure;
 	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
 
@@ -8101,26 +9041,29 @@
 }
 
 
-static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
-				       enum nl80211_iftype nlmode)
+static int wpa_driver_nl80211_set_mode_impl(
+		struct i802_bss *bss,
+		enum nl80211_iftype nlmode,
+		struct hostapd_freq_params *desired_freq_params)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ret = -1;
 	int i;
 	int was_ap = is_ap_interface(drv->nlmode);
 	int res;
+	int mode_switch_res;
 
-	res = nl80211_set_mode(drv, drv->ifindex, nlmode);
-	if (res && nlmode == nl80211_get_ifmode(bss))
-		res = 0;
+	mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+	if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
+		mode_switch_res = 0;
 
-	if (res == 0) {
+	if (mode_switch_res == 0) {
 		drv->nlmode = nlmode;
 		ret = 0;
 		goto done;
 	}
 
-	if (res == -ENODEV)
+	if (mode_switch_res == -ENODEV)
 		return -1;
 
 	if (nlmode == drv->nlmode) {
@@ -8140,21 +9083,35 @@
 		res = i802_set_iface_flags(bss, 0);
 		if (res == -EACCES || res == -ENODEV)
 			break;
-		if (res == 0) {
-			/* Try to set the mode again while the interface is
-			 * down */
-			ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
-			if (ret == -EACCES)
-				break;
-			res = i802_set_iface_flags(bss, 1);
-			if (res && !ret)
-				ret = -1;
-			else if (ret != -EBUSY)
-				break;
-		} else
+		if (res != 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
 				   "interface down");
-		os_sleep(0, 100000);
+			os_sleep(0, 100000);
+			continue;
+		}
+
+		/*
+		 * Setting the mode will fail for some drivers if the phy is
+		 * on a frequency that the mode is disallowed in.
+		 */
+		if (desired_freq_params) {
+			res = i802_set_freq(bss, desired_freq_params);
+			if (res) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Failed to set frequency on interface");
+			}
+		}
+
+		/* Try to set the mode again while the interface is down */
+		mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+		if (mode_switch_res == -EBUSY) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Delaying mode set while interface going down");
+			os_sleep(0, 100000);
+			continue;
+		}
+		ret = mode_switch_res;
+		break;
 	}
 
 	if (!ret) {
@@ -8164,6 +9121,14 @@
 		drv->ignore_if_down_event = 1;
 	}
 
+	/* Bring the interface back up */
+	res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+	if (res != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to set interface up after switching mode");
+		ret = -1;
+	}
+
 done:
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
@@ -8197,11 +9162,61 @@
 }
 
 
+static int dfs_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	int *dfs_capability_ptr = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
+			u32 val;
+			val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
+			wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
+				   val);
+			*dfs_capability_ptr = val;
+		}
+	}
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+				       enum nl80211_iftype nlmode)
+{
+	return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq)
+{
+	struct hostapd_freq_params freq_params;
+	os_memset(&freq_params, 0, sizeof(freq_params));
+	freq_params.freq = freq;
+	return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+						&freq_params);
+}
+
+
 static int wpa_driver_nl80211_get_capa(void *priv,
 				       struct wpa_driver_capa *capa)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int dfs_capability = 0;
+	int ret = 0;
+
 	if (!drv->has_capability)
 		return -1;
 	os_memcpy(capa, &drv->capa, sizeof(*capa));
@@ -8217,7 +9232,31 @@
 		capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
 	}
 
-	return 0;
+	if (drv->dfs_vendor_cmd_avail == 1) {
+		msg = nlmsg_alloc();
+		if (!msg)
+			return -ENOMEM;
+
+		nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
+
+		NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+		NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA);
+		NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			    QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY);
+
+		ret = send_and_recv_msgs(drv, msg, dfs_info_handler,
+					 &dfs_capability);
+		if (!ret) {
+			if (dfs_capability)
+				capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+		}
+	}
+
+	return ret;
+
+ nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
 }
 
 
@@ -8226,8 +9265,9 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
-	wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
-		   __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+	wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)",
+		   bss->ifname, drv->operstate, state,
+		   state ? "UP" : "DORMANT");
 	drv->operstate = state;
 	return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
 				      state ? IF_OPER_UP : IF_OPER_DORMANT);
@@ -8240,6 +9280,12 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	struct nl80211_sta_flag_update upd;
+	int ret = -ENOBUFS;
+
+	if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
+		wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
+		return 0;
+	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
 		   MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
@@ -8260,10 +9306,15 @@
 		upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
 	NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (!ret)
+		return 0;
  nla_put_failure:
 	nlmsg_free(msg);
-	return -ENOBUFS;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
+		   ret, strerror(-ret));
+	return ret;
 }
 
 
@@ -8271,12 +9322,10 @@
 static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
 {
 	struct i802_bss *bss = priv;
-	return wpa_driver_nl80211_set_freq(bss, freq);
+	return nl80211_set_channel(bss, freq, 0);
 }
 
 
-#if defined(HOSTAPD) || defined(CONFIG_AP)
-
 static inline int min_int(int a, int b)
 {
 	if (a < b)
@@ -8431,8 +9480,6 @@
 	return -ENOBUFS;
 }
 
-#endif /* HOSTAPD || CONFIG_AP */
-
 
 static int get_sta_handler(struct nl_msg *msg, void *arg)
 {
@@ -8513,8 +9560,6 @@
 }
 
 
-#if defined(HOSTAPD) || defined(CONFIG_AP)
-
 static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
 				    int cw_min, int cw_max, int burst_time)
 {
@@ -8585,6 +9630,10 @@
 	if (!msg)
 		return -ENOMEM;
 
+	wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR
+		   ", ifname=%s[%d], vlan_id=%d)",
+		   bss->ifname, if_nametoindex(bss->ifname),
+		   MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
 
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
@@ -8676,9 +9725,29 @@
 					    0);
 }
 
-#endif /* HOSTAPD || CONFIG_AP */
 
-#ifdef HOSTAPD
+static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
+{
+	char buf[200], *pos, *end;
+	int i, res;
+
+	pos = buf;
+	end = pos + sizeof(buf);
+
+	for (i = 0; i < drv->num_if_indices; i++) {
+		if (!drv->if_indices[i])
+			continue;
+		res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]);
+		if (res < 0 || res >= end - pos)
+			break;
+		pos += res;
+	}
+	*pos = '\0';
+
+	wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s",
+		   drv->num_if_indices, buf);
+}
+
 
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
 {
@@ -8687,9 +9756,15 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
 		   ifidx);
+	if (have_ifidx(drv, ifidx)) {
+		wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
+			   ifidx);
+		return;
+	}
 	for (i = 0; i < drv->num_if_indices; i++) {
 		if (drv->if_indices[i] == 0) {
 			drv->if_indices[i] = ifidx;
+			dump_ifidx(drv);
 			return;
 		}
 	}
@@ -8715,6 +9790,7 @@
 			  sizeof(drv->default_if_indices));
 	drv->if_indices[drv->num_if_indices] = ifidx;
 	drv->num_if_indices++;
+	dump_ifidx(drv);
 }
 
 
@@ -8728,6 +9804,7 @@
 			break;
 		}
 	}
+	dump_ifidx(drv);
 }
 
 
@@ -8744,7 +9821,7 @@
 
 
 static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
-                            const char *bridge_ifname, char *ifname_wds)
+			    const char *bridge_ifname, char *ifname_wds)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -8760,7 +9837,8 @@
 		if (!if_nametoindex(name)) {
 			if (nl80211_create_iface(drv, name,
 						 NL80211_IFTYPE_AP_VLAN,
-						 bss->addr, 1, NULL, NULL) < 0)
+						 bss->addr, 1, NULL, NULL, 0) <
+			    0)
 				return -1;
 			if (bridge_ifname &&
 			    linux_br_add_if(drv->global->ioctl_sock,
@@ -8778,8 +9856,8 @@
 					name);
 
 		i802_set_sta_vlan(priv, addr, bss->ifname, 0);
-		return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
-						    name);
+		nl80211_remove_iface(drv, if_nametoindex(name));
+		return 0;
 	}
 }
 
@@ -8795,7 +9873,8 @@
 	len = recvfrom(sock, buf, sizeof(buf), 0,
 		       (struct sockaddr *)&lladdr, &fromlen);
 	if (len < 0) {
-		perror("recv");
+		wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
+			   strerror(errno));
 		return;
 	}
 
@@ -8868,14 +9947,13 @@
 	int ifindex, br_ifindex;
 	int br_added = 0;
 
-	bss = wpa_driver_nl80211_init(hapd, params->ifname,
-				      params->global_priv);
+	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+					  params->global_priv, 1,
+					  params->bssid);
 	if (bss == NULL)
 		return NULL;
 
 	drv = bss->drv;
-	drv->nlmode = NL80211_IFTYPE_AP;
-	drv->eapol_sock = -1;
 
 	if (linux_br_get(brname, params->ifname) == 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
@@ -8886,8 +9964,6 @@
 		br_ifindex = 0;
 	}
 
-	drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
-	drv->if_indices = drv->default_if_indices;
 	for (i = 0; i < params->num_bridge; i++) {
 		if (params->bridge[i]) {
 			ifindex = if_nametoindex(params->bridge[i]);
@@ -8904,37 +9980,20 @@
 	/* start listening for EAPOL on the default AP interface */
 	add_ifidx(drv, drv->ifindex);
 
-	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0))
-		goto failed;
-
-	if (params->bssid) {
-		if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
-				       params->bssid))
-			goto failed;
-	}
-
-	if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
-			   "into AP mode", bss->ifname);
-		goto failed;
-	}
-
 	if (params->num_bridge && params->bridge[0] &&
 	    i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
 		goto failed;
 
-	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1))
-		goto failed;
-
 	drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
 	if (drv->eapol_sock < 0) {
-		perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
+		wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s",
+			   strerror(errno));
 		goto failed;
 	}
 
 	if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
 	{
-		printf("Could not register read socket for eapol\n");
+		wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
 		goto failed;
 	}
 
@@ -8958,8 +10017,6 @@
 	wpa_driver_nl80211_deinit(bss);
 }
 
-#endif /* HOSTAPD */
-
 
 static enum nl80211_iftype wpa_driver_nl80211_if_type(
 	enum wpa_driver_if_type type)
@@ -8990,7 +10047,7 @@
 	struct wpa_driver_nl80211_data *drv;
 	dl_list_for_each(drv, &global->interfaces,
 			 struct wpa_driver_nl80211_data, list) {
-		if (os_memcmp(addr, drv->first_bss.addr, ETH_ALEN) == 0)
+		if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0)
 			return 1;
 	}
 	return 0;
@@ -9005,9 +10062,9 @@
 	if (!drv->global)
 		return -1;
 
-	os_memcpy(new_addr, drv->first_bss.addr, ETH_ALEN);
+	os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN);
 	for (idx = 0; idx < 64; idx++) {
-		new_addr[0] = drv->first_bss.addr[0] | 0x02;
+		new_addr[0] = drv->first_bss->addr[0] | 0x02;
 		new_addr[0] ^= idx << 2;
 		if (!nl80211_addr_in_use(drv->global, new_addr))
 			break;
@@ -9055,21 +10112,13 @@
 				     const char *ifname, const u8 *addr,
 				     void *bss_ctx, void **drv_priv,
 				     char *force_ifname, u8 *if_addr,
-				     const char *bridge)
+				     const char *bridge, int use_existing)
 {
 	enum nl80211_iftype nlmode;
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ifidx;
-#ifdef HOSTAPD
-	struct i802_bss *new_bss = NULL;
-
-	if (type == WPA_IF_AP_BSS) {
-		new_bss = os_zalloc(sizeof(*new_bss));
-		if (new_bss == NULL)
-			return -1;
-	}
-#endif /* HOSTAPD */
+	int added = 1;
 
 	if (addr)
 		os_memcpy(if_addr, addr, ETH_ALEN);
@@ -9080,7 +10129,7 @@
 		os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
 		ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
 					     0, nl80211_wdev_handler,
-					     &p2pdev_info);
+					     &p2pdev_info, use_existing);
 		if (!p2pdev_info.wdev_id_set || ifidx != 0) {
 			wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
 				   ifname);
@@ -9096,11 +10145,11 @@
 			   (long long unsigned int) p2pdev_info.wdev_id);
 	} else {
 		ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
-					     0, NULL, NULL);
-		if (ifidx < 0) {
-#ifdef HOSTAPD
-			os_free(new_bss);
-#endif /* HOSTAPD */
+					     0, NULL, NULL, use_existing);
+		if (use_existing && ifidx == -ENFILE) {
+			added = 0;
+			ifidx = if_nametoindex(ifname);
+		} else if (ifidx < 0) {
 			return -1;
 		}
 	}
@@ -9110,7 +10159,8 @@
 			os_memcpy(if_addr, bss->addr, ETH_ALEN);
 		else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
 					    bss->ifname, if_addr) < 0) {
-			nl80211_remove_iface(drv, ifidx);
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
 			return -1;
 		}
 	}
@@ -9124,19 +10174,22 @@
 
 		if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
 				       new_addr) < 0) {
-			nl80211_remove_iface(drv, ifidx);
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
 			return -1;
 		}
 		if (nl80211_addr_in_use(drv->global, new_addr)) {
 			wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
 				   "for P2P group interface");
 			if (nl80211_p2p_interface_addr(drv, new_addr) < 0) {
-				nl80211_remove_iface(drv, ifidx);
+				if (added)
+					nl80211_remove_iface(drv, ifidx);
 				return -1;
 			}
 			if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
 					       new_addr) < 0) {
-				nl80211_remove_iface(drv, ifidx);
+				if (added)
+					nl80211_remove_iface(drv, ifidx);
 				return -1;
 			}
 		}
@@ -9144,20 +10197,29 @@
 	}
 #endif /* CONFIG_P2P */
 
-#ifdef HOSTAPD
-	if (bridge &&
-	    i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
-			   "interface %s to a bridge %s", ifname, bridge);
-		nl80211_remove_iface(drv, ifidx);
-		os_free(new_bss);
-		return -1;
-	}
-
 	if (type == WPA_IF_AP_BSS) {
+		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
+		if (new_bss == NULL) {
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			return -1;
+		}
+
+		if (bridge &&
+		    i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+				   "interface %s to a bridge %s",
+				   ifname, bridge);
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			os_free(new_bss);
+			return -1;
+		}
+
 		if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
 		{
-			nl80211_remove_iface(drv, ifidx);
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
 			os_free(new_bss);
 			return -1;
 		}
@@ -9165,10 +10227,11 @@
 		os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
 		new_bss->ifindex = ifidx;
 		new_bss->drv = drv;
-		new_bss->next = drv->first_bss.next;
-		new_bss->freq = drv->first_bss.freq;
+		new_bss->next = drv->first_bss->next;
+		new_bss->freq = drv->first_bss->freq;
 		new_bss->ctx = bss_ctx;
-		drv->first_bss.next = new_bss;
+		new_bss->added_if = added;
+		drv->first_bss->next = new_bss;
 		if (drv_priv)
 			*drv_priv = new_bss;
 		nl80211_init_bss(new_bss);
@@ -9177,11 +10240,21 @@
 		if (nl80211_setup_ap(new_bss))
 			return -1;
 	}
-#endif /* HOSTAPD */
 
 	if (drv->global)
 		drv->global->if_add_ifindex = ifidx;
 
+	/*
+	 * Some virtual interfaces need to process EAPOL packets and events on
+	 * the parent interface. This is used mainly with hostapd.
+	 */
+	if (ifidx > 0 &&
+	    (drv->hostapd ||
+	     nlmode == NL80211_IFTYPE_AP_VLAN ||
+	     nlmode == NL80211_IFTYPE_WDS ||
+	     nlmode == NL80211_IFTYPE_MONITOR))
+		add_ifidx(drv, ifidx);
+
 	return 0;
 }
 
@@ -9193,14 +10266,17 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ifindex = if_nametoindex(ifname);
 
-	wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d",
-		   __func__, type, ifname, ifindex);
-	if (ifindex <= 0)
-		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
+		   __func__, type, ifname, ifindex, bss->added_if);
+	if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
+		nl80211_remove_iface(drv, ifindex);
+	else if (ifindex > 0 && !bss->added_if) {
+		struct wpa_driver_nl80211_data *drv2;
+		dl_list_for_each(drv2, &drv->global->interfaces,
+				 struct wpa_driver_nl80211_data, list)
+			del_ifidx(drv2, ifindex);
+	}
 
-	nl80211_remove_iface(drv, ifindex);
-
-#ifdef HOSTAPD
 	if (type != WPA_IF_AP_BSS)
 		return 0;
 
@@ -9218,15 +10294,18 @@
 				   bss->brname, strerror(errno));
 	}
 
-	if (bss != &drv->first_bss) {
+	if (bss != drv->first_bss) {
 		struct i802_bss *tbss;
 
-		for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+		wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it");
+		for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
 			if (tbss->next == bss) {
 				tbss->next = bss->next;
 				/* Unsubscribe management frames */
 				nl80211_teardown_ap(bss);
 				nl80211_destroy_bss(bss);
+				if (!bss->added_if)
+					i802_set_iface_flags(bss, 0);
 				os_free(bss);
 				bss = NULL;
 				break;
@@ -9235,8 +10314,22 @@
 		if (bss)
 			wpa_printf(MSG_INFO, "nl80211: %s - could not find "
 				   "BSS %p in the list", __func__, bss);
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
+		nl80211_teardown_ap(bss);
+		if (!bss->added_if && !drv->first_bss->next)
+			wpa_driver_nl80211_del_beacon(drv);
+		nl80211_destroy_bss(bss);
+		if (!bss->added_if)
+			i802_set_iface_flags(bss, 0);
+		if (drv->first_bss->next) {
+			drv->first_bss = drv->first_bss->next;
+			drv->ctx = drv->first_bss->ctx;
+			os_free(bss);
+		} else {
+			wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
+		}
 	}
-#endif /* HOSTAPD */
 
 	return 0;
 }
@@ -9282,7 +10375,8 @@
 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
 	if (wait)
 		NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
-	if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+	if (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+			  drv->test_use_roc_tx))
 		NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
 	if (no_cck)
 		NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
@@ -9341,7 +10435,10 @@
 	os_memcpy(hdr->addr2, src, ETH_ALEN);
 	os_memcpy(hdr->addr3, bssid, ETH_ALEN);
 
-	if (is_ap_interface(drv->nlmode))
+	if (is_ap_interface(drv->nlmode) &&
+	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+	     (int) freq == bss->freq || drv->device_ap_sme ||
+	     !drv->use_monitor))
 		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
 						   0, freq, no_cck, 1,
 						   wait_time);
@@ -9473,7 +10570,8 @@
 
 	if (!report) {
 		if (bss->nl_preq && drv->device_ap_sme &&
-		    is_ap_interface(drv->nlmode)) {
+		    is_ap_interface(drv->nlmode) && !bss->in_deinit &&
+		    !bss->static_ap) {
 			/*
 			 * Do not disable Probe Request reporting that was
 			 * enabled in nl80211_setup_ap().
@@ -9484,9 +10582,7 @@
 		} else if (bss->nl_preq) {
 			wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
 				   "reporting nl_preq=%p", bss->nl_preq);
-			eloop_unregister_read_sock(
-				nl_socket_get_fd(bss->nl_preq));
-			nl_destroy_handles(&bss->nl_preq);
+			nl80211_destroy_eloop_handle(&bss->nl_preq);
 		}
 		return 0;
 	}
@@ -9509,9 +10605,9 @@
 				   NULL, 0) < 0)
 		goto out_err;
 
-	eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq),
-				 wpa_driver_nl80211_event_receive, bss->nl_cb,
-				 bss->nl_preq);
+	nl80211_register_eloop_read(&bss->nl_preq,
+				    wpa_driver_nl80211_event_receive,
+				    bss->nl_cb);
 
 	return 0;
 
@@ -9712,27 +10808,6 @@
 }
 
 
-/* Converts nl80211_chan_width to a common format */
-static enum chan_width convert2width(int width)
-{
-	switch (width) {
-	case NL80211_CHAN_WIDTH_20_NOHT:
-		return CHAN_WIDTH_20_NOHT;
-	case NL80211_CHAN_WIDTH_20:
-		return CHAN_WIDTH_20;
-	case NL80211_CHAN_WIDTH_40:
-		return CHAN_WIDTH_40;
-	case NL80211_CHAN_WIDTH_80:
-		return CHAN_WIDTH_80;
-	case NL80211_CHAN_WIDTH_80P80:
-		return CHAN_WIDTH_80P80;
-	case NL80211_CHAN_WIDTH_160:
-		return CHAN_WIDTH_160;
-	}
-	return CHAN_WIDTH_UNKNOWN;
-}
-
-
 static int get_channel_width(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -9818,19 +10893,15 @@
 			 struct wpa_driver_nl80211_data, list) {
 		if (drv == driver ||
 		    os_strcmp(drv->phyname, driver->phyname) != 0 ||
-#ifdef ANDROID_P2P
-		    (!driver->associated && !is_ap_interface(driver->nlmode)))
-#else
 		    !driver->associated)
-#endif
 			continue;
 
 		wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
 			   MACSTR,
-			   driver->phyname, driver->first_bss.ifname,
-			   MAC2STR(driver->first_bss.addr));
+			   driver->phyname, driver->first_bss->ifname,
+			   MAC2STR(driver->first_bss->addr));
 		if (is_ap_interface(driver->nlmode))
-			freq = driver->first_bss.freq;
+			freq = driver->first_bss->freq;
 		else
 			freq = nl80211_get_assoc_freq(driver);
 		wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
@@ -9876,17 +10947,26 @@
 		struct wpa_driver_nl80211_data *drv = bss->drv;
 		drv->allow_p2p_device = 1;
 	}
+#endif /* CONFIG_P2P */
 
-#ifdef ANDROID_P2P
-	if(os_strstr(param, "use_multi_chan_concurrent=1")) {
+	if (os_strstr(param, "use_monitor=1")) {
 		struct i802_bss *bss = priv;
 		struct wpa_driver_nl80211_data *drv = bss->drv;
-		wpa_printf(MSG_DEBUG, "nl80211: Use Multi channel "
-			   "concurrency");
-		drv->capa.num_multichan_concurrent = 2;
+		drv->use_monitor = 1;
 	}
-#endif
-#endif /* CONFIG_P2P */
+
+	if (os_strstr(param, "force_connect_cmd=1")) {
+		struct i802_bss *bss = priv;
+		struct wpa_driver_nl80211_data *drv = bss->drv;
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
+	}
+
+	if (os_strstr(param, "no_offchannel_tx=1")) {
+		struct i802_bss *bss = priv;
+		struct wpa_driver_nl80211_data *drv = bss->drv;
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+		drv->test_use_roc_tx = 1;
+	}
 
 	return 0;
 }
@@ -9922,7 +11002,8 @@
 
 	global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (global->ioctl_sock < 0) {
-		perror("socket(PF_INET,SOCK_DGRAM)");
+		wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
+			   strerror(errno));
 		goto err;
 	}
 
@@ -9950,11 +11031,8 @@
 
 	nl_destroy_handles(&global->nl);
 
-	if (global->nl_event) {
-		eloop_unregister_read_sock(
-			nl_socket_get_fd(global->nl_event));
-		nl_destroy_handles(&global->nl_event);
-	}
+	if (global->nl_event)
+		nl80211_destroy_eloop_handle(&global->nl_event);
 
 	nl_cb_put(global->nl_cb);
 
@@ -10122,6 +11200,9 @@
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
 
+	if (!tb[NL80211_ATTR_IFINDEX])
+		return NL_SKIP;
+
 	ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
 
 	if (!tb[NL80211_ATTR_SURVEY_INFO])
@@ -10328,12 +11409,13 @@
 	wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
 		   "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
 
-	if (opp_ps != -1 || ctwindow != -1)
+	if (opp_ps != -1 || ctwindow != -1) {
 #ifdef ANDROID_P2P
 		wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
-#else
+#else /* ANDROID_P2P */
 		return -1; /* Not yet supported */
-#endif
+#endif /* ANDROID_P2P */
+	}
 
 	if (legacy_ps == -1)
 		return 0;
@@ -10344,14 +11426,18 @@
 }
 
 
-static int nl80211_start_radar_detection(void *priv, int freq)
+static int nl80211_start_radar_detection(void *priv,
+					 struct hostapd_freq_params *freq)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int ret;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC)");
+	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,
+		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
+
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
 		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
 			   "detection");
@@ -10364,10 +11450,53 @@
 
 	nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT);
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
 
-	/* only HT20 is supported at this point */
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20);
+	if (freq->vht_enabled) {
+		switch (freq->bandwidth) {
+		case 20:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_20);
+			break;
+		case 40:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_40);
+			break;
+		case 80:
+			if (freq->center_freq2)
+				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+					    NL80211_CHAN_WIDTH_80P80);
+			else
+				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+					    NL80211_CHAN_WIDTH_80);
+			break;
+		case 160:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_160);
+			break;
+		default:
+			return -1;
+		}
+		NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
+		if (freq->center_freq2)
+			NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2,
+				    freq->center_freq2);
+	} else if (freq->ht_enabled) {
+		switch (freq->sec_channel_offset) {
+		case -1:
+			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+				    NL80211_CHAN_HT40MINUS);
+			break;
+		case 1:
+			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+				    NL80211_CHAN_HT40PLUS);
+			break;
+		default:
+			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+				    NL80211_CHAN_HT20);
+			break;
+		}
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret == 0)
@@ -10382,7 +11511,7 @@
 
 static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
 				  u8 dialog_token, u16 status_code,
-				  const u8 *buf, size_t len)
+				  u32 peer_capab, const u8 *buf, size_t len)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -10404,6 +11533,15 @@
 	NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code);
 	NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token);
 	NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code);
+	if (peer_capab) {
+		/*
+		 * The internal enum tdls_peer_capability definition is
+		 * currently identical with the nl80211 enum
+		 * nl80211_tdls_peer_capability, so no conversion is needed
+		 * here.
+		 */
+		NLA_PUT_U32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY, peer_capab);
+	}
 	NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -10570,7 +11708,7 @@
 
 	memset(&ifr, 0, sizeof(ifr));
 	memset(&priv_cmd, 0, sizeof(priv_cmd));
-	os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
 
 	priv_cmd.buf = buf;
 	priv_cmd.used_len = bp;
@@ -10667,14 +11805,12 @@
 }
 
 
-#if defined(HOSTAPD) || defined(CONFIG_AP)
 static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr,
 				       const char *ifname, int vlan_id)
 {
 	struct i802_bss *bss = priv;
 	return i802_set_sta_vlan(bss, addr, ifname, vlan_id);
 }
-#endif /* HOSTAPD || CONFIG_AP */
 
 
 static int driver_nl80211_read_sta_data(void *priv,
@@ -10751,6 +11887,471 @@
 }
 
 
+static const char * scan_state_str(enum scan_states scan_state)
+{
+	switch (scan_state) {
+	case NO_SCAN:
+		return "NO_SCAN";
+	case SCAN_REQUESTED:
+		return "SCAN_REQUESTED";
+	case SCAN_STARTED:
+		return "SCAN_STARTED";
+	case SCAN_COMPLETED:
+		return "SCAN_COMPLETED";
+	case SCAN_ABORTED:
+		return "SCAN_ABORTED";
+	case SCHED_SCAN_STARTED:
+		return "SCHED_SCAN_STARTED";
+	case SCHED_SCAN_STOPPED:
+		return "SCHED_SCAN_STOPPED";
+	case SCHED_SCAN_RESULTS:
+		return "SCHED_SCAN_RESULTS";
+	}
+
+	return "??";
+}
+
+
+static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int res;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	res = os_snprintf(pos, end - pos,
+			  "ifindex=%d\n"
+			  "ifname=%s\n"
+			  "brname=%s\n"
+			  "addr=" MACSTR "\n"
+			  "freq=%d\n"
+			  "%s%s%s%s%s",
+			  bss->ifindex,
+			  bss->ifname,
+			  bss->brname,
+			  MAC2STR(bss->addr),
+			  bss->freq,
+			  bss->beacon_set ? "beacon_set=1\n" : "",
+			  bss->added_if_into_bridge ?
+			  "added_if_into_bridge=1\n" : "",
+			  bss->added_bridge ? "added_bridge=1\n" : "",
+			  bss->in_deinit ? "in_deinit=1\n" : "",
+			  bss->if_dynamic ? "if_dynamic=1\n" : "");
+	if (res < 0 || res >= end - pos)
+		return pos - buf;
+	pos += res;
+
+	if (bss->wdev_id_set) {
+		res = os_snprintf(pos, end - pos, "wdev_id=%llu\n",
+				  (unsigned long long) bss->wdev_id);
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+	}
+
+	res = os_snprintf(pos, end - pos,
+			  "phyname=%s\n"
+			  "drv_ifindex=%d\n"
+			  "operstate=%d\n"
+			  "scan_state=%s\n"
+			  "auth_bssid=" MACSTR "\n"
+			  "auth_attempt_bssid=" MACSTR "\n"
+			  "bssid=" MACSTR "\n"
+			  "prev_bssid=" MACSTR "\n"
+			  "associated=%d\n"
+			  "assoc_freq=%u\n"
+			  "monitor_sock=%d\n"
+			  "monitor_ifidx=%d\n"
+			  "monitor_refcount=%d\n"
+			  "last_mgmt_freq=%u\n"
+			  "eapol_tx_sock=%d\n"
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  drv->phyname,
+			  drv->ifindex,
+			  drv->operstate,
+			  scan_state_str(drv->scan_state),
+			  MAC2STR(drv->auth_bssid),
+			  MAC2STR(drv->auth_attempt_bssid),
+			  MAC2STR(drv->bssid),
+			  MAC2STR(drv->prev_bssid),
+			  drv->associated,
+			  drv->assoc_freq,
+			  drv->monitor_sock,
+			  drv->monitor_ifidx,
+			  drv->monitor_refcount,
+			  drv->last_mgmt_freq,
+			  drv->eapol_tx_sock,
+			  drv->ignore_if_down_event ?
+			  "ignore_if_down_event=1\n" : "",
+			  drv->scan_complete_events ?
+			  "scan_complete_events=1\n" : "",
+			  drv->disabled_11b_rates ?
+			  "disabled_11b_rates=1\n" : "",
+			  drv->pending_remain_on_chan ?
+			  "pending_remain_on_chan=1\n" : "",
+			  drv->in_interface_list ? "in_interface_list=1\n" : "",
+			  drv->device_ap_sme ? "device_ap_sme=1\n" : "",
+			  drv->poll_command_supported ?
+			  "poll_command_supported=1\n" : "",
+			  drv->data_tx_status ? "data_tx_status=1\n" : "",
+			  drv->scan_for_auth ? "scan_for_auth=1\n" : "",
+			  drv->retry_auth ? "retry_auth=1\n" : "",
+			  drv->use_monitor ? "use_monitor=1\n" : "",
+			  drv->ignore_next_local_disconnect ?
+			  "ignore_next_local_disconnect=1\n" : "",
+			  drv->ignore_next_local_deauth ?
+			  "ignore_next_local_deauth=1\n" : "",
+			  drv->allow_p2p_device ? "allow_p2p_device=1\n" : "");
+	if (res < 0 || res >= end - pos)
+		return pos - buf;
+	pos += res;
+
+	if (drv->has_capability) {
+		res = os_snprintf(pos, end - pos,
+				  "capa.key_mgmt=0x%x\n"
+				  "capa.enc=0x%x\n"
+				  "capa.auth=0x%x\n"
+				  "capa.flags=0x%x\n"
+				  "capa.max_scan_ssids=%d\n"
+				  "capa.max_sched_scan_ssids=%d\n"
+				  "capa.sched_scan_supported=%d\n"
+				  "capa.max_match_sets=%d\n"
+				  "capa.max_remain_on_chan=%u\n"
+				  "capa.max_stations=%u\n"
+				  "capa.probe_resp_offloads=0x%x\n"
+				  "capa.max_acl_mac_addrs=%u\n"
+				  "capa.num_multichan_concurrent=%u\n",
+				  drv->capa.key_mgmt,
+				  drv->capa.enc,
+				  drv->capa.auth,
+				  drv->capa.flags,
+				  drv->capa.max_scan_ssids,
+				  drv->capa.max_sched_scan_ssids,
+				  drv->capa.sched_scan_supported,
+				  drv->capa.max_match_sets,
+				  drv->capa.max_remain_on_chan,
+				  drv->capa.max_stations,
+				  drv->capa.probe_resp_offloads,
+				  drv->capa.max_acl_mac_addrs,
+				  drv->capa.num_multichan_concurrent);
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+	}
+
+	return pos - buf;
+}
+
+
+static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
+{
+	if (settings->head)
+		NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD,
+			settings->head_len, settings->head);
+
+	if (settings->tail)
+		NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL,
+			settings->tail_len, settings->tail);
+
+	if (settings->beacon_ies)
+		NLA_PUT(msg, NL80211_ATTR_IE,
+			settings->beacon_ies_len, settings->beacon_ies);
+
+	if (settings->proberesp_ies)
+		NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP,
+			settings->proberesp_ies_len, settings->proberesp_ies);
+
+	if (settings->assocresp_ies)
+		NLA_PUT(msg,
+			NL80211_ATTR_IE_ASSOC_RESP,
+			settings->assocresp_ies_len, settings->assocresp_ies);
+
+	if (settings->probe_resp)
+		NLA_PUT(msg, NL80211_ATTR_PROBE_RESP,
+			settings->probe_resp_len, settings->probe_resp);
+
+	return 0;
+
+nla_put_failure:
+	return -ENOBUFS;
+}
+
+
+static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+{
+	struct nl_msg *msg;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nlattr *beacon_csa;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)",
+		   settings->cs_count, settings->block_tx,
+		   settings->freq_params.freq, settings->freq_params.bandwidth,
+		   settings->freq_params.center_freq1,
+		   settings->freq_params.center_freq2);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command");
+		return -EOPNOTSUPP;
+	}
+
+	if ((drv->nlmode != NL80211_IFTYPE_AP) &&
+	    (drv->nlmode != NL80211_IFTYPE_P2P_GO))
+		return -EOPNOTSUPP;
+
+	/* check settings validity */
+	if (!settings->beacon_csa.tail ||
+	    ((settings->beacon_csa.tail_len <=
+	      settings->counter_offset_beacon) ||
+	     (settings->beacon_csa.tail[settings->counter_offset_beacon] !=
+	      settings->cs_count)))
+		return -EINVAL;
+
+	if (settings->beacon_csa.probe_resp &&
+	    ((settings->beacon_csa.probe_resp_len <=
+	      settings->counter_offset_presp) ||
+	     (settings->beacon_csa.probe_resp[settings->counter_offset_presp] !=
+	      settings->cs_count)))
+		return -EINVAL;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_CHANNEL_SWITCH);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+	NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count);
+	ret = nl80211_put_freq_params(msg, &settings->freq_params);
+	if (ret)
+		goto error;
+
+	if (settings->block_tx)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX);
+
+	/* beacon_after params */
+	ret = set_beacon_data(msg, &settings->beacon_after);
+	if (ret)
+		goto error;
+
+	/* beacon_csa params */
+	beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+	if (!beacon_csa)
+		goto nla_put_failure;
+
+	ret = set_beacon_data(msg, &settings->beacon_csa);
+	if (ret)
+		goto error;
+
+	NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+		    settings->counter_offset_beacon);
+
+	if (settings->beacon_csa.probe_resp)
+		NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+			    settings->counter_offset_presp);
+
+	nla_nest_end(msg, beacon_csa);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+
+nla_put_failure:
+	ret = -ENOBUFS;
+error:
+	nlmsg_free(msg);
+	wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request");
+	return ret;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int cmd_reply_handler(struct nl_msg *msg, void *arg)
+{
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpabuf *buf = arg;
+
+	if (!buf)
+		return NL_SKIP;
+
+	if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply");
+		return NL_SKIP;
+	}
+
+	wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0),
+			genlmsg_attrlen(gnlh, 0));
+
+	return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int vendor_reply_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct nlattr *nl_vendor_reply, *nl;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpabuf *buf = arg;
+	int rem;
+
+	if (!buf)
+		return NL_SKIP;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA];
+
+	if (!nl_vendor_reply)
+		return NL_SKIP;
+
+	if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply");
+		return NL_SKIP;
+	}
+
+	nla_for_each_nested(nl, nl_vendor_reply, rem) {
+		wpabuf_put_data(buf, nla_data(nl), nla_len(nl));
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
+			      unsigned int subcmd, const u8 *data,
+			      size_t data_len, struct wpabuf *buf)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (vendor_id == 0xffffffff) {
+		nl80211_cmd(drv, msg, 0, subcmd);
+		if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
+		    0)
+			goto nla_put_failure;
+		ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf);
+		if (ret)
+			wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
+				   ret);
+		return ret;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
+	if (nl80211_set_iface_id(msg, bss) < 0)
+		goto nla_put_failure;
+	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, vendor_id);
+	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd);
+	if (data)
+		NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, data_len, data);
+
+	ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
+			   ret);
+	return ret;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set,
+			       u8 qos_map_set_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
+		    qos_map_set, qos_map_set_len);
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_QOS_MAP);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	NLA_PUT(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
+
+	return ret;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int nl80211_set_wowlan(void *priv,
+			      const struct wowlan_triggers *triggers)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *wowlan_triggers;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WOWLAN);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	wowlan_triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+	if (!wowlan_triggers)
+		goto nla_put_failure;
+
+	if (triggers->any)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+	if (triggers->disconnect)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
+	if (triggers->magic_pkt)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+	if (triggers->gtk_rekey_failure)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
+	if (triggers->eap_identity_req)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
+	if (triggers->four_way_handshake)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
+	if (triggers->rfkill_release)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
+
+	nla_nest_end(msg, wowlan_triggers);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
+
+	return ret;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -10772,6 +12373,7 @@
 	.set_operstate = wpa_driver_nl80211_set_operstate,
 	.set_supp_port = wpa_driver_nl80211_set_supp_port,
 	.set_country = wpa_driver_nl80211_set_country,
+	.get_country = wpa_driver_nl80211_get_country,
 	.set_ap = wpa_driver_nl80211_set_ap,
 	.set_acl = wpa_driver_nl80211_set_acl,
 	.if_add = wpa_driver_nl80211_if_add,
@@ -10782,12 +12384,9 @@
 	.sta_remove = driver_nl80211_sta_remove,
 	.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
 	.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
-#ifdef HOSTAPD
 	.hapd_init = i802_init,
 	.hapd_deinit = i802_deinit,
 	.set_wds_sta = i802_set_wds_sta,
-#endif /* HOSTAPD */
-#if defined(HOSTAPD) || defined(CONFIG_AP)
 	.get_seqnum = i802_get_seqnum,
 	.flush = i802_flush,
 	.get_inact_sec = i802_get_inact_sec,
@@ -10798,7 +12397,6 @@
 	.set_sta_vlan = driver_nl80211_set_sta_vlan,
 	.sta_deauth = i802_sta_deauth,
 	.sta_disassoc = i802_sta_disassoc,
-#endif /* HOSTAPD || CONFIG_AP */
 	.read_sta_data = driver_nl80211_read_sta_data,
 	.set_freq = i802_set_freq,
 	.send_action = driver_nl80211_send_action,
@@ -10832,12 +12430,17 @@
 	.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
 	.get_mac_addr = wpa_driver_nl80211_get_macaddr,
 	.get_survey = wpa_driver_nl80211_get_survey,
+	.status = wpa_driver_nl80211_status,
+	.switch_channel = nl80211_switch_channel,
 #ifdef ANDROID_P2P
 	.set_noa = wpa_driver_set_p2p_noa,
 	.get_noa = wpa_driver_get_p2p_noa,
 	.set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
-#endif
+#endif /* ANDROID_P2P */
 #ifdef ANDROID
 	.driver_cmd = wpa_driver_nl80211_driver_cmd,
-#endif
+#endif /* ANDROID */
+	.vendor_cmd = nl80211_vendor_cmd,
+	.set_qos_map = nl80211_set_qos_map,
+	.set_wowlan = nl80211_set_wowlan,
 };
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index 0a9078a..9ce3fa2 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -260,17 +260,17 @@
 					    ROBO_ARLCTRL_CONF, read1, 1);
 	} else {
 		/* if both multiport addresses are the same we can add */
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_ADDR_1, read1, 3);
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_ADDR_2, read2, 3);
-		if (os_memcmp(read1, read2, 6) != 0)
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_1, read1, 3) ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_2, read2, 3) ||
+		    os_memcmp(read1, read2, 6) != 0)
 			return -1;
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_VEC_1, read1, 1);
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-					   ROBO_ARLCTRL_VEC_2, read2, 1);
-		if (read1[0] != read2[0])
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_1, read1, 1) ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_2, read2, 1) ||
+		    read1[0] != read2[0])
 			return -1;
 		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
 					    ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
index 541ebcc..3608b52 100644
--- a/src/drivers/driver_test.c
+++ b/src/drivers/driver_test.c
@@ -28,7 +28,6 @@
 #include "common/ieee802_11_defs.h"
 #include "crypto/sha1.h"
 #include "l2_packet/l2_packet.h"
-#include "p2p/p2p.h"
 #include "wps/wps.h"
 #include "driver.h"
 
@@ -102,20 +101,6 @@
 	unsigned int remain_on_channel_duration;
 
 	int current_freq;
-
-	struct p2p_data *p2p;
-	unsigned int off_channel_freq;
-	struct wpabuf *pending_action_tx;
-	u8 pending_action_src[ETH_ALEN];
-	u8 pending_action_dst[ETH_ALEN];
-	u8 pending_action_bssid[ETH_ALEN];
-	unsigned int pending_action_freq;
-	unsigned int pending_action_no_cck;
-	unsigned int pending_listen_freq;
-	unsigned int pending_listen_duration;
-	int pending_p2p_scan;
-	struct sockaddr *probe_from;
-	socklen_t probe_from_len;
 };
 
 
@@ -125,7 +110,6 @@
 static void wpa_driver_test_close_test_socket(
 	struct wpa_driver_test_data *drv);
 static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
-static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv);
 
 
 static void test_driver_free_bss(struct test_driver_bss *bss)
@@ -479,34 +463,6 @@
 	event.tx_status.ack = ret >= 0;
 	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
 
-#ifdef CONFIG_P2P
-	if (drv->p2p &&
-	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
-		if (drv->pending_action_tx == NULL) {
-			wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
-				   "no pending operation");
-			return ret;
-		}
-
-		if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) !=
-		    0) {
-			wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
-				   "unknown destination address");
-			return ret;
-		}
-
-		wpabuf_free(drv->pending_action_tx);
-		drv->pending_action_tx = NULL;
-
-		p2p_send_action_cb(drv->p2p, drv->pending_action_freq,
-				   drv->pending_action_dst,
-				   drv->pending_action_src,
-				   drv->pending_action_bssid,
-				   ret >= 0);
-	}
-#endif /* CONFIG_P2P */
-
 	return ret;
 }
 
@@ -553,10 +509,6 @@
 		event.rx_probe_req.ie = ie;
 		event.rx_probe_req.ie_len = ielen;
 		wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
-#ifdef CONFIG_P2P
-		if (drv->p2p)
-			p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
-#endif /* CONFIG_P2P */
 	}
 
 	dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
@@ -1059,7 +1011,7 @@
 			      const char *ifname, const u8 *addr,
 			      void *bss_ctx, void **drv_priv,
 			      char *force_ifname, u8 *if_addr,
-			      const char *bridge)
+			      const char *bridge, int use_existing)
 {
 	struct test_driver_bss *dbss = priv;
 	struct wpa_driver_test_data *drv = dbss->drv;
@@ -1313,25 +1265,7 @@
 
 static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 {
-	struct wpa_driver_test_data *drv = eloop_ctx;
 	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
-	if (drv->pending_p2p_scan && drv->p2p) {
-#ifdef CONFIG_P2P
-		size_t i;
-		struct os_time now;
-		os_get_time(&now);
-		for (i = 0; i < drv->num_scanres; i++) {
-			struct wpa_scan_res *bss = drv->scanres[i];
-			if (p2p_scan_res_handler(drv->p2p, bss->bssid,
-						 bss->freq, &now, bss->level,
-						 (const u8 *) (bss + 1),
-						 bss->ie_len) > 0)
-				return;
-		}
-		p2p_scan_res_handled(drv->p2p);
-#endif /* CONFIG_P2P */
-		return;
-	}
 	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
 }
 
@@ -1595,7 +1529,8 @@
 #endif /* DRIVER_TEST_UNIX */
 
 	if (params->mode == IEEE80211_MODE_AP) {
-		os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+		if (params->ssid)
+			os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
 		dbss->ssid_len = params->ssid_len;
 		os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN);
 		if (params->wpa_ie && params->wpa_ie_len) {
@@ -1616,8 +1551,9 @@
 				  MAC2STR(drv->own_addr));
 		if (ret >= 0 && ret < end - pos)
 			pos += ret;
-		pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
-					params->ssid_len);
+		if (params->ssid)
+			pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
+						params->ssid_len);
 		ret = os_snprintf(pos, end - pos, " ");
 		if (ret >= 0 && ret < end - pos)
 			pos += ret;
@@ -1641,12 +1577,15 @@
 			return -1;
 		}
 
-		os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+		if (params->ssid)
+			os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
 		dbss->ssid_len = params->ssid_len;
 	} else {
 		drv->associated = 1;
 		if (params->mode == IEEE80211_MODE_IBSS) {
-			os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+			if (params->ssid)
+				os_memcpy(dbss->ssid, params->ssid,
+					  params->ssid_len);
 			dbss->ssid_len = params->ssid_len;
 			if (params->bssid)
 				os_memcpy(dbss->bssid, params->bssid,
@@ -1949,30 +1888,8 @@
 				data_len - (mgmt->u.probe_req.variable - data);
 			wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
 					     &event);
-#ifdef CONFIG_P2P
-			if (drv->p2p)
-				p2p_probe_req_rx(drv->p2p, mgmt->sa,
-						 mgmt->da, mgmt->bssid,
-						 event.rx_probe_req.ie,
-						 event.rx_probe_req.ie_len);
-#endif /* CONFIG_P2P */
 		}
 	}
-
-#ifdef CONFIG_P2P
-	if (drv->p2p &&
-	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
-		size_t hdr_len;
-		hdr_len = (const u8 *)
-			&mgmt->u.action.u.vs_public_action.action - data;
-		p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
-			      mgmt->u.action.category,
-			      &mgmt->u.action.u.vs_public_action.action,
-			      data_len - hdr_len, freq);
-	}
-#endif /* CONFIG_P2P */
-
 }
 
 
@@ -1988,31 +1905,8 @@
 	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
 
 	/* data: optional [ STA-addr | ' ' | IEs(hex) ] */
-#ifdef CONFIG_P2P
-	if (drv->probe_req_report && drv->p2p && data_len) {
-		const char *d = (const char *) data;
-		u8 sa[ETH_ALEN];
-		u8 ie[512];
-		size_t ielen;
 
-		if (hwaddr_aton(d, sa))
-			return;
-		d += 18;
-		while (*d == ' ')
-			d++;
-		ielen = os_strlen(d) / 2;
-		if (ielen > sizeof(ie))
-			ielen = sizeof(ie);
-		if (hexstr2bin(d, ie, ielen) < 0)
-			ielen = 0;
-		drv->probe_from = from;
-		drv->probe_from_len = fromlen;
-		p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
-		drv->probe_from = NULL;
-	}
-#endif /* CONFIG_P2P */
-
-	if (!drv->ibss)
+	if (bss == NULL || !drv->ibss)
 		return;
 
 	pos = buf;
@@ -2167,12 +2061,6 @@
 	struct test_client_socket *cli, *prev;
 	int i;
 
-#ifdef CONFIG_P2P
-	if (drv->p2p)
-		p2p_deinit(drv->p2p);
-	wpabuf_free(drv->pending_action_tx);
-#endif /* CONFIG_P2P */
-
 	cli = drv->cli;
 	while (cli) {
 		prev = cli;
@@ -2369,13 +2257,6 @@
 		drv->use_associnfo = 1;
 	}
 
-	if (os_strstr(param, "p2p_mgmt=1")) {
-		wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P "
-			   "management");
-		if (wpa_driver_test_init_p2p(drv) < 0)
-			return -1;
-	}
-
 	return 0;
 }
 
@@ -2465,8 +2346,6 @@
 
 static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
 {
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
 	os_memset(capa, 0, sizeof(*capa));
 	capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
@@ -2482,8 +2361,6 @@
 	capa->auth = WPA_DRIVER_AUTH_OPEN |
 		WPA_DRIVER_AUTH_SHARED |
 		WPA_DRIVER_AUTH_LEAP;
-	if (drv->p2p)
-		capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT;
 	capa->flags |= WPA_DRIVER_FLAGS_AP;
 	capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
 	capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
@@ -2691,33 +2568,6 @@
 }
 
 
-#ifdef CONFIG_P2P
-static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_driver_test_data *drv = eloop_ctx;
-
-	if (drv->pending_action_tx == NULL)
-		return;
-
-	if (drv->off_channel_freq != drv->pending_action_freq) {
-		wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
-			   "waiting for another freq=%u",
-			   drv->pending_action_freq);
-		return;
-	}
-	wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
-		   MACSTR, MAC2STR(drv->pending_action_dst));
-	wpa_driver_test_send_action(drv, drv->pending_action_freq, 0,
-				    drv->pending_action_dst,
-				    drv->pending_action_src,
-				    drv->pending_action_bssid,
-				    wpabuf_head(drv->pending_action_tx),
-				    wpabuf_len(drv->pending_action_tx),
-				    drv->pending_action_no_cck);
-}
-#endif /* CONFIG_P2P */
-
-
 static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_driver_test_data *drv = eloop_ctx;
@@ -2729,9 +2579,6 @@
 	data.remain_on_channel.freq = drv->remain_on_channel_freq;
 	data.remain_on_channel.duration = drv->remain_on_channel_duration;
 
-	if (drv->p2p)
-		drv->off_channel_freq = 0;
-
 	drv->remain_on_channel_freq = 0;
 
 	wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
@@ -2765,18 +2612,6 @@
 	data.remain_on_channel.duration = duration;
 	wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
 
-#ifdef CONFIG_P2P
-	if (drv->p2p) {
-		drv->off_channel_freq = drv->remain_on_channel_freq;
-		test_send_action_cb(drv, NULL);
-		if (drv->off_channel_freq == drv->pending_listen_freq) {
-			p2p_listen_cb(drv->p2p, drv->pending_listen_freq,
-				      drv->pending_listen_duration);
-			drv->pending_listen_freq = 0;
-		}
-	}
-#endif /* CONFIG_P2P */
-
 	return 0;
 }
 
@@ -2804,470 +2639,6 @@
 }
 
 
-#ifdef CONFIG_P2P
-
-static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
-	if (!drv->p2p)
-		return -1;
-	return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0);
-}
-
-
-static int wpa_driver_test_p2p_stop_find(void *priv)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	if (!drv->p2p)
-		return -1;
-	p2p_stop_find(drv->p2p);
-	return 0;
-}
-
-
-static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
-	if (!drv->p2p)
-		return -1;
-	return p2p_listen(drv->p2p, timeout);
-}
-
-
-static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr,
-				       int wps_method, int go_intent,
-				       const u8 *own_interface_addr,
-				       unsigned int force_freq,
-				       int persistent_group)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d "
-		   "go_intent=%d "
-		   "own_interface_addr=" MACSTR " force_freq=%u "
-		   "persistent_group=%d)",
-		   __func__, MAC2STR(peer_addr), wps_method, go_intent,
-		   MAC2STR(own_interface_addr), force_freq, persistent_group);
-	if (!drv->p2p)
-		return -1;
-	return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
-			   own_interface_addr, force_freq, persistent_group,
-			   NULL, 0, 0, 0);
-}
-
-
-static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")",
-		   __func__, MAC2STR(peer_addr));
-	if (!drv->p2p)
-		return -1;
-	p2p_wps_success_cb(drv->p2p, peer_addr);
-	return 0;
-}
-
-
-static int wpa_driver_test_p2p_group_formation_failed(void *priv)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	if (!drv->p2p)
-		return -1;
-	p2p_group_formation_failed(drv->p2p);
-	return 0;
-}
-
-
-static int wpa_driver_test_p2p_set_params(void *priv,
-					  const struct p2p_params *params)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	if (!drv->p2p)
-		return -1;
-	if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 ||
-	    p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 ||
-	    p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type,
-				  params->num_sec_dev_types) < 0)
-		return -1;
-	return 0;
-}
-
-
-static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
-			 unsigned int num_req_dev_types,
-			 const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
-{
-	struct wpa_driver_test_data *drv = ctx;
-	struct wpa_driver_scan_params params;
-	int ret;
-	struct wpabuf *wps_ie, *ies;
-	int social_channels[] = { 2412, 2437, 2462, 0, 0 };
-	size_t ielen;
-
-	wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)",
-		   __func__, type, freq);
-
-	os_memset(&params, 0, sizeof(params));
-
-	/* P2P Wildcard SSID */
-	params.num_ssids = 1;
-	params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
-	params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
-
-#if 0 /* TODO: WPS IE */
-	wpa_s->wps->dev.p2p = 1;
-	wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
-					wpa_s->wps->uuid, WPS_REQ_ENROLLEE);
-#else
-	wps_ie = wpabuf_alloc(1);
-#endif
-	if (wps_ie == NULL)
-		return -1;
-
-	ielen = p2p_scan_ie_buf_len(drv->p2p);
-	ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
-	if (ies == NULL) {
-		wpabuf_free(wps_ie);
-		return -1;
-	}
-	wpabuf_put_buf(ies, wps_ie);
-	wpabuf_free(wps_ie);
-
-	p2p_scan_ie(drv->p2p, ies, dev_id);
-
-	params.extra_ies = wpabuf_head(ies);
-	params.extra_ies_len = wpabuf_len(ies);
-
-	switch (type) {
-	case P2P_SCAN_SOCIAL:
-		params.freqs = social_channels;
-		break;
-	case P2P_SCAN_FULL:
-		break;
-	case P2P_SCAN_SOCIAL_PLUS_ONE:
-		social_channels[3] = freq;
-		params.freqs = social_channels;
-		break;
-	}
-
-	drv->pending_p2p_scan = 1;
-	ret = wpa_driver_test_scan(drv, &params);
-
-	wpabuf_free(ies);
-
-	return ret;
-}
-
-
-static int test_send_action(void *ctx, unsigned int freq, const u8 *dst,
-			    const u8 *src, const u8 *bssid, const u8 *buf,
-			    size_t len, unsigned int wait_time)
-{
-	struct wpa_driver_test_data *drv = ctx;
-
-	wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR
-		   " bssid=" MACSTR " len=%d",
-		   __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
-		   (int) len);
-	if (freq <= 0) {
-		wpa_printf(MSG_WARNING, "P2P: No frequency specified for "
-			   "action frame TX");
-		return -1;
-	}
-
-	if (drv->pending_action_tx) {
-		wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
-			   "to " MACSTR, MAC2STR(drv->pending_action_dst));
-		wpabuf_free(drv->pending_action_tx);
-	}
-	drv->pending_action_tx = wpabuf_alloc(len);
-	if (drv->pending_action_tx == NULL)
-		return -1;
-	wpabuf_put_data(drv->pending_action_tx, buf, len);
-	os_memcpy(drv->pending_action_src, src, ETH_ALEN);
-	os_memcpy(drv->pending_action_dst, dst, ETH_ALEN);
-	os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN);
-	drv->pending_action_freq = freq;
-	drv->pending_action_no_cck = 1;
-
-	if (drv->off_channel_freq == freq) {
-		/* Already on requested channel; send immediately */
-		/* TODO: Would there ever be need to extend the current
-		 * duration on the channel? */
-		eloop_cancel_timeout(test_send_action_cb, drv, NULL);
-		eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL);
-		return 0;
-	}
-
-	wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
-		   "once the driver gets to the requested channel");
-	if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
-			   "to remain on channel (%u MHz) for Action "
-			   "Frame TX", freq);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static void test_send_action_done(void *ctx)
-{
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	/* TODO */
-}
-
-
-static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
-{
-	struct wpa_driver_test_data *drv = ctx;
-	union wpa_event_data event;
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	os_memset(&event, 0, sizeof(event));
-	event.p2p_go_neg_completed.res = res;
-	wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event);
-}
-
-
-static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
-{
-	struct wpa_driver_test_data *drv = ctx;
-	union wpa_event_data event;
-	wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src));
-	os_memset(&event, 0, sizeof(event));
-	event.p2p_go_neg_req_rx.src = src;
-	event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id;
-	wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event);
-}
-
-
-static void test_dev_found(void *ctx, const u8 *addr,
-			   const struct p2p_peer_info *info, int new_device)
-{
-	struct wpa_driver_test_data *drv = ctx;
-	union wpa_event_data event;
-	char devtype[WPS_DEV_TYPE_BUFSIZE];
-	wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR
-		   " pri_dev_type=%s name='%s' config_methods=0x%x "
-		   "dev_capab=0x%x group_capab=0x%x)",
-		   __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr),
-		   wps_dev_type_bin2str(info->pri_dev_type, devtype,
-					sizeof(devtype)),
-		   info->device_name, info->config_methods, info->dev_capab,
-		   info->group_capab);
-
-	os_memset(&event, 0, sizeof(event));
-	event.p2p_dev_found.addr = addr;
-	event.p2p_dev_found.dev_addr = info->p2p_device_addr;
-	event.p2p_dev_found.pri_dev_type = info->pri_dev_type;
-	event.p2p_dev_found.dev_name = info->device_name;
-	event.p2p_dev_found.config_methods = info->config_methods;
-	event.p2p_dev_found.dev_capab = info->dev_capab;
-	event.p2p_dev_found.group_capab = info->group_capab;
-	wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event);
-}
-
-
-static int test_start_listen(void *ctx, unsigned int freq,
-			     unsigned int duration,
-			     const struct wpabuf *probe_resp_ie)
-{
-	struct wpa_driver_test_data *drv = ctx;
-
-	wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)",
-		   __func__, freq, duration);
-
-	if (wpa_driver_test_probe_req_report(drv, 1) < 0)
-		return -1;
-
-	drv->pending_listen_freq = freq;
-	drv->pending_listen_duration = duration;
-
-	if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) {
-		drv->pending_listen_freq = 0;
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static void test_stop_listen(void *ctx)
-{
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	/* TODO */
-}
-
-
-static int test_send_probe_resp(void *ctx, const struct wpabuf *buf)
-{
-	struct wpa_driver_test_data *drv = ctx;
-	char resp[512], *pos, *end;
-	int ret;
-	const struct ieee80211_mgmt *mgmt;
-	const u8 *ie, *ie_end;
-
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf);
-	if (wpabuf_len(buf) < 24)
-		return -1;
-	if (!drv->probe_from) {
-		wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__);
-		return -1;
-	}
-
-	pos = resp;
-	end = resp + sizeof(resp);
-
-	mgmt = wpabuf_head(buf);
-
-	/* reply: SCANRESP BSSID SSID IEs */
-	ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
-			  MAC2STR(mgmt->bssid));
-	if (ret < 0 || ret >= end - pos)
-		return -1;
-	pos += ret;
-
-	ie = mgmt->u.probe_resp.variable;
-	ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf);
-	if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID ||
-	    ie + 2 + ie[1] > ie_end)
-		return -1;
-	pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]);
-
-	ret = os_snprintf(pos, end - pos, " ");
-	if (ret < 0 || ret >= end - pos)
-		return -1;
-	pos += ret;
-	pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie);
-
-	sendto(drv->test_socket, resp, pos - resp, 0,
-	       drv->probe_from, drv->probe_from_len);
-
-	return 0;
-}
-
-
-static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-			    u16 update_indic, const u8 *tlvs, size_t tlvs_len)
-{
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	/* TODO */
-}
-
-
-static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-			     const u8 *tlvs, size_t tlvs_len)
-{
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	/* TODO */
-}
-
-
-static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
-			       const u8 *dev_addr, const u8 *pri_dev_type,
-			       const char *dev_name, u16 supp_config_methods,
-			       u8 dev_capab, u8 group_capab,
-			       const u8 *group_id, size_t group_id_len)
-{
-	wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
-		   __func__, MAC2STR(peer), config_methods);
-	/* TODO */
-}
-
-
-static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
-{
-	wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
-		   __func__, MAC2STR(peer), config_methods);
-	/* TODO */
-}
-
-
-static void test_p2p_debug_print(void *ctx, int level, const char *msg)
-{
-	wpa_printf(level, "P2P: %s", msg);
-}
-
-#endif /* CONFIG_P2P */
-
-
-static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv)
-{
-#ifdef CONFIG_P2P
-	struct p2p_config p2p;
-	unsigned int r;
-	int i;
-
-	os_memset(&p2p, 0, sizeof(p2p));
-	p2p.cb_ctx = drv;
-	p2p.debug_print = test_p2p_debug_print;
-	p2p.p2p_scan = test_p2p_scan;
-	p2p.send_action = test_send_action;
-	p2p.send_action_done = test_send_action_done;
-	p2p.go_neg_completed = test_go_neg_completed;
-	p2p.go_neg_req_rx = test_go_neg_req_rx;
-	p2p.dev_found = test_dev_found;
-	p2p.start_listen = test_start_listen;
-	p2p.stop_listen = test_stop_listen;
-	p2p.send_probe_resp = test_send_probe_resp;
-	p2p.sd_request = test_sd_request;
-	p2p.sd_response = test_sd_response;
-	p2p.prov_disc_req = test_prov_disc_req;
-	p2p.prov_disc_resp = test_prov_disc_resp;
-
-	os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN);
-
-	p2p.reg_class = 12; /* TODO: change depending on location */
-	/*
-	 * Pick one of the social channels randomly as the listen
-	 * channel.
-	 */
-	os_get_random((u8 *) &r, sizeof(r));
-	p2p.channel = 1 + (r % 3) * 5;
-
-	/* TODO: change depending on location */
-	p2p.op_reg_class = 12;
-	/*
-	 * For initial tests, pick the operation channel randomly.
-	 * TODO: Use scan results (etc.) to select the best channel.
-	 */
-	p2p.op_channel = 1 + r % 11;
-
-	os_memcpy(p2p.country, "US ", 3);
-
-	/* FIX: fetch available channels from the driver */
-	p2p.channels.reg_classes = 1;
-	p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */
-	p2p.channels.reg_class[0].channels = 11;
-	for (i = 0; i < 11; i++)
-		p2p.channels.reg_class[0].channel[i] = i + 1;
-
-	p2p.max_peers = 100;
-
-	drv->p2p = p2p_init(&p2p);
-	if (drv->p2p == NULL)
-		return -1;
-	return 0;
-#else /* CONFIG_P2P */
-	wpa_printf(MSG_INFO, "driver_test: P2P support not included");
-	return -1;
-#endif /* CONFIG_P2P */
-}
-
-
 const struct wpa_driver_ops wpa_driver_test_ops = {
 	"test",
 	"wpa_supplicant test driver",
@@ -3309,14 +2680,4 @@
 	.remain_on_channel = wpa_driver_test_remain_on_channel,
 	.cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
 	.probe_req_report = wpa_driver_test_probe_req_report,
-#ifdef CONFIG_P2P
-	.p2p_find = wpa_driver_test_p2p_find,
-	.p2p_stop_find = wpa_driver_test_p2p_stop_find,
-	.p2p_listen = wpa_driver_test_p2p_listen,
-	.p2p_connect = wpa_driver_test_p2p_connect,
-	.wps_success_cb = wpa_driver_test_wps_success_cb,
-	.p2p_group_formation_failed =
-	wpa_driver_test_p2p_group_formation_failed,
-	.p2p_set_params = wpa_driver_test_p2p_set_params,
-#endif /* CONFIG_P2P */
 };
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 1401050..459ac48 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -31,10 +31,6 @@
 #include "driver.h"
 #include "driver_wext.h"
 
-#ifdef ANDROID
-#include "android_drv.h"
-#endif /* ANDROID */
-
 static int wpa_driver_wext_flush_pmkid(void *priv);
 static int wpa_driver_wext_get_range(void *priv);
 static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
@@ -299,14 +295,6 @@
 		}
 		wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
 #endif /* CONFIG_PEERKEY */
-#ifdef ANDROID
-	} else if (os_strncmp(custom, "STOP", 4) == 0) {
-		wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
-	} else if (os_strncmp(custom, "START", 5) == 0) {
-		wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
-	} else if (os_strncmp(custom, "HANG", 4) == 0) {
-		wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
-#endif /* ANDROID */
 	}
 }
 
@@ -858,12 +846,6 @@
 
 	drv->mlme_sock = -1;
 
-#ifdef ANDROID
-	drv->errors = 0;
-	drv->driver_is_started = TRUE;
-	drv->bgscan_enabled = 0;
-#endif /* ANDROID */
-
 	if (wpa_driver_wext_finish_drv_init(drv) < 0)
 		goto err3;
 
@@ -1869,10 +1851,8 @@
 {
 	struct iwreq iwr;
 	const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-#ifndef ANDROID
 	u8 ssid[32];
 	int i;
-#endif /* ANDROID */
 
 	/*
 	 * Only force-disconnect when the card is in infrastructure mode,
@@ -1893,7 +1873,6 @@
 				   "selection on disconnect");
 		}
 
-#ifndef ANDROID
 		if (drv->cfg80211) {
 			/*
 			 * cfg80211 supports SIOCSIWMLME commands, so there is
@@ -1919,7 +1898,6 @@
 			wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
 				   "SSID to disconnect");
 		}
-#endif /* ANDROID */
 	}
 }
 
@@ -1960,15 +1938,15 @@
 int wpa_driver_wext_cipher2wext(int cipher)
 {
 	switch (cipher) {
-	case CIPHER_NONE:
+	case WPA_CIPHER_NONE:
 		return IW_AUTH_CIPHER_NONE;
-	case CIPHER_WEP40:
+	case WPA_CIPHER_WEP40:
 		return IW_AUTH_CIPHER_WEP40;
-	case CIPHER_TKIP:
+	case WPA_CIPHER_TKIP:
 		return IW_AUTH_CIPHER_TKIP;
-	case CIPHER_CCMP:
+	case WPA_CIPHER_CCMP:
 		return IW_AUTH_CIPHER_CCMP;
-	case CIPHER_WEP104:
+	case WPA_CIPHER_WEP104:
 		return IW_AUTH_CIPHER_WEP104;
 	default:
 		return 0;
@@ -1979,10 +1957,10 @@
 int wpa_driver_wext_keymgmt2wext(int keymgmt)
 {
 	switch (keymgmt) {
-	case KEY_MGMT_802_1X:
-	case KEY_MGMT_802_1X_NO_WPA:
+	case WPA_KEY_MGMT_IEEE8021X:
+	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
 		return IW_AUTH_KEY_MGMT_802_1X;
-	case KEY_MGMT_PSK:
+	case WPA_KEY_MGMT_PSK:
 		return IW_AUTH_KEY_MGMT_PSK;
 	default:
 		return 0;
@@ -2049,7 +2027,11 @@
 		 * Stop cfg80211 from trying to associate before we are done
 		 * with all parameters.
 		 */
-		wpa_driver_wext_set_ssid(drv, (u8 *) "", 0);
+		if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "WEXT: Failed to clear SSID to stop pending cfg80211 association attempts (if any)");
+			/* continue anyway */
+		}
 	}
 
 	if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
@@ -2099,9 +2081,9 @@
 	if (wpa_driver_wext_set_auth_param(drv,
 					   IW_AUTH_KEY_MGMT, value) < 0)
 		ret = -1;
-	value = params->key_mgmt_suite != KEY_MGMT_NONE ||
-		params->pairwise_suite != CIPHER_NONE ||
-		params->group_suite != CIPHER_NONE ||
+	value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE ||
+		params->pairwise_suite != WPA_CIPHER_NONE ||
+		params->group_suite != WPA_CIPHER_NONE ||
 		params->wpa_ie_len;
 	if (wpa_driver_wext_set_auth_param(drv,
 					   IW_AUTH_PRIVACY_INVOKED, value) < 0)
@@ -2110,8 +2092,8 @@
 	/* Allow unencrypted EAPOL messages even if pairwise keys are set when
 	 * not using WPA. IEEE 802.1X specifies that these frames are not
 	 * encrypted, but WPA encrypts them when pairwise keys are in use. */
-	if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
-	    params->key_mgmt_suite == KEY_MGMT_PSK)
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
 		allow_unencrypted_eapol = 0;
 	else
 		allow_unencrypted_eapol = 1;
@@ -2338,129 +2320,37 @@
 }
 
 
-#ifdef ANDROID
-
-static int android_wext_cmd(struct wpa_driver_wext_data *drv, const char *cmd)
+static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si)
 {
+	struct wpa_driver_wext_data *drv = priv;
+	struct iw_statistics stats;
 	struct iwreq iwr;
-	char buf[MAX_DRV_CMD_SIZE];
-	int ret;
+
+	os_memset(si, 0, sizeof(*si));
+	si->current_signal = -9999;
+	si->current_noise = 9999;
+	si->chanwidth = CHAN_WIDTH_UNKNOWN;
 
 	os_memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) &stats;
+	iwr.u.data.length = sizeof(stats);
+	iwr.u.data.flags = 1;
 
-	os_memset(buf, 0, sizeof(buf));
-	os_strlcpy(buf, cmd, sizeof(buf));
-
-	iwr.u.data.pointer = buf;
-	iwr.u.data.length = sizeof(buf);
-
-	ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
-
-	if (ret < 0) {
-		wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret,
-			   cmd);
-		drv->errors++;
-		if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
-			drv->errors = 0;
-			wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE
-				"HANGED");
-		}
-		return ret;
+	if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s",
+			   strerror(errno));
+		return -1;
 	}
 
-	drv->errors = 0;
+	si->current_signal = stats.qual.level -
+		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
+	si->current_noise = stats.qual.noise -
+		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
 	return 0;
 }
 
 
-static int wext_sched_scan(void *priv, struct wpa_driver_scan_params *params,
-			   u32 interval)
-{
-	struct wpa_driver_wext_data *drv = priv;
-	struct iwreq iwr;
-	int ret = 0, i = 0, bp;
-	char buf[WEXT_PNO_MAX_COMMAND_SIZE];
-
-	bp = WEXT_PNOSETUP_HEADER_SIZE;
-	os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
-	buf[bp++] = WEXT_PNO_TLV_PREFIX;
-	buf[bp++] = WEXT_PNO_TLV_VERSION;
-	buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
-	buf[bp++] = WEXT_PNO_TLV_RESERVED;
-
-	while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
-		/*
-		 * Check that there is enough space needed for 1 more SSID, the
-		 * other sections and null termination.
-		 */
-		if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE +
-		     WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
-			break;
-
-		wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
-				  params->ssids[i].ssid,
-				  params->ssids[i].ssid_len);
-		buf[bp++] = WEXT_PNO_SSID_SECTION;
-		buf[bp++] = params->ssids[i].ssid_len;
-		os_memcpy(&buf[bp], params->ssids[i].ssid,
-			  params->ssids[i].ssid_len);
-		bp += params->ssids[i].ssid_len;
-		i++;
-	}
-
-	buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
-	/* TODO: consider using interval parameter (interval in msec) instead
-	 * of hardcoded value here */
-	os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
-		    WEXT_PNO_SCAN_INTERVAL);
-	bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
-
-	buf[bp++] = WEXT_PNO_REPEAT_SECTION;
-	os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
-		    WEXT_PNO_REPEAT);
-	bp += WEXT_PNO_REPEAT_LENGTH;
-
-	buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
-	os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
-		    WEXT_PNO_MAX_REPEAT);
-	bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
-
-	os_memset(&iwr, 0, sizeof(iwr));
-	os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-	iwr.u.data.pointer = buf;
-	iwr.u.data.length = bp;
-
-	ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
-	if (ret < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
-			   ret);
-		drv->errors++;
-		if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
-			drv->errors = 0;
-			wpa_msg(drv->ctx, MSG_INFO,
-				WPA_EVENT_DRIVER_STATE "HANGED");
-		}
-		return ret;
-	}
-
-	drv->errors = 0;
-	drv->bgscan_enabled = 1;
-
-	return android_wext_cmd(drv, "PNOFORCE 1");
-}
-
-
-static int wext_stop_sched_scan(void *priv)
-{
-	struct wpa_driver_wext_data *drv = priv;
-	drv->bgscan_enabled = 0;
-	return android_wext_cmd(drv, "PNOFORCE 0");
-}
-
-#endif /* ANDROID */
-
-
 const struct wpa_driver_ops wpa_driver_wext_ops = {
 	.name = "wext",
 	.desc = "Linux wireless extensions (generic)",
@@ -2480,8 +2370,5 @@
 	.get_capa = wpa_driver_wext_get_capa,
 	.set_operstate = wpa_driver_wext_set_operstate,
 	.get_radio_name = wext_get_radio_name,
-#ifdef ANDROID
-	.sched_scan = wext_sched_scan,
-	.stop_sched_scan = wext_stop_sched_scan,
-#endif /* ANDROID */
+	.signal_poll = wpa_driver_wext_signal_poll,
 };
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
index c4a5bc9..b4b5960 100644
--- a/src/drivers/driver_wext.h
+++ b/src/drivers/driver_wext.h
@@ -44,12 +44,6 @@
 	int cfg80211; /* whether driver is using cfg80211 */
 
 	u8 max_level;
-
-#ifdef ANDROID
-	int errors;
-	int driver_is_started;
-	int bgscan_enabled;
-#endif /* ANDROID */
 };
 
 int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
index 04eb4fd..d0e42ec 100644
--- a/src/drivers/drivers.c
+++ b/src/drivers/drivers.c
@@ -6,8 +6,9 @@
  * See README for more details.
  */
 
-#include "includes.h"
-
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "driver.h"
 
 #ifdef CONFIG_DRIVER_WEXT
 extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
@@ -33,6 +34,10 @@
 #ifdef CONFIG_DRIVER_WIRED
 extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
 #endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+ /* driver_macsec_qca.c */
+extern struct wpa_driver_ops wpa_driver_macsec_qca_ops;
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
 #ifdef CONFIG_DRIVER_TEST
 extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
 #endif /* CONFIG_DRIVER_TEST */
@@ -74,6 +79,9 @@
 #ifdef CONFIG_DRIVER_WIRED
 	&wpa_driver_wired_ops,
 #endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+	&wpa_driver_macsec_qca_ops,
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
 #ifdef CONFIG_DRIVER_TEST
 	&wpa_driver_test_ops,
 #endif /* CONFIG_DRIVER_TEST */
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 68ff910..40aaba5 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -17,6 +17,11 @@
 DRV_OBJS += ../src/drivers/driver_wired.o
 endif
 
+ifdef CONFIG_DRIVER_MACSEC_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
+DRV_OBJS += ../src/drivers/driver_macsec_qca.o
+endif
+
 ifdef CONFIG_DRIVER_NL80211
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
 DRV_OBJS += ../src/drivers/driver_nl80211.o
@@ -100,6 +105,9 @@
 CONFIG_L2_PACKET=linux
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
+ifdef ATH_GCM_SUPPORT
+CFLAGS += -DATH_GCM_SUPPORT
+endif
 endif
 
 ##### PURE CLIENT DRIVERS
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 4380428..837971d 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -204,11 +204,14 @@
 int linux_br_get(char *brname, const char *ifname)
 {
 	char path[128], brlink[128], *pos;
+	ssize_t res;
+
 	os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
 		    ifname);
-	os_memset(brlink, 0, sizeof(brlink));
-	if (readlink(path, brlink, sizeof(brlink) - 1) < 0)
+	res = readlink(path, brlink, sizeof(brlink));
+	if (res < 0 || (size_t) res >= sizeof(brlink))
 		return -1;
+	brlink[res] = '\0';
 	pos = os_strrchr(brlink, '/');
 	if (pos == NULL)
 		return -1;
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
index 6c60550..2fa20b1 100644
--- a/src/drivers/netlink.c
+++ b/src/drivers/netlink.c
@@ -1,6 +1,6 @@
 /*
  * Netlink helper functions for driver wrappers
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -137,6 +137,35 @@
 	os_free(netlink);
 }
 
+
+static const char * linkmode_str(int mode)
+{
+	switch (mode) {
+	case -1:
+		return "no change";
+	case 0:
+		return "kernel-control";
+	case 1:
+		return "userspace-control";
+	}
+	return "?";
+}
+
+
+static const char * operstate_str(int state)
+{
+	switch (state) {
+	case -1:
+		return "no change";
+	case IF_OPER_DORMANT:
+		return "IF_OPER_DORMANT";
+	case IF_OPER_UP:
+		return "IF_OPER_UP";
+	}
+	return "?";
+}
+
+
 int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
 			   int linkmode, int operstate)
 {
@@ -184,8 +213,9 @@
 			RTA_LENGTH(sizeof(char));
 	}
 
-	wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
-		   linkmode, operstate);
+	wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)",
+		   ifindex, linkmode, linkmode_str(linkmode),
+		   operstate, operstate_str(operstate));
 
 	ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
 	if (ret < 0) {
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 32b060e..406010d 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -126,6 +126,31 @@
  */
 
 /**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -278,8 +303,9 @@
  *	passed, all channels allowed for the current regulatory domain
  *	are used.  Extra IEs can also be passed from the userspace by
  *	using the %NL80211_ATTR_IE attribute.
- * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan.  Returns -ENOENT
- *	if scheduled scan is not running.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ *	scheduled scan is not running. The caller may assume that as soon
+ *	as the call returns, it is safe to start a new scheduled scan again.
  * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
  *	results available.
  * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
@@ -393,8 +419,18 @@
  *	%NL80211_ATTR_SSID attribute, and can optionally specify the association
  *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
  *	%NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
- *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
- *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ *	%NL80211_ATTR_WIPHY_FREQ_HINT.
+ *	If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ *	restrictions on BSS selection, i.e., they effectively prevent roaming
+ *	within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ *	can be included to provide a recommendation of the initial BSS while
+ *	allowing the driver to roam to other BSSes within the ESS and also to
+ *	ignore this recommendation if the indicated BSS is not ideal. Only one
+ *	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).
  *	Background scan period can optionally be
  *	specified in %NL80211_ATTR_BG_SCAN_PERIOD,
  *	if not specified default background scan configuration
@@ -556,7 +592,14 @@
  *	operation, %NL80211_ATTR_MAC contains the peer MAC address, and
  *	%NL80211_ATTR_REASON_CODE the reason code to be used (only with
  *	%NL80211_TDLS_TEARDOWN).
- * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame.
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The
+ *	%NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be
+ *	sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as
+ *	802.11 management frames, while TDLS action codes (802.11-2012
+ *	8.5.13.1) will be encapsulated and sent as data frames. The currently
+ *	supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES
+ *	and the currently supported TDLS actions codes are given in
+ *	&enum ieee80211_tdls_actioncode.
  *
  * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
  *	(or GO) interface (i.e. hostapd) to ask for unexpected frames to
@@ -648,6 +691,34 @@
  * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
  *	return back to normal.
  *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ *	the new channel information (Channel Switch Announcement - CSA)
+ *	in the beacon for some time (as defined in the
+ *	%NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ *	new channel. Userspace provides the new channel information (using
+ *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ *	width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ *	other station that transmission must be blocked until the channel
+ *	switch is complete.
+ *
+ * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
+ *	by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
+ *	%NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
+ *	%NL80211_ATTR_VENDOR_DATA.
+ *	For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
+ *	used in the wiphy data as a nested attribute containing descriptions
+ *	(&struct nl80211_vendor_cmd_info) of the supported vendor commands.
+ *	This may also be sent as an event with the same attributes.
+ *
+ * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values.
+ *	The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If
+ *	that attribute is not included, QoS mapping is disabled. Since this
+ *	QoS mapping is relevant for IP packets, it is only valid during an
+ *	association. This is cleared on disassociation and AP restart.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -810,6 +881,15 @@
 	NL80211_CMD_CRIT_PROTOCOL_START,
 	NL80211_CMD_CRIT_PROTOCOL_STOP,
 
+	NL80211_CMD_GET_COALESCE,
+	NL80211_CMD_SET_COALESCE,
+
+	NL80211_CMD_CHANNEL_SWITCH,
+
+	NL80211_CMD_VENDOR,
+
+	NL80211_CMD_SET_QOS_MAP,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -945,7 +1025,7 @@
  * 	to query the CRDA to retrieve one regulatory domain. This attribute can
  * 	also be used by userspace to query the kernel for the currently set
  * 	regulatory domain. We chose an alpha2 as that is also used by the
- * 	IEEE-802.11d country information element to identify a country.
+ * 	IEEE-802.11 country information element to identify a country.
  * 	Users can also simply ask the wireless core to set regulatory domain
  * 	to a specific alpha2.
  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
@@ -1436,6 +1516,73 @@
  *	allowed to be used with the first @NL80211_CMD_SET_STATION command to
  *	update a TDLS peer STA entry.
  *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ *	until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ *	must be blocked on the current channel (before the channel switch
+ *	operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ *	for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ *	field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ *	field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ *	As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ *      supported operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *	controls DFS operation in IBSS mode. If the flag is included in
+ *	%NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *	channels and reports radar events to userspace. Userspace is required
+ *	to react to radar events, e.g. initiate a channel switch or leave the
+ *	IBSS network.
+ *
+ * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports
+ *	5 MHz channel bandwidth.
+ * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports
+ *	10 MHz channel bandwidth.
+ *
+ * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
+ *	Notification Element based on association request when used with
+ *	%NL80211_CMD_NEW_STATION; u8 attribute.
+ *
+ * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
+ *	%NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
+ * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
+ * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
+ *	attribute is also used for vendor command feature advertisement
+ * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
+ *	info, containing a nested array of possible events
+ *
+ * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This
+ *	data is in the format defined for the payload of the QoS Map Set element
+ *	in IEEE Std 802.11-2012, 8.4.2.97.
+ *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ *	associated stations are supported in AP mode (including P2P GO); u32.
+ *	Since drivers may not have a fixed limit on the maximum number (e.g.,
+ *	other concurrent operations may affect this), drivers are allowed to
+ *	advertise values that cannot always be met. In such cases, an attempt
+ *	to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ *	As specified in the &enum nl80211_tdls_peer_capability.
+ *
+ * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
+ *	creation then the new interface will be owned by the netlink socket
+ *	that created it and will be destroyed when the socket is closed
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1736,6 +1883,43 @@
 
 	NL80211_ATTR_PEER_AID,
 
+	NL80211_ATTR_COALESCE_RULE,
+
+	NL80211_ATTR_CH_SWITCH_COUNT,
+	NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+	NL80211_ATTR_CSA_IES,
+	NL80211_ATTR_CSA_C_OFF_BEACON,
+	NL80211_ATTR_CSA_C_OFF_PRESP,
+
+	NL80211_ATTR_RXMGMT_FLAGS,
+
+	NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+	NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+	NL80211_ATTR_HANDLE_DFS,
+
+	NL80211_ATTR_SUPPORT_5_MHZ,
+	NL80211_ATTR_SUPPORT_10_MHZ,
+
+	NL80211_ATTR_OPMODE_NOTIF,
+
+	NL80211_ATTR_VENDOR_ID,
+	NL80211_ATTR_VENDOR_SUBCMD,
+	NL80211_ATTR_VENDOR_DATA,
+	NL80211_ATTR_VENDOR_EVENTS,
+
+	NL80211_ATTR_QOS_MAP,
+
+	NL80211_ATTR_MAC_HINT,
+	NL80211_ATTR_WIPHY_FREQ_HINT,
+
+	NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+	NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
+	NL80211_ATTR_IFACE_SOCKET_OWNER,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -2136,10 +2320,9 @@
  * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
  * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
  *	regulatory domain.
- * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is
- *	permitted on this channel in current regulatory domain.
- * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted
- *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+ * 	are permitted on this channel, this includes sending probe
+ * 	requests, or modes of operation that require beaconing.
  * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
  *	on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
@@ -2158,16 +2341,42 @@
  * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
  *	using this channel as the primary or any of the secondary channels
  *	isn't possible
+ * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ *	channel. A channel that has the INDOOR_ONLY attribute can only be
+ *	used when there is a clear assessment that the device is operating in
+ *	an indoor surroundings, i.e., it is connected to AC power (and not
+ *	through portable DC inverters) or is under the control of a master
+ *	that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this
+ *	channel if it's connected concurrently to a BSS on the same channel on
+ *	the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ *	band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a
+ *	channel that has the GO_CONCURRENT attribute set can be done when there
+ *	is a clear assessment that the device is operating under the guidance of
+ *	an authorized master, i.e., setting up a GO while the device is also
+ *	connected to an AP with DFS and radar detection on the UNII band (it is
+ *	up to user-space, i.e., wpa_supplicant to perform the required
+ *	verifications)
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ *	on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *	currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_GO_CONCURRENT.
  */
 enum nl80211_frequency_attr {
 	__NL80211_FREQUENCY_ATTR_INVALID,
 	NL80211_FREQUENCY_ATTR_FREQ,
 	NL80211_FREQUENCY_ATTR_DISABLED,
-	NL80211_FREQUENCY_ATTR_PASSIVE_SCAN,
-	NL80211_FREQUENCY_ATTR_NO_IBSS,
+	NL80211_FREQUENCY_ATTR_NO_IR,
+	__NL80211_FREQUENCY_ATTR_NO_IBSS,
 	NL80211_FREQUENCY_ATTR_RADAR,
 	NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
 	NL80211_FREQUENCY_ATTR_DFS_STATE,
@@ -2176,6 +2385,11 @@
 	NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
 	NL80211_FREQUENCY_ATTR_NO_80MHZ,
 	NL80211_FREQUENCY_ATTR_NO_160MHZ,
+	NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+	NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+	NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+	NL80211_FREQUENCY_ATTR_NO_20MHZ,
+	NL80211_FREQUENCY_ATTR_NO_10MHZ,
 
 	/* keep last */
 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2183,6 +2397,9 @@
 };
 
 #define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN	NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IBSS		NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IR		NL80211_FREQUENCY_ATTR_NO_IR
 
 /**
  * enum nl80211_bitrate_attr - bitrate attributes
@@ -2263,12 +2480,14 @@
  * 	in KHz. This is not a center a frequency but an actual regulatory
  * 	band edge.
  * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
- * 	frequency range, in KHz.
+ *	frequency range, in KHz.
  * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
  * 	for a given frequency range. The value is in mBi (100 * dBi).
  * 	If you don't have one then don't send this.
  * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
  * 	a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ *	If not present or 0 default CAC time will be used.
  * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
  *	currently defined
  * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
@@ -2284,6 +2503,8 @@
 	NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
 	NL80211_ATTR_POWER_RULE_MAX_EIRP,
 
+	NL80211_ATTR_DFS_CAC_TIME,
+
 	/* keep last */
 	__NL80211_REG_RULE_ATTR_AFTER_LAST,
 	NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
@@ -2293,9 +2514,15 @@
  * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
- * only report BSS with matching SSID.
+ *	only report BSS with matching SSID.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
- *	BSS in scan results. Filtering is turned off if not specified.
+ *	BSS in scan results. Filtering is turned off if not specified. Note that
+ *	if this attribute is in a match set of its own, then it is treated as
+ *	the default value for all matchsets with an SSID, rather than being a
+ *	matchset of its own without an RSSI filter. This is due to problems with
+ *	how this API was implemented in the past. Also, due to the same problem,
+ *	the only way to create a matchset with only an RSSI filter (with this
+ *	attribute) is if there's only a single matchset with the RSSI attribute.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *	attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -2325,8 +2552,12 @@
  * @NL80211_RRF_DFS: DFS support is required to be used
  * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
  * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
- * @NL80211_RRF_PASSIVE_SCAN: passive scan is required
- * @NL80211_RRF_NO_IBSS: no IBSS is allowed
+ * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+ * 	this includes probe requests or modes of operation that require
+ * 	beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ *	base on contiguous rules and wider channels will be allowed to cross
+ *	multiple contiguous/overlapping frequency ranges.
  */
 enum nl80211_reg_rule_flags {
 	NL80211_RRF_NO_OFDM		= 1<<0,
@@ -2336,10 +2567,18 @@
 	NL80211_RRF_DFS			= 1<<4,
 	NL80211_RRF_PTP_ONLY		= 1<<5,
 	NL80211_RRF_PTMP_ONLY		= 1<<6,
-	NL80211_RRF_PASSIVE_SCAN	= 1<<7,
-	NL80211_RRF_NO_IBSS		= 1<<8,
+	NL80211_RRF_NO_IR		= 1<<7,
+	__NL80211_RRF_NO_IBSS		= 1<<8,
+	NL80211_RRF_AUTO_BW		= 1<<11,
 };
 
+#define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IBSS		NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IR		NL80211_RRF_NO_IR
+
+/* For backport compatibility with older userspace */
+#define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+
 /**
  * enum nl80211_dfs_regions - regulatory DFS regions
  *
@@ -2369,10 +2608,13 @@
  *	present has been registered with the wireless core that
  *	has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
  *	supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ *	platform is operating in an indoor environment.
  */
 enum nl80211_user_reg_hint_type {
 	NL80211_USER_REG_HINT_USER	= 0,
 	NL80211_USER_REG_HINT_CELL_BASE = 1,
+	NL80211_USER_REG_HINT_INDOOR    = 2,
 };
 
 /**
@@ -2428,6 +2670,8 @@
  * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering
  * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing.
  *	overrides all other flags.
+ * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
+ *	and ACK incoming unicast packets.
  *
  * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
  * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
@@ -2439,6 +2683,7 @@
 	NL80211_MNTR_FLAG_CONTROL,
 	NL80211_MNTR_FLAG_OTHER_BSS,
 	NL80211_MNTR_FLAG_COOK_FRAMES,
+	NL80211_MNTR_FLAG_ACTIVE,
 
 	/* keep last */
 	__NL80211_MNTR_FLAG_AFTER_LAST,
@@ -2574,6 +2819,10 @@
  *
  * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
  *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ *	established peering with for longer than this time (in seconds), then
+ *	remove it from the STA's list of peers.  Default is 30 minutes.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2605,6 +2854,7 @@
 	NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
 	NL80211_MESHCONF_POWER_MODE,
 	NL80211_MESHCONF_AWAKE_WINDOW,
+	NL80211_MESHCONF_PLINK_TIMEOUT,
 
 	/* keep last */
 	__NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2750,6 +3000,8 @@
  *	and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
  * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
  *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
  */
 enum nl80211_chan_width {
 	NL80211_CHAN_WIDTH_20_NOHT,
@@ -2758,6 +3010,23 @@
 	NL80211_CHAN_WIDTH_80,
 	NL80211_CHAN_WIDTH_80P80,
 	NL80211_CHAN_WIDTH_160,
+	NL80211_CHAN_WIDTH_5,
+	NL80211_CHAN_WIDTH_10,
+};
+
+/**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+	NL80211_BSS_CHAN_WIDTH_20,
+	NL80211_BSS_CHAN_WIDTH_10,
+	NL80211_BSS_CHAN_WIDTH_5,
 };
 
 /**
@@ -2784,6 +3053,8 @@
  * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
  *	elements from a Beacon frame (bin); not present if no Beacon frame has
  *	yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ *	(u32, enum nl80211_bss_scan_width)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -2800,6 +3071,7 @@
 	NL80211_BSS_STATUS,
 	NL80211_BSS_SEEN_MS_AGO,
 	NL80211_BSS_BEACON_IES,
+	NL80211_BSS_CHAN_WIDTH,
 
 	/* keep last */
 	__NL80211_BSS_AFTER_LAST,
@@ -2940,21 +3212,43 @@
  *	in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
  *	1 = 500 kbps) but without the IE length restriction (at most
  *	%NL80211_MAX_SUPP_RATES in a single array).
- * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection
+ * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection
  *	in an array of MCS numbers.
+ * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
+ *	see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
 enum nl80211_tx_rate_attributes {
 	__NL80211_TXRATE_INVALID,
 	NL80211_TXRATE_LEGACY,
-	NL80211_TXRATE_MCS,
+	NL80211_TXRATE_HT,
+	NL80211_TXRATE_VHT,
+	NL80211_TXRATE_GI,
 
 	/* keep last */
 	__NL80211_TXRATE_AFTER_LAST,
 	NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
 };
 
+#define NL80211_TXRATE_MCS NL80211_TXRATE_HT
+#define NL80211_VHT_NSS_MAX		8
+
+/**
+ * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_vht {
+	__u16 mcs[NL80211_VHT_NSS_MAX];
+};
+
+enum nl80211_txrate_gi {
+	NL80211_TXRATE_DEFAULT_GI,
+	NL80211_TXRATE_FORCE_SGI,
+	NL80211_TXRATE_FORCE_LGI,
+};
+
 /**
  * enum nl80211_band - Frequency band
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
@@ -3048,11 +3342,11 @@
 };
 
 /**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
  *	a zero bit are ignored
- * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
  *	a bit for each byte in the pattern. The lowest-order bit corresponds
  *	to the first byte of the pattern, but the bytes of the pattern are
  *	in a little-endian-like format, i.e. the 9th byte of the pattern
@@ -3063,39 +3357,50 @@
  *	Note that the pattern matching is done as though frames were not
  *	802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
  *	first (including SNAP header unpacking) and then matched.
- * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
  *	these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
  */
-enum nl80211_wowlan_packet_pattern_attr {
-	__NL80211_WOWLAN_PKTPAT_INVALID,
-	NL80211_WOWLAN_PKTPAT_MASK,
-	NL80211_WOWLAN_PKTPAT_PATTERN,
-	NL80211_WOWLAN_PKTPAT_OFFSET,
+enum nl80211_packet_pattern_attr {
+	__NL80211_PKTPAT_INVALID,
+	NL80211_PKTPAT_MASK,
+	NL80211_PKTPAT_PATTERN,
+	NL80211_PKTPAT_OFFSET,
 
-	NUM_NL80211_WOWLAN_PKTPAT,
-	MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+	NUM_NL80211_PKTPAT,
+	MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
 };
 
 /**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * struct nl80211_pattern_support - packet pattern support information
  * @max_patterns: maximum number of patterns supported
  * @min_pattern_len: minimum length of each pattern
  * @max_pattern_len: maximum length of each pattern
  * @max_pkt_offset: maximum Rx packet offset
  *
  * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
- * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
- * capability information given by the kernel to userspace.
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
  */
-struct nl80211_wowlan_pattern_support {
+struct nl80211_pattern_support {
 	__u32 max_patterns;
 	__u32 min_pattern_len;
 	__u32 max_pattern_len;
 	__u32 max_pkt_offset;
 } __attribute__((packed));
 
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
 /**
  * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
  * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
@@ -3115,7 +3420,7 @@
  *	pattern matching is done after the packet is converted to the MSDU.
  *
  *	In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
- *	carrying a &struct nl80211_wowlan_pattern_support.
+ *	carrying a &struct nl80211_pattern_support.
  *
  *	When reporting wakeup. it is a u32 attribute containing the 0-based
  *	index of the pattern that caused the wakeup, in the patterns passed
@@ -3272,7 +3577,7 @@
  * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
  *	u32 attribute holding the maximum length
  * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
- *	feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ *	feature advertising. The mask works like @NL80211_PKTPAT_MASK
  *	but on the TCP payload only.
  * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
  * @MAX_NL80211_WOWLAN_TCP: highest attribute number
@@ -3297,6 +3602,55 @@
 };
 
 /**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+	__u32 max_rules;
+	struct nl80211_pattern_support pat;
+	__u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ *	see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ *	after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+	__NL80211_COALESCE_RULE_INVALID,
+	NL80211_ATTR_COALESCE_RULE_DELAY,
+	NL80211_ATTR_COALESCE_RULE_CONDITION,
+	NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+	/* keep last */
+	NUM_NL80211_ATTR_COALESCE_RULE,
+	NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ *	in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ *	in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+	NL80211_COALESCE_CONDITION_MATCH,
+	NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
  * enum nl80211_iface_limit_attrs - limit attributes
  * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
  * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
@@ -3540,11 +3894,6 @@
  * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
  *	to work properly to suppport receiving regulatory hints from
  *	cellular base stations.
- * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active
- *	P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel
- *	in the interface combinations, even when it's only used for scan
- *	and remain-on-channel. This could be due to, for example, the
- *	remain-on-channel implementation requiring a channel context.
  * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
  *	equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
  *	mode
@@ -3576,13 +3925,20 @@
  *	Peering Management entity which may be implemented by registering for
  *	beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
  *	still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ *	interface. An active monitor interface behaves like a normal monitor
+ *	interface, but gets added to the driver. It ensures that incoming
+ *	unicast packets directed at the configured interface address get ACKed.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ *	channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ *	lifetime of a BSS.
  */
 enum nl80211_feature_flags {
 	NL80211_FEATURE_SK_TX_STATUS			= 1 << 0,
 	NL80211_FEATURE_HT_IBSS				= 1 << 1,
 	NL80211_FEATURE_INACTIVITY_TIMER		= 1 << 2,
 	NL80211_FEATURE_CELL_BASE_REG_HINTS		= 1 << 3,
-	NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL	= 1 << 4,
+	/* bit 4 is reserved - don't use */
 	NL80211_FEATURE_SAE				= 1 << 5,
 	NL80211_FEATURE_LOW_PRIORITY_SCAN		= 1 << 6,
 	NL80211_FEATURE_SCAN_FLUSH			= 1 << 7,
@@ -3595,6 +3951,8 @@
 	NL80211_FEATURE_ADVERTISE_CHAN_LIMITS		= 1 << 14,
 	NL80211_FEATURE_FULL_AP_CLIENT_STATE		= 1 << 15,
 	NL80211_FEATURE_USERSPACE_MPM			= 1 << 16,
+	NL80211_FEATURE_ACTIVE_MONITOR			= 1 << 17,
+	NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE	= 1 << 18,
 };
 
 /**
@@ -3695,13 +4053,12 @@
  *
  * Channel states used by the DFS code.
  *
- * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
  *	check (CAC) must be performed before using it for AP or IBSS.
- * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
  *	is therefore marked as not available.
- * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
  */
-
 enum nl80211_dfs_state {
 	NL80211_DFS_USABLE,
 	NL80211_DFS_UNAVAILABLE,
@@ -3741,4 +4098,51 @@
 /* maximum duration for critical protocol measures */
 #define NL80211_CRIT_PROTO_MAX_DURATION		5000 /* msec */
 
+/**
+ * enum nl80211_rxmgmt_flags - flags for received management frame.
+ *
+ * Used by cfg80211_rx_mgmt()
+ *
+ * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ */
+enum nl80211_rxmgmt_flags {
+	NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+};
+
+/*
+ * If this flag is unset, the lower 24 bits are an OUI, if set
+ * a Linux nl80211 vendor ID is used (no such IDs are allocated
+ * yet, so that's not valid so far)
+ */
+#define NL80211_VENDOR_ID_IS_LINUX	0x80000000
+
+/**
+ * struct nl80211_vendor_cmd_info - vendor command data
+ * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
+ *	value is a 24-bit OUI; if it is set then a separately allocated ID
+ *	may be used, but no such IDs are allocated yet. New IDs should be
+ *	added to this file when needed.
+ * @subcmd: sub-command ID for the command
+ */
+struct nl80211_vendor_cmd_info {
+	__u32 vendor_id;
+	__u32 subcmd;
+};
+
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ */
+enum nl80211_tdls_peer_capability {
+	NL80211_TDLS_PEER_HT = 1<<0,
+	NL80211_TDLS_PEER_VHT = 1<<1,
+	NL80211_TDLS_PEER_WMM = 1<<2,
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h
index 74d6ce5..6232088 100644
--- a/src/drivers/priv_netlink.h
+++ b/src/drivers/priv_netlink.h
@@ -69,6 +69,7 @@
 (struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
 #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
 #define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
+#define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0))
 
 
 struct sockaddr_nl
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
index 9c41962..adfd3df 100644
--- a/src/eap_common/Makefile
+++ b/src/eap_common/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
index f5890be..4f14a01 100644
--- a/src/eap_common/eap_defs.h
+++ b/src/eap_common/eap_defs.h
@@ -72,13 +72,16 @@
 enum {
 	EAP_VENDOR_IETF = 0,
 	EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
-	EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */,
-	EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */
+	EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance (moved to WBA) */,
+	EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */,
+	EAP_VENDOR_WFA_NEW = 40808 /* Wi-Fi Alliance */
 };
 
 #define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP
 #define EAP_VENDOR_TYPE_UNAUTH_TLS 1
 
+#define EAP_VENDOR_WFA_UNAUTH_TLS 13
+
 #define EAP_MSK_LEN 64
 #define EAP_EMSK_LEN 64
 
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index a62ac8e..4dfdb3f 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -692,7 +692,7 @@
 	if (eap_eke_mac(sess->mac, sess->ki, prot + block_size,
 			prot_len - block_size - icv_len, icv) < 0)
 		return -1;
-	if (os_memcmp(icv, prot + prot_len - icv_len, icv_len) != 0) {
+	if (os_memcmp_const(icv, prot + prot_len - icv_len, icv_len) != 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data");
 		return -1;
 	}
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index 04b987d..fceb1b0 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -174,7 +174,7 @@
 
 
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
-		       int tlv_type, u8 *pos, int len)
+		       int tlv_type, u8 *pos, size_t len)
 {
 	switch (tlv_type) {
 	case EAP_TLV_EAP_PAYLOAD_TLV:
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
index 8955617..d59a845 100644
--- a/src/eap_common/eap_fast_common.h
+++ b/src/eap_common/eap_fast_common.h
@@ -102,6 +102,6 @@
 void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
 void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
-		       int tlv_type, u8 *pos, int len);
+		       int tlv_type, u8 *pos, size_t len);
 
 #endif /* EAP_FAST_H */
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
index 7a33215..8c7ae27 100644
--- a/src/eap_common/eap_gpsk_common.c
+++ b/src/eap_common/eap_gpsk_common.c
@@ -284,7 +284,6 @@
 			 u8 *pk, size_t *pk_len)
 {
 	u8 *seed, *pos;
-	size_t seed_len;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
@@ -296,8 +295,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
 
 	/* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
-	seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
-	seed = os_malloc(seed_len);
+	seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
 	if (seed == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
 			   "for key derivation");
@@ -313,17 +311,18 @@
 	pos += EAP_GPSK_RAND_LEN;
 	os_memcpy(pos, id_server, id_server_len);
 	pos += id_server_len;
-	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
 
 	switch (specifier) {
 	case EAP_GPSK_CIPHER_AES:
-		ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
+		ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed,
 					       msk, emsk, sk, sk_len,
 					       pk, pk_len);
 		break;
 #ifdef EAP_GPSK_SHA256
 	case EAP_GPSK_CIPHER_SHA256:
-		ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
+		ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed,
+						  pos - seed,
 						  msk, emsk, sk, sk_len);
 		break;
 #endif /* EAP_GPSK_SHA256 */
@@ -423,7 +422,6 @@
 {
 	u8 *seed, *pos;
 	u8 kdf_out[16];
-	size_t seed_len;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
@@ -441,8 +439,7 @@
 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
 	 *                      CSuite_Sel || inputString)
 	 */
-	seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
-	seed = os_malloc(seed_len);
+	seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
 	if (seed == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
 			   "for Session-Id derivation");
@@ -458,11 +455,11 @@
 	pos += EAP_GPSK_RAND_LEN;
 	os_memcpy(pos, id_server, id_server_len);
 	pos += id_server_len;
-	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
 
 	ret = eap_gpsk_derive_mid_helper(specifier,
 					 kdf_out, sizeof(kdf_out),
-					 psk, seed, seed_len,
+					 psk, seed, pos - seed,
 					 method_type);
 
 	sid[0] = method_type;
diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c
index 6095fd8..585c79c 100644
--- a/src/eap_common/eap_ikev2_common.c
+++ b/src/eap_common/eap_ikev2_common.c
@@ -52,22 +52,12 @@
 {
 	struct wpabuf *msg;
 
-#ifdef CCNS_PL
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id);
-	if (msg == NULL) {
-		wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
-			   "for fragment ack");
-		return NULL;
-	}
-	wpabuf_put_u8(msg, 0); /* Flags */
-#else /* CCNS_PL */
 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id);
 	if (msg == NULL) {
 		wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
 			   "for fragment ack");
 		return NULL;
 	}
-#endif /* CCNS_PL */
 
 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
 
@@ -110,7 +100,7 @@
 		return -1;
 	}
 
-	if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
+	if (os_memcmp_const(icv, end - icv_len, icv_len) != 0) {
 		wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV");
 		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV",
 			    icv, icv_len);
diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h
index 329ccc4..e7502d7 100644
--- a/src/eap_common/eap_ikev2_common.h
+++ b/src/eap_common/eap_ikev2_common.h
@@ -9,16 +9,9 @@
 #ifndef EAP_IKEV2_COMMON_H
 #define EAP_IKEV2_COMMON_H
 
-#ifdef CCNS_PL
-/* incorrect bit order */
-#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01
-#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02
-#define IKEV2_FLAGS_ICV_INCLUDED 0x04
-#else /* CCNS_PL */
 #define IKEV2_FLAGS_LENGTH_INCLUDED 0x80
 #define IKEV2_FLAGS_MORE_FRAGMENTS 0x40
 #define IKEV2_FLAGS_ICV_INCLUDED 0x20
-#endif /* CCNS_PL */
 
 #define IKEV2_FRAGMENT_SIZE 1400
 
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 7d6e6b8..fdcff7f 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -263,18 +263,18 @@
  fail:
 		EC_GROUP_free(grp->group);
 		grp->group = NULL;
-		EC_POINT_free(grp->pwe);
+		EC_POINT_clear_free(grp->pwe);
 		grp->pwe = NULL;
-		BN_free(grp->order);
+		BN_clear_free(grp->order);
 		grp->order = NULL;
-		BN_free(grp->prime);
+		BN_clear_free(grp->prime);
 		grp->prime = NULL;
 		ret = 1;
 	}
 	/* cleanliness and order.... */
-	BN_free(cofactor);
-	BN_free(x_candidate);
-	BN_free(rnd);
+	BN_clear_free(cofactor);
+	BN_clear_free(x_candidate);
+	BN_clear_free(rnd);
 	os_free(prfbuf);
 
 	return ret;
@@ -284,11 +284,10 @@
 int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
 		 BIGNUM *peer_scalar, BIGNUM *server_scalar,
 		 u8 *confirm_peer, u8 *confirm_server,
-		 u32 *ciphersuite, u8 *msk, u8 *emsk)
+		 u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
 {
 	struct crypto_hash *hash;
 	u8 mk[SHA256_MAC_LEN], *cruft;
-	u8 session_id[SHA256_MAC_LEN + 1];
 	u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
 	int offset;
 
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index 816e58c..c54c441 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -59,7 +59,7 @@
 int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *,
 			     int, u8 *);
 int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
-		 u8 *, u8 *, u32 *, u8 *, u8 *);
+		 u8 *, u8 *, u32 *, u8 *, u8 *, u8 *);
 struct crypto_hash * eap_pwd_h_init(void);
 void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
 void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
index e1773bf..2adc3b3 100644
--- a/src/eap_common/eap_sim_common.c
+++ b/src/eap_common/eap_sim_common.c
@@ -198,7 +198,7 @@
 		    hmac, EAP_SIM_MAC_LEN);
 	os_free(tmp);
 
-	return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+	return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
 }
 
 
@@ -393,7 +393,7 @@
 		    hmac, EAP_SIM_MAC_LEN);
 	os_free(tmp);
 
-	return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+	return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
 }
 
 
@@ -893,7 +893,7 @@
 			if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
 				wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
 					   "AT_KDF attributes - ignore this");
-				continue;
+				break;
 			}
 			attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
 			attr->kdf_count++;
@@ -972,7 +972,6 @@
 struct eap_sim_msg {
 	struct wpabuf *buf;
 	size_t mac, iv, encr; /* index from buf */
-	int type;
 };
 
 
@@ -986,7 +985,6 @@
 	if (msg == NULL)
 		return NULL;
 
-	msg->type = type;
 	msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
 	if (msg->buf == NULL) {
 		os_free(msg);
@@ -1006,7 +1004,8 @@
 }
 
 
-struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
+				   const u8 *k_aut,
 				   const u8 *extra, size_t extra_len)
 {
 	struct eap_hdr *eap;
@@ -1019,7 +1018,7 @@
 	eap->length = host_to_be16(wpabuf_len(msg->buf));
 
 #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
-	if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) {
+	if (k_aut && msg->mac && type == EAP_TYPE_AKA_PRIME) {
 		eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
 				       wpabuf_len(msg->buf),
 				       (u8 *) wpabuf_mhead(msg->buf) +
diff --git a/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h
index 6021bd2..daeb0e2 100644
--- a/src/eap_common/eap_sim_common.h
+++ b/src/eap_common/eap_sim_common.h
@@ -211,7 +211,8 @@
 struct eap_sim_msg;
 
 struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype);
-struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
+				   const u8 *k_aut,
 				   const u8 *extra, size_t extra_len);
 void eap_sim_msg_free(struct eap_sim_msg *msg);
 u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
index 376fcad..3d4fb6f 100644
--- a/src/eap_common/ikev2_common.c
+++ b/src/eap_common/ikev2_common.c
@@ -21,7 +21,7 @@
 	{ AUTH_HMAC_MD5_96, 16, 12 }
 };
 
-#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0]))
+#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs)
 
 
 static struct ikev2_prf_alg ikev2_prf_algs[] = {
@@ -29,7 +29,7 @@
 	{ PRF_HMAC_MD5, 16, 16 }
 };
 
-#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0]))
+#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs)
 
 
 static struct ikev2_encr_alg ikev2_encr_algs[] = {
@@ -37,7 +37,7 @@
 	{ ENCR_3DES, 24, 8 }
 };
 
-#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0]))
+#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs)
 
 
 const struct ikev2_integ_alg * ikev2_get_integ(int id)
@@ -173,46 +173,12 @@
 }
 
 
-#ifdef CCNS_PL
-/* from des.c */
-struct des3_key_s {
-	u32 ek[3][32];
-	u32 dk[3][32];
-};
-
-void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
-void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
-void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
-#endif /* CCNS_PL */
-
-
 int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
 		       const u8 *plain, u8 *crypt, size_t len)
 {
 	struct crypto_cipher *cipher;
 	int encr_alg;
 
-#ifdef CCNS_PL
-	if (alg == ENCR_3DES) {
-		struct des3_key_s des3key;
-		size_t i, blocks;
-		u8 *pos;
-
-		/* ECB mode is used incorrectly for 3DES!? */
-		if (key_len != 24) {
-			wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
-			return -1;
-		}
-		des3_key_setup(key, &des3key);
-
-		blocks = len / 8;
-		pos = crypt;
-		for (i = 0; i < blocks; i++) {
-			des3_encrypt(pos, &des3key, pos);
-			pos += 8;
-		}
-	} else {
-#endif /* CCNS_PL */
 	switch (alg) {
 	case ENCR_3DES:
 		encr_alg = CRYPTO_CIPHER_ALG_3DES;
@@ -237,9 +203,6 @@
 		return -1;
 	}
 	crypto_cipher_deinit(cipher);
-#ifdef CCNS_PL
-	}
-#endif /* CCNS_PL */
 
 	return 0;
 }
@@ -251,31 +214,6 @@
 	struct crypto_cipher *cipher;
 	int encr_alg;
 
-#ifdef CCNS_PL
-	if (alg == ENCR_3DES) {
-		struct des3_key_s des3key;
-		size_t i, blocks;
-
-		/* ECB mode is used incorrectly for 3DES!? */
-		if (key_len != 24) {
-			wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
-			return -1;
-		}
-		des3_key_setup(key, &des3key);
-
-		if (len % 8) {
-			wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted "
-				   "length");
-			return -1;
-		}
-		blocks = len / 8;
-		for (i = 0; i < blocks; i++) {
-			des3_decrypt(crypt, &des3key, plain);
-			plain += 8;
-			crypt += 8;
-		}
-	} else {
-#endif /* CCNS_PL */
 	switch (alg) {
 	case ENCR_3DES:
 		encr_alg = CRYPTO_CIPHER_ALG_3DES;
@@ -300,9 +238,6 @@
 		return -1;
 	}
 	crypto_cipher_deinit(cipher);
-#ifdef CCNS_PL
-	}
-#endif /* CCNS_PL */
 
 	return 0;
 }
@@ -542,7 +477,7 @@
 			   "hash");
 		return NULL;
 	}
-	if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) {
+	if (os_memcmp_const(integ, hash, integ_alg->hash_len) != 0) {
 		wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
 			   "Data");
 		return NULL;
@@ -706,10 +641,6 @@
 	keys->SK_integ_len = integ->key_len;
 	keys->SK_encr_len = encr->key_len;
 	keys->SK_prf_len = prf->key_len;
-#ifdef CCNS_PL
-	/* Uses encryption key length for SK_d; should be PRF length */
-	keys->SK_d_len = keys->SK_encr_len;
-#endif /* CCNS_PL */
 
 	keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len +
 		2 * keys->SK_encr_len + 2 * keys->SK_prf_len;
diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h
index 45c970b..8a7982a 100644
--- a/src/eap_common/ikev2_common.h
+++ b/src/eap_common/ikev2_common.h
@@ -70,11 +70,7 @@
 /* Current IKEv2 version from RFC 4306 */
 #define IKEV2_MjVer 2
 #define IKEV2_MnVer 0
-#ifdef CCNS_PL
-#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4))
-#else /* CCNS_PL */
 #define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer))
-#endif /* CCNS_PL */
 
 /* IKEv2 Exchange Types */
 enum {
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
index 3651056..f79519b 100644
--- a/src/eap_peer/Makefile
+++ b/src/eap_peer/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.so *.d
+	rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov
 
 install:
 	if ls *.so >/dev/null 2>&1; then \
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 554e7e9..9880d3b 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-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -92,6 +92,15 @@
 }
 
 
+static void eap_sm_free_key(struct eap_sm *sm)
+{
+	if (sm->eapKeyData) {
+		bin_clear_free(sm->eapKeyData, sm->eapKeyDataLen);
+		sm->eapKeyData = NULL;
+	}
+}
+
+
 static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
 {
 	ext_password_free(sm->ext_pw_buf);
@@ -144,11 +153,13 @@
 	SM_ENTRY(EAP, INITIALIZE);
 	if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
 	    sm->m->has_reauth_data(sm, sm->eap_method_priv) &&
-	    !sm->prev_failure) {
+	    !sm->prev_failure &&
+	    sm->last_config == eap_get_config(sm)) {
 		wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
 			   "fast reauthentication");
 		sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
 	} else {
+		sm->last_config = eap_get_config(sm);
 		eap_deinit_prev_method(sm, "INITIALIZE");
 	}
 	sm->selectedMethod = EAP_TYPE_NONE;
@@ -159,8 +170,7 @@
 	eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
 	eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
 	eapol_set_bool(sm, EAPOL_eapFail, FALSE);
-	os_free(sm->eapKeyData);
-	sm->eapKeyData = NULL;
+	eap_sm_free_key(sm);
 	os_free(sm->eapSessionId);
 	sm->eapSessionId = NULL;
 	sm->eapKeyAvailable = FALSE;
@@ -179,6 +189,7 @@
 	eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
 	sm->num_rounds = 0;
 	sm->prev_failure = 0;
+	sm->expected_failure = 0;
 }
 
 
@@ -388,10 +399,11 @@
 	sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
 					 eapReqData);
 	wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
-		   "methodState=%s decision=%s",
+		   "methodState=%s decision=%s eapRespData=%p",
 		   ret.ignore ? "TRUE" : "FALSE",
 		   eap_sm_method_state_txt(ret.methodState),
-		   eap_sm_decision_txt(ret.decision));
+		   eap_sm_decision_txt(ret.decision),
+		   sm->eapRespData);
 
 	sm->ignore = ret.ignore;
 	if (sm->ignore)
@@ -402,7 +414,7 @@
 
 	if (sm->m->isKeyAvailable && sm->m->getKey &&
 	    sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
-		os_free(sm->eapKeyData);
+		eap_sm_free_key(sm);
 		sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
 					       &sm->eapKeyDataLen);
 		os_free(sm->eapSessionId);
@@ -432,8 +444,10 @@
 		sm->lastId = sm->reqId;
 		sm->lastRespData = wpabuf_dup(sm->eapRespData);
 		eapol_set_bool(sm, EAPOL_eapResp, TRUE);
-	} else
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP: No eapRespData available");
 		sm->lastRespData = NULL;
+	}
 	eapol_set_bool(sm, EAPOL_eapReq, FALSE);
 	eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
 }
@@ -724,8 +738,19 @@
 			SM_ENTER(EAP, SEND_RESPONSE);
 		break;
 	case EAP_METHOD:
+		/*
+		 * Note: RFC 4137 uses methodState == DONE && decision == FAIL
+		 * as the condition. eapRespData == NULL here is used to allow
+		 * final EAP method response to be sent without having to change
+		 * all methods to either use methodState MAY_CONT or leaving
+		 * decision to something else than FAIL in cases where the only
+		 * expected response is EAP-Failure.
+		 */
 		if (sm->ignore)
 			SM_ENTER(EAP, DISCARD);
+		else if (sm->methodState == METHOD_DONE &&
+			 sm->decision == DECISION_FAIL && !sm->eapRespData)
+			SM_ENTER(EAP, FAILURE);
 		else
 			SM_ENTER(EAP, SEND_RESPONSE);
 		break;
@@ -1473,8 +1498,7 @@
 	sm->lastRespData = NULL;
 	wpabuf_free(sm->eapRespData);
 	sm->eapRespData = NULL;
-	os_free(sm->eapKeyData);
-	sm->eapKeyData = NULL;
+	eap_sm_free_key(sm);
 	os_free(sm->eapSessionId);
 	sm->eapSessionId = NULL;
 
@@ -1638,7 +1662,8 @@
 			   const char *msg, size_t msglen)
 {
 	struct eap_peer_config *config;
-	char *txt = NULL, *tmp;
+	const char *txt = NULL;
+	char *tmp;
 
 	if (sm == NULL)
 		return;
@@ -1681,6 +1706,9 @@
 	case WPA_CTRL_REQ_EAP_PASSPHRASE:
 		config->pending_req_passphrase++;
 		break;
+	case WPA_CTRL_REQ_SIM:
+		txt = msg;
+		break;
 	default:
 		return;
 	}
@@ -1792,6 +1820,17 @@
 
 
 /**
+ * eap_sm_request_sim - Request external SIM processing
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @req: EAP method specific request
+ */
+void eap_sm_request_sim(struct eap_sm *sm, const char *req)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req));
+}
+
+
+/**
  * eap_sm_notify_ctrl_attached - Notification of attached monitor
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  *
@@ -2017,6 +2056,8 @@
 	if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
 		if (eap_get_ext_password(sm, config) < 0)
 			return NULL;
+		if (hash)
+			*hash = 0;
 		*len = wpabuf_len(sm->ext_pw_buf);
 		return wpabuf_head(sm->ext_pw_buf);
 	}
@@ -2304,6 +2345,17 @@
 }
 
 
+/**
+ * eap_set_external_sim - Set external_sim flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @external_sim: Whether external SIM/USIM processing is used
+ */
+void eap_set_external_sim(struct eap_sm *sm, int external_sim)
+{
+	sm->external_sim = external_sim;
+}
+
+
  /**
  * eap_notify_pending - Notify that EAP method is ready to re-process a request
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
@@ -2375,3 +2427,9 @@
 	if (sm->eapol_cb->set_anon_id)
 		sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len);
 }
+
+
+int eap_peer_was_failure_expected(struct eap_sm *sm)
+{
+	return sm->expected_failure;
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index f87f9b3..712e929 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -296,6 +296,7 @@
 void eap_sm_request_pin(struct eap_sm *sm);
 void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
 void eap_sm_request_passphrase(struct eap_sm *sm);
+void eap_sm_request_sim(struct eap_sm *sm, const char *req);
 void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
 u32 eap_get_phase2_type(const char *name, int *vendor);
 struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
@@ -303,6 +304,7 @@
 void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
 void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
 void eap_set_force_disabled(struct eap_sm *sm, int disabled);
+void eap_set_external_sim(struct eap_sm *sm, int external_sim);
 int eap_key_available(struct eap_sm *sm);
 void eap_notify_success(struct eap_sm *sm);
 void eap_notify_lower_layer_success(struct eap_sm *sm);
@@ -318,6 +320,7 @@
 struct ext_password_data;
 void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
 void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
+int eap_peer_was_failure_expected(struct eap_sm *sm);
 
 #endif /* IEEE8021X_EAPOL */
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index dc424d7..0662ae7 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -42,7 +42,7 @@
 	u8 *last_eap_identity;
 	size_t last_eap_identity_len;
 	enum {
-		CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
 	} state;
 
 	struct wpabuf *id_msgs;
@@ -64,8 +64,6 @@
 		return "CONTINUE";
 	case RESULT_SUCCESS:
 		return "RESULT_SUCCESS";
-	case RESULT_FAILURE:
-		return "RESULT_FAILURE";
 	case SUCCESS:
 		return "SUCCESS";
 	case FAILURE:
@@ -128,6 +126,21 @@
 #endif /* EAP_AKA_PRIME */
 
 
+static void eap_aka_clear_keys(struct eap_aka_data *data, int reauth)
+{
+	if (!reauth) {
+		os_memset(data->mk, 0, EAP_SIM_MK_LEN);
+		os_memset(data->k_aut, 0, EAP_AKA_PRIME_K_AUT_LEN);
+		os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN);
+		os_memset(data->k_re, 0, EAP_AKA_PRIME_K_RE_LEN);
+	}
+	os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN);
+	os_memset(data->emsk, 0, EAP_EMSK_LEN);
+	os_memset(data->autn, 0, EAP_AKA_AUTN_LEN);
+	os_memset(data->auts, 0, EAP_AKA_AUTS_LEN);
+}
+
+
 static void eap_aka_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_aka_data *data = priv;
@@ -137,11 +150,95 @@
 		os_free(data->last_eap_identity);
 		wpabuf_free(data->id_msgs);
 		os_free(data->network_name);
+		eap_aka_clear_keys(data, 0);
 		os_free(data);
 	}
 }
 
 
+static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data)
+{
+	char req[200], *pos, *end;
+
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing");
+	pos = req;
+	end = pos + sizeof(req);
+	pos += os_snprintf(pos, end - pos, "UMTS-AUTH");
+	pos += os_snprintf(pos, end - pos, ":");
+	pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN);
+	pos += os_snprintf(pos, end - pos, ":");
+	wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN);
+
+	eap_sm_request_sim(sm, req);
+	return 1;
+}
+
+
+static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data,
+				  struct eap_peer_config *conf)
+{
+	char *resp, *pos;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-AKA: Use result from external USIM processing");
+
+	resp = conf->external_sim_resp;
+	conf->external_sim_resp = NULL;
+
+	if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) {
+		pos = resp + 10;
+		if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0)
+			goto invalid;
+		wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts,
+				EAP_AKA_AUTS_LEN);
+		os_free(resp);
+		return -2;
+	}
+
+	if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response");
+		os_free(resp);
+		return -1;
+	}
+
+	pos = resp + 10;
+	wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN);
+
+	if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0)
+		goto invalid;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN);
+	pos += EAP_AKA_IK_LEN * 2;
+	if (*pos != ':')
+		goto invalid;
+	pos++;
+
+	if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0)
+		goto invalid;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN);
+	pos += EAP_AKA_CK_LEN * 2;
+	if (*pos != ':')
+		goto invalid;
+	pos++;
+
+	data->res_len = os_strlen(pos) / 2;
+	if (data->res_len > EAP_AKA_RES_MAX_LEN) {
+		data->res_len = 0;
+		goto invalid;
+	}
+	if (hexstr2bin(pos, data->res, data->res_len) < 0)
+		goto invalid;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len);
+
+	os_free(resp);
+	return 0;
+
+invalid:
+	wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response");
+	os_free(resp);
+	return -1;
+}
+
+
 static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
 {
 	struct eap_peer_config *conf;
@@ -151,6 +248,14 @@
 	conf = eap_get_config(sm);
 	if (conf == NULL)
 		return -1;
+
+	if (sm->external_sim) {
+		if (conf->external_sim_resp)
+			return eap_aka_ext_sim_result(sm, data, conf);
+		else
+			return eap_aka_ext_sim_req(sm, data);
+	}
+
 	if (conf->pcsc) {
 		return scard_umts_auth(sm->scard_ctx, data->rand,
 				       data->autn, data->res, &data->res_len,
@@ -205,7 +310,7 @@
 	{
 		u8 autn[EAP_AKA_AUTN_LEN];
 		os_memset(autn, '1', EAP_AKA_AUTN_LEN);
-		if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
+		if (os_memcmp_const(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
 			wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
 				   "with expected value");
 			return -1;
@@ -225,7 +330,7 @@
 
 #else /* CONFIG_USIM_HARDCODED */
 
-	wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith "
+	wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm "
 		   "enabled");
 	return -1;
 
@@ -420,7 +525,7 @@
 #endif /* EAP_AKA_PRIME */
 		sha1_vector(1, &addr, &len, hash);
 
-	if (os_memcmp(hash, checkcode, hash_len) != 0) {
+	if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
 		return -1;
 	}
@@ -443,7 +548,7 @@
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 			       EAP_AKA_SUBTYPE_CLIENT_ERROR);
 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -460,7 +565,7 @@
 		   "(id=%d)", id);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 			       EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -479,7 +584,7 @@
 	wpa_printf(MSG_DEBUG, "   AT_AUTS");
 	eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
 			     EAP_AKA_AUTS_LEN);
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -523,7 +628,7 @@
 				identity, identity_len);
 	}
 
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -545,7 +650,8 @@
 	}
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "",
+				  0);
 }
 
 
@@ -587,7 +693,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s,
 				  EAP_SIM_NONCE_S_LEN);
 }
 
@@ -621,7 +727,7 @@
 		wpa_printf(MSG_DEBUG, "   AT_MAC");
 		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 	}
-	return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+	return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0);
 }
 
 
@@ -701,7 +807,7 @@
 			       EAP_AKA_SUBTYPE_CHALLENGE);
 	wpa_printf(MSG_DEBUG, "   AT_KDF");
 	eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0);
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -861,6 +967,9 @@
 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
 			   "failed (AUTN seq# -> AUTS)");
 		return eap_aka_synchronization_failure(data, id);
+	} else if (res > 0) {
+		wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
+		return NULL;
 	} else if (res) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
 		return eap_aka_client_error(data, id,
@@ -931,7 +1040,7 @@
 	if (data->result_ind && attr->result_ind)
 		data->use_result_ind = 1;
 
-	if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+	if (data->state != FAILURE) {
 		eap_aka_state(data, data->use_result_ind ?
 			      RESULT_SUCCESS : SUCCESS);
 	}
@@ -1147,7 +1256,7 @@
 	if (data->result_ind && attr->result_ind)
 		data->use_result_ind = 1;
 
-	if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+	if (data->state != FAILURE) {
 		eap_aka_state(data, data->use_result_ind ?
 			      RESULT_SUCCESS : SUCCESS);
 	}
@@ -1253,9 +1362,7 @@
 		 */
 		ret->methodState = data->use_result_ind ?
 			METHOD_DONE : METHOD_MAY_CONT;
-	} else if (data->state == RESULT_FAILURE)
-		ret->methodState = METHOD_CONT;
-	else if (data->state == RESULT_SUCCESS)
+	} else if (data->state == RESULT_SUCCESS)
 		ret->methodState = METHOD_CONT;
 
 	if (ret->methodState == METHOD_DONE) {
@@ -1282,6 +1389,7 @@
 	data->id_msgs = NULL;
 	data->use_result_ind = 0;
 	data->kdf_negotiation = 0;
+	eap_aka_clear_keys(data, 1);
 }
 
 
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 42f525b..2591e11 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -157,7 +157,7 @@
 	 *
 	 * If left out, this will be asked through control interface.
 	 */
-	u8 *private_key_passwd;
+	char *private_key_passwd;
 
 	/**
 	 * dh_file - File path to DH/DSA parameters file (in PEM format)
@@ -208,6 +208,24 @@
 	u8 *altsubject_match;
 
 	/**
+	 * domain_suffix_match - Constraint for server domain name
+	 *
+	 * If set, this FQDN is used as a suffix match requirement for the
+	 * server certificate in SubjectAltName dNSName element(s). If a
+	 * matching dNSName is found, this constraint is met. If no dNSName
+	 * values are present, this constraint is matched against SubjetName CN
+	 * using same suffix match comparison. Suffix match here means that the
+	 * host/domain name is compared one label at a time starting from the
+	 * top-level domain and all the labels in domain_suffix_match shall be
+	 * included in the certificate. The certificate may include additional
+	 * sub-level labels in addition to the required labels.
+	 *
+	 * For example, domain_suffix_match=example.com would match
+	 * test.example.com but would not match test-example.com.
+	 */
+	char *domain_suffix_match;
+
+	/**
 	 * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
 	 *
 	 * This file can have one or more trusted CA certificates. If ca_cert2
@@ -271,7 +289,7 @@
 	 * This field is like private_key_passwd, but used for phase 2 (inside
 	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
 	 */
-	u8 *private_key2_passwd;
+	char *private_key2_passwd;
 
 	/**
 	 * dh_file2 - File path to DH/DSA parameters file (in PEM format)
@@ -303,6 +321,14 @@
 	u8 *altsubject_match2;
 
 	/**
+	 * domain_suffix_match2 - Constraint for server domain name
+	 *
+	 * This field is like domain_suffix_match, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	char *domain_suffix_match2;
+
+	/**
 	 * eap_methods - Allowed EAP methods
 	 *
 	 * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
@@ -643,6 +669,23 @@
 	 * 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
+	 * internally when control interface is used to request external
+	 * SIM/USIM processing.
+	 */
+	char *external_sim_resp;
+
+	/**
+	 * sim_num - User selected SIM identifier
+	 *
+	 * This variable is used for identifying which SIM is used if the system
+	 * has more than one.
+	 */
+	int sim_num;
 };
 
 
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index c71db5f..9fec66c 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -28,6 +28,10 @@
 	u8 nonce_p[EAP_EKE_MAX_NONCE_LEN];
 	u8 nonce_s[EAP_EKE_MAX_NONCE_LEN];
 	struct wpabuf *msgs;
+	u8 dhgroup; /* forced DH group or 0 to allow all supported */
+	u8 encr; /* forced encryption algorithm or 0 to allow all supported */
+	u8 prf; /* forced PRF or 0 to allow all supported */
+	u8 mac; /* forced MAC or 0 to allow all supported */
 };
 
 
@@ -66,6 +70,7 @@
 	struct eap_eke_data *data;
 	const u8 *identity, *password;
 	size_t identity_len, password_len;
+	const char *phase1;
 
 	password = eap_get_config_password(sm, &password_len);
 	if (!password) {
@@ -89,6 +94,39 @@
 		data->peerid_len = identity_len;
 	}
 
+	phase1 = eap_get_config_phase1(sm);
+	if (phase1) {
+		const char *pos;
+
+		pos = os_strstr(phase1, "dhgroup=");
+		if (pos) {
+			data->dhgroup = atoi(pos + 8);
+			wpa_printf(MSG_DEBUG, "EAP-EKE: Forced dhgroup %u",
+				   data->dhgroup);
+		}
+
+		pos = os_strstr(phase1, "encr=");
+		if (pos) {
+			data->encr = atoi(pos + 5);
+			wpa_printf(MSG_DEBUG, "EAP-EKE: Forced encr %u",
+				   data->encr);
+		}
+
+		pos = os_strstr(phase1, "prf=");
+		if (pos) {
+			data->prf = atoi(pos + 4);
+			wpa_printf(MSG_DEBUG, "EAP-EKE: Forced prf %u",
+				   data->prf);
+		}
+
+		pos = os_strstr(phase1, "mac=");
+		if (pos) {
+			data->mac = atoi(pos + 4);
+			wpa_printf(MSG_DEBUG, "EAP-EKE: Forced mac %u",
+				   data->mac);
+		}
+	}
+
 	return data;
 }
 
@@ -100,7 +138,7 @@
 	os_free(data->serverid);
 	os_free(data->peerid);
 	wpabuf_free(data->msgs);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -226,16 +264,20 @@
 			   i, pos[0], pos[1], pos[2], pos[3]);
 		pos += 4;
 
-		if (!eap_eke_supp_dhgroup(*tmp))
+		if ((data->dhgroup && data->dhgroup != *tmp) ||
+		    !eap_eke_supp_dhgroup(*tmp))
 			continue;
 		tmp++;
-		if (!eap_eke_supp_encr(*tmp))
+		if ((data->encr && data->encr != *tmp) ||
+		    !eap_eke_supp_encr(*tmp))
 			continue;
 		tmp++;
-		if (!eap_eke_supp_prf(*tmp))
+		if ((data->prf && data->prf != *tmp) ||
+		    !eap_eke_supp_prf(*tmp))
 			continue;
 		tmp++;
-		if (!eap_eke_supp_mac(*tmp))
+		if ((data->mac && data->mac != *tmp) ||
+		    !eap_eke_supp_mac(*tmp))
 			continue;
 
 		prop = tmp - 3;
@@ -409,7 +451,7 @@
 	/* DHComponent_P = Encr(key, y_p) */
 	rpos = wpabuf_put(resp, data->sess.dhcomp_len);
 	if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
-		wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S");
+		wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
 		os_memset(key, 0, sizeof(key));
 		return eap_eke_build_fail(data, ret, reqData,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
@@ -481,7 +523,7 @@
 	end = payload + payload_len;
 
 	if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) {
-		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
+		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
 		return eap_eke_build_fail(data, ret, reqData,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
@@ -501,7 +543,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S",
 			nonces, 2 * data->sess.nonce_len);
 	if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) {
-		wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match trnsmitted Nonce_P");
+		wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P");
 		return eap_eke_build_fail(data, ret, reqData,
 					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
 	}
@@ -524,8 +566,8 @@
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len);
-	if (os_memcmp(auth_s, pos + data->sess.pnonce_ps_len,
-		      data->sess.prf_len) != 0) {
+	if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len,
+			    data->sess.prf_len) != 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match");
 		return eap_eke_build_fail(data, ret, reqData,
 					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 3b8d803..0739187 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -149,14 +149,16 @@
 	struct eap_fast_data *data;
 	struct eap_peer_config *config = eap_get_config(sm);
 
+	if (config == NULL)
+		return NULL;
+
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
 		return NULL;
 	data->fast_version = EAP_FAST_VERSION;
 	data->max_pac_list_len = 10;
 
-	if (config && config->phase1 &&
-	    eap_fast_parse_phase1(data, config->phase1) < 0) {
+	if (config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) {
 		eap_fast_deinit(sm, data);
 		return NULL;
 	}
@@ -196,14 +198,22 @@
 			   "workarounds");
 	}
 
+	if (!config->pac_file) {
+		wpa_printf(MSG_INFO, "EAP-FAST: No PAC file configured");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
 	if (data->use_pac_binary_format &&
 	    eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file");
 		eap_fast_deinit(sm, data);
 		return NULL;
 	}
 
 	if (!data->use_pac_binary_format &&
 	    eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file");
 		eap_fast_deinit(sm, data);
 		return NULL;
 	}
@@ -240,6 +250,8 @@
 		pac = pac->next;
 		eap_fast_free_pac(prev);
 	}
+	os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+	os_memset(data->emsk, 0, EAP_EMSK_LEN);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
 	os_free(data);
@@ -757,7 +769,7 @@
 		    "MAC calculation", (u8 *) _bind, bind_len);
 	hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len,
 		  _bind->compound_mac);
-	res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac));
+	res = os_memcmp_const(cmac, _bind->compound_mac, sizeof(cmac));
 	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC",
 		    cmac, sizeof(cmac));
 	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC",
@@ -1047,6 +1059,7 @@
 		}
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
 			   "- Provisioning completed successfully");
+		sm->expected_failure = 1;
 	} else {
 		/*
 		 * This is PAC refreshing, i.e., normal authentication that is
@@ -1069,7 +1082,8 @@
 				    struct eap_fast_tlv_parse *tlv,
 				    struct wpabuf **resp)
 {
-	int mandatory, tlv_type, len, res;
+	int mandatory, tlv_type, res;
+	size_t len;
 	u8 *pos, *end;
 
 	os_memset(tlv, 0, sizeof(*tlv));
@@ -1083,13 +1097,14 @@
 		pos += 2;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end) {
+		if (len > (size_t) (end - pos)) {
 			wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
 			return -1;
 		}
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
-			   "TLV type %d length %d%s",
-			   tlv_type, len, mandatory ? " (mandatory)" : "");
+			   "TLV type %d length %u%s",
+			   tlv_type, (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
 
 		res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
 		if (res == -2)
@@ -1244,6 +1259,7 @@
 				   "provisioning completed successfully.");
 			ret->methodState = METHOD_DONE;
 			ret->decision = DECISION_FAIL;
+			sm->expected_failure = 1;
 		} else {
 			wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
 				   "completed successfully.");
@@ -1622,6 +1638,8 @@
 		os_free(data);
 		return NULL;
 	}
+	os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+	os_memset(data->emsk, 0, EAP_EMSK_LEN);
 	os_free(data->session_id);
 	data->session_id = NULL;
 	if (data->phase2_priv && data->phase2_method &&
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index 8c480b9..21d6098 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -330,6 +330,8 @@
 static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
 					    char *pos)
 {
+	if (!pos)
+		return "Cannot parse pac type";
 	pac->pac_type = atoi(pos);
 	if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
 	    pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index 8a0644d..c54bf11 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-GPSK (RFC 5433)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -33,6 +33,7 @@
 	int specifier; /* CSuite/Specifier */
 	u8 *psk;
 	size_t psk_len;
+	u16 forced_cipher; /* force cipher or 0 to allow all supported */
 };
 
 
@@ -80,6 +81,7 @@
 	struct eap_gpsk_data *data;
 	const u8 *identity, *password;
 	size_t identity_len, password_len;
+	const char *phase1;
 
 	password = eap_get_config_password(sm, &password_len);
 	if (password == NULL) {
@@ -103,6 +105,18 @@
 		data->id_peer_len = identity_len;
 	}
 
+	phase1 = eap_get_config_phase1(sm);
+	if (phase1) {
+		const char *pos;
+
+		pos = os_strstr(phase1, "cipher=");
+		if (pos) {
+			data->forced_cipher = atoi(pos + 7);
+			wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u",
+				   data->forced_cipher);
+		}
+	}
+
 	data->psk = os_malloc(password_len);
 	if (data->psk == NULL) {
 		eap_gpsk_deinit(sm, data);
@@ -120,8 +134,11 @@
 	struct eap_gpsk_data *data = priv;
 	os_free(data->id_server);
 	os_free(data->id_peer);
-	os_free(data->psk);
-	os_free(data);
+	if (data->psk) {
+		os_memset(data->psk, 0, data->psk_len);
+		os_free(data->psk);
+	}
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -195,7 +212,9 @@
 			   i, vendor, specifier);
 		if (data->vendor == EAP_GPSK_VENDOR_IETF &&
 		    data->specifier == EAP_GPSK_CIPHER_RESERVED &&
-		    eap_gpsk_supported_ciphersuite(vendor, specifier)) {
+		    eap_gpsk_supported_ciphersuite(vendor, specifier) &&
+		    (!data->forced_cipher || data->forced_cipher == specifier))
+		{
 			data->vendor = vendor;
 			data->specifier = specifier;
 		}
@@ -220,6 +239,8 @@
 					       size_t *list_len,
 					       const u8 *pos, const u8 *end)
 {
+	size_t len;
+
 	if (pos == NULL)
 		return NULL;
 
@@ -227,23 +248,25 @@
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
 		return NULL;
 	}
-	*list_len = WPA_GET_BE16(pos);
+	len = WPA_GET_BE16(pos);
 	pos += 2;
-	if (end - pos < (int) *list_len) {
+	if (len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
 		return NULL;
 	}
-	if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+	if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
-			   (unsigned long) *list_len);
+			   (unsigned long) len);
 		return NULL;
 	}
-	*list = pos;
-	pos += *list_len;
 
-	if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+	if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
 		return NULL;
 
+	*list = pos;
+	*list_len = len;
+	pos += len;
+
 	return pos;
 }
 
@@ -273,6 +296,7 @@
 	pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
 					   &csuite_list_len, pos, end);
 	if (pos == NULL) {
+		ret->methodState = METHOD_DONE;
 		eap_gpsk_state(data, FAILURE);
 		return NULL;
 	}
@@ -544,7 +568,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
 		return NULL;
 	}
-	if (os_memcmp(mic, pos, miclen) != 0) {
+	if (os_memcmp_const(mic, pos, miclen) != 0) {
 		wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 62c867c..fde809c 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -345,9 +345,14 @@
 	struct wps_context *wps;
 
 	int prev_failure;
+	struct eap_peer_config *last_config;
 
 	struct ext_password_data *ext_pw;
 	struct wpabuf *ext_pw_buf;
+
+	int external_sim;
+
+	unsigned int expected_failure:1;
 };
 
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
index 09a655e..c12b519 100644
--- a/src/eap_peer/eap_ikev2.c
+++ b/src/eap_peer/eap_ikev2.c
@@ -1,6 +1,6 @@
 /*
  * EAP-IKEv2 peer (RFC 5106)
- * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -60,6 +60,7 @@
 	struct eap_ikev2_data *data;
 	const u8 *identity, *password;
 	size_t identity_len, password_len;
+	int fragment_size;
 
 	identity = eap_get_config_identity(sm, &identity_len);
 	if (identity == NULL) {
@@ -71,7 +72,11 @@
 	if (data == NULL)
 		return NULL;
 	data->state = WAIT_START;
-	data->fragment_size = IKEV2_FRAGMENT_SIZE;
+	fragment_size = eap_get_config_fragment_size(sm);
+	if (fragment_size <= 0)
+		data->fragment_size = IKEV2_FRAGMENT_SIZE;
+	else
+		data->fragment_size = 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");
@@ -108,7 +113,7 @@
 	wpabuf_free(data->in_buf);
 	wpabuf_free(data->out_buf);
 	ikev2_responder_deinit(&data->ikev2);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -149,12 +154,6 @@
 			send_len -= 4;
 		}
 	}
-#ifdef CCNS_PL
-	/* Some issues figuring out the length of the message if Message Length
-	 * field not included?! */
-	if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED))
-		flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
-#endif /* CCNS_PL */
 
 	plen = 1 + send_len;
 	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
@@ -246,7 +245,8 @@
 
 static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
 				 const struct wpabuf *reqData,
-				 u8 flags, const u8 *pos, const u8 **end)
+				 u8 flags, const u8 *pos, const u8 **end,
+				 int frag_ack)
 {
 	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
 		int icv_len = eap_ikev2_validate_icv(
@@ -256,7 +256,7 @@
 			return -1;
 		/* Hide Integrity Checksum Data from further processing */
 		*end -= icv_len;
-	} else if (data->keys_ready) {
+	} else if (data->keys_ready && !frag_ack) {
 		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
 			   "included integrity checksum");
 		return -1;
@@ -346,7 +346,9 @@
 	else
 		flags = *pos++;
 
-	if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) {
+	if (eap_ikev2_process_icv(data, reqData, flags, pos, &end,
+				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
+	{
 		ret->ignore = TRUE;
 		return NULL;
 	}
@@ -373,12 +375,7 @@
 		   "Message Length %u", flags, message_length);
 
 	if (data->state == WAIT_FRAG_ACK) {
-#ifdef CCNS_PL
-		if (len > 1) /* Empty Flags field included in ACK */
-#else /* CCNS_PL */
-		if (len != 0)
-#endif /* CCNS_PL */
-		{
+		if (len != 0) {
 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
 				   "in WAIT_FRAG_ACK state");
 			ret->ignore = TRUE;
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
index df34013..e0f8bcf 100644
--- a/src/eap_peer/eap_leap.c
+++ b/src/eap_peer/eap_leap.c
@@ -244,7 +244,7 @@
 	ret->methodState = METHOD_DONE;
 	ret->allowNotifications = FALSE;
 
-	if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
+	if (os_memcmp_const(pos, expected, LEAP_RESPONSE_LEN) != 0) {
 		wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
 			   "response - authentication failed");
 		wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
@@ -383,6 +383,9 @@
 	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));
+
 	return key;
 }
 
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
index a465fd2..e35c919 100644
--- a/src/eap_peer/eap_methods.h
+++ b/src/eap_peer/eap_methods.h
@@ -86,6 +86,7 @@
 int eap_peer_md5_register(void);
 int eap_peer_tls_register(void);
 int eap_peer_unauth_tls_register(void);
+int eap_peer_wfa_unauth_tls_register(void);
 int eap_peer_mschapv2_register(void);
 int eap_peer_peap_register(void);
 int eap_peer_ttls_register(void);
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index f9aa742..430c501 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -140,7 +140,7 @@
 	os_free(data->peer_challenge);
 	os_free(data->auth_challenge);
 	wpabuf_free(data->prev_challenge);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -303,18 +303,23 @@
 			WPA_EVENT_PASSWORD_CHANGED
 			"EAP-MSCHAPV2: Password changed successfully");
 		data->prev_error = 0;
-		os_free(config->password);
+		bin_clear_free(config->password, config->password_len);
 		if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
 			/* TODO: update external storage */
 		} else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
 			config->password = os_malloc(16);
 			config->password_len = 16;
-			if (config->password) {
-				nt_password_hash(config->new_password,
-						 config->new_password_len,
-						 config->password);
+			if (config->password &&
+			    nt_password_hash(config->new_password,
+					     config->new_password_len,
+					     config->password)) {
+				bin_clear_free(config->password,
+					       config->password_len);
+				config->password = NULL;
+				config->password_len = 0;
 			}
-			os_free(config->new_password);
+			bin_clear_free(config->new_password,
+				       config->new_password_len);
 		} else {
 			config->password = config->new_password;
 			config->password_len = config->new_password_len;
@@ -549,15 +554,17 @@
 	/* Encrypted-Hash */
 	if (pwhash) {
 		u8 new_password_hash[16];
-		nt_password_hash(new_password, new_password_len,
-				 new_password_hash);
+		if (nt_password_hash(new_password, new_password_len,
+				     new_password_hash))
+			goto fail;
 		nt_password_hash_encrypted_with_block(password,
 						      new_password_hash,
 						      cp->encr_hash);
 	} else {
-		old_nt_password_hash_encrypted_with_new_nt_password_hash(
-			new_password, new_password_len,
-			password, password_len, cp->encr_hash);
+		if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
+			    new_password, new_password_len,
+			    password, password_len, cp->encr_hash))
+			goto fail;
 	}
 
 	/* Peer-Challenge */
@@ -594,9 +601,13 @@
 
 	/* Likewise, generate master_key here since we have the needed data
 	 * available. */
-	nt_password_hash(new_password, new_password_len, password_hash);
-	hash_nt_password_hash(password_hash, password_hash_hash);
-	get_master_key(password_hash_hash, cp->nt_response, data->master_key);
+	if (nt_password_hash(new_password, new_password_len, password_hash) ||
+	    hash_nt_password_hash(password_hash, password_hash_hash) ||
+	    get_master_key(password_hash_hash, cp->nt_response,
+			   data->master_key)) {
+		data->auth_response_valid = 0;
+		goto fail;
+	}
 	data->master_key_valid = 1;
 
 	/* Flags */
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index 7f87052..1c111c2 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -86,7 +86,7 @@
 {
 	struct eap_pax_data *data = priv;
 	os_free(data->cid);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -278,7 +278,7 @@
 	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, mac);
-	if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) {
+	if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
 			   "received");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
@@ -415,7 +415,7 @@
 			    wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
 			    icvbuf);
 	}
-	if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
+	if (os_memcmp_const(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the "
 			   "message");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV",
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 3b93209..472e861 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -22,7 +22,6 @@
 /* Maximum supported PEAP version
  * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
  * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
- * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
  */
 #define EAP_PEAP_VERSION 1
 
@@ -171,6 +170,15 @@
 }
 
 
+static void eap_peap_free_key(struct eap_peap_data *data)
+{
+	if (data->key_data) {
+		bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+		data->key_data = NULL;
+	}
+}
+
+
 static void eap_peap_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_peap_data *data = priv;
@@ -180,7 +188,7 @@
 		data->phase2_method->deinit(sm, data->phase2_priv);
 	os_free(data->phase2_types);
 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
-	os_free(data->key_data);
+	eap_peap_free_key(data);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
 	os_free(data);
@@ -315,8 +323,6 @@
 	len[1] = 1;
 
 	tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
-	if (data->peap_version >= 2)
-		tlv_type |= EAP_TLV_TYPE_MANDATORY;
 	wpabuf_put_be16(buf, tlv_type);
 	wpabuf_put_be16(buf, 56);
 
@@ -426,7 +432,7 @@
 		    buf, sizeof(buf));
 	hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
 
-	if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+	if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
 			   "cryptobinding TLV");
 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",
@@ -580,33 +586,6 @@
 }
 
 
-static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
-{
-	struct wpabuf *e;
-	struct eap_tlv_hdr *tlv;
-
-	if (buf == NULL)
-		return NULL;
-
-	/* Encapsulate EAP packet in EAP-Payload TLV */
-	wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
-	e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
-	if (e == NULL) {
-		wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
-			   "for TLV encapsulation");
-		wpabuf_free(buf);
-		return NULL;
-	}
-	tlv = wpabuf_put(e, sizeof(*tlv));
-	tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
-				     EAP_TLV_EAP_PAYLOAD_TLV);
-	tlv->length = host_to_be16(wpabuf_len(buf));
-	wpabuf_put_buf(e, buf);
-	wpabuf_free(buf);
-	return e;
-}
-
-
 static int eap_peap_phase2_request(struct eap_sm *sm,
 				   struct eap_peap_data *data,
 				   struct eap_method_ret *ret,
@@ -837,49 +816,6 @@
 		in_decrypted = nmsg;
 	}
 
-	if (data->peap_version >= 2) {
-		struct eap_tlv_hdr *tlv;
-		struct wpabuf *nmsg;
-
-		if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
-				   "EAP TLV");
-			wpabuf_free(in_decrypted);
-			return 0;
-		}
-		tlv = wpabuf_mhead(in_decrypted);
-		if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
-		    EAP_TLV_EAP_PAYLOAD_TLV) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
-			wpabuf_free(in_decrypted);
-			return 0;
-		}
-		if (sizeof(*tlv) + be_to_host16(tlv->length) >
-		    wpabuf_len(in_decrypted)) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
-				   "length");
-			wpabuf_free(in_decrypted);
-			return 0;
-		}
-		hdr = (struct eap_hdr *) (tlv + 1);
-		if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
-				   "EAP packet in EAP TLV");
-			wpabuf_free(in_decrypted);
-			return 0;
-		}
-
-		nmsg = wpabuf_alloc(be_to_host16(hdr->length));
-		if (nmsg == NULL) {
-			wpabuf_free(in_decrypted);
-			return 0;
-		}
-
-		wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
-		wpabuf_free(in_decrypted);
-		in_decrypted = nmsg;
-	}
-
 	hdr = wpabuf_mhead(in_decrypted);
 	if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
 		wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
@@ -996,11 +932,6 @@
 		wpa_hexdump_buf_key(MSG_DEBUG,
 				    "EAP-PEAP: Encrypting Phase 2 data", resp);
 		/* PEAP version changes */
-		if (data->peap_version >= 2) {
-			resp = eap_peapv2_tlv_eap_payload(resp);
-			if (resp == NULL)
-				return -1;
-		}
 		if (wpabuf_len(resp) >= 5 &&
 		    wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
 		    eap_get_type(resp) == EAP_TYPE_TLV)
@@ -1083,7 +1014,7 @@
 			char *label;
 			wpa_printf(MSG_DEBUG,
 				   "EAP-PEAP: TLS done, proceed to Phase 2");
-			os_free(data->key_data);
+			eap_peap_free_key(data);
 			/* draft-josefsson-ppext-eap-tls-eap-05.txt
 			 * specifies that PEAPv1 would use "client PEAP
 			 * encryption" as the label. However, most existing
@@ -1091,7 +1022,7 @@
 			 * label, "client EAP encryption", instead. Use the old
 			 * label by default, but allow it to be configured with
 			 * phase1 parameter peaplabel=1. */
-			if (data->peap_version > 1 || data->force_new_label)
+			if (data->force_new_label)
 				label = "client PEAP encryption";
 			else
 				label = "client EAP encryption";
@@ -1193,8 +1124,7 @@
 static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_peap_data *data = priv;
-	os_free(data->key_data);
-	data->key_data = NULL;
+	eap_peap_free_key(data);
 	os_free(data->session_id);
 	data->session_id = NULL;
 	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h
index 3b4dcef..23cdbe6 100644
--- a/src/eap_peer/eap_proxy.h
+++ b/src/eap_peer/eap_proxy.h
@@ -40,7 +40,8 @@
 int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
 			    int verbose);
 
-int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len);
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
+		       size_t *imsi_len);
 
 int eap_proxy_notify_config(struct eap_proxy_sm *sm,
 			    struct eap_peer_config *config);
diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c
index cd97fb6..d84f012 100644
--- a/src/eap_peer/eap_proxy_dummy.c
+++ b/src/eap_peer/eap_proxy_dummy.c
@@ -63,7 +63,8 @@
 }
 
 
-int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len)
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
+		       size_t *imsi_len)
 {
 	return -1;
 }
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
index cd0e3f9..f012663 100644
--- a/src/eap_peer/eap_psk.c
+++ b/src/eap_peer/eap_psk.c
@@ -76,7 +76,7 @@
 	struct eap_psk_data *data = priv;
 	os_free(data->id_s);
 	os_free(data->id_p);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -237,7 +237,7 @@
 		return NULL;
 	}
 	os_free(buf);
-	if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
+	if (os_memcmp_const(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
 		wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third "
 			   "message");
 		ret->methodState = METHOD_DONE;
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 267d0a5..1c915ed 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -16,7 +16,8 @@
 
 struct eap_pwd_data {
 	enum {
-		PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+		PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req,
+		SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
 	} state;
 	u8 *id_peer;
 	size_t id_peer_len;
@@ -42,6 +43,7 @@
 
 	u8 msk[EAP_MSK_LEN];
 	u8 emsk[EAP_EMSK_LEN];
+	u8 session_id[1 + SHA256_MAC_LEN];
 
 	BN_CTX *bnctx;
 };
@@ -57,6 +59,8 @@
 		return "PWD-Commit-Req";
         case PWD_Confirm_Req:
 		return "PWD-Confirm-Req";
+	case SUCCESS_ON_FRAG_COMPLETION:
+		return "SUCCESS_ON_FRAG_COMPLETION";
         case SUCCESS:
 		return "SUCCESS";
         case FAILURE:
@@ -81,6 +85,7 @@
 	struct eap_pwd_data *data;
 	const u8 *identity, *password;
 	size_t identity_len, password_len;
+	int fragment_size;
 
 	password = eap_get_config_password(sm, &password_len);
 	if (password == NULL) {
@@ -118,7 +123,7 @@
 	if ((data->password = os_malloc(password_len)) == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
 		BN_CTX_free(data->bnctx);
-		os_free(data->id_peer);
+		bin_clear_free(data->id_peer, data->id_peer_len);
 		os_free(data);
 		return NULL;
 	}
@@ -127,7 +132,11 @@
 
 	data->out_frag_pos = data->in_frag_pos = 0;
 	data->inbuf = data->outbuf = NULL;
-	data->mtu = 1020; /* default from RFC 5931, make it configurable! */
+	fragment_size = eap_get_config_fragment_size(sm);
+	if (fragment_size <= 0)
+		data->mtu = 1020; /* default from RFC 5931 */
+	else
+		data->mtu = fragment_size;
 
 	data->state = PWD_ID_Req;
 
@@ -139,24 +148,26 @@
 {
 	struct eap_pwd_data *data = priv;
 
-	BN_free(data->private_value);
-	BN_free(data->server_scalar);
-	BN_free(data->my_scalar);
-	BN_free(data->k);
+	BN_clear_free(data->private_value);
+	BN_clear_free(data->server_scalar);
+	BN_clear_free(data->my_scalar);
+	BN_clear_free(data->k);
 	BN_CTX_free(data->bnctx);
-	EC_POINT_free(data->my_element);
-	EC_POINT_free(data->server_element);
-	os_free(data->id_peer);
-	os_free(data->id_server);
-	os_free(data->password);
+	EC_POINT_clear_free(data->my_element);
+	EC_POINT_clear_free(data->server_element);
+	bin_clear_free(data->id_peer, data->id_peer_len);
+	bin_clear_free(data->id_server, data->id_server_len);
+	bin_clear_free(data->password, data->password_len);
 	if (data->grp) {
 		EC_GROUP_free(data->grp->group);
-		EC_POINT_free(data->grp->pwe);
-		BN_free(data->grp->order);
-		BN_free(data->grp->prime);
+		EC_POINT_clear_free(data->grp->pwe);
+		BN_clear_free(data->grp->order);
+		BN_clear_free(data->grp->prime);
 		os_free(data->grp);
 	}
-	os_free(data);
+	wpabuf_free(data->inbuf);
+	wpabuf_free(data->outbuf);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -179,6 +190,25 @@
 }
 
 
+static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pwd_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	id = os_malloc(1 + SHA256_MAC_LEN);
+	if (id == NULL)
+		return NULL;
+
+	os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
+	*len = 1 + SHA256_MAC_LEN;
+
+	return id;
+}
+
+
 static void
 eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 			    struct eap_method_ret *ret,
@@ -222,8 +252,8 @@
 	wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
 			  data->id_server, data->id_server_len);
 
-	if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) ==
-	    NULL) {
+	data->grp = os_zalloc(sizeof(EAP_PWD_group));
+	if (data->grp == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
 			   "group");
 		eap_pwd_state(data, FAILURE);
@@ -287,11 +317,15 @@
 		goto fin;
 	}
 
-	BN_rand_range(data->private_value, data->grp->order);
-	BN_rand_range(mask, data->grp->order);
-	BN_add(data->my_scalar, data->private_value, mask);
-	BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
-	       data->bnctx);
+	if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
+	    BN_rand_range(mask, data->grp->order) != 1 ||
+	    BN_add(data->my_scalar, data->private_value, mask) != 1 ||
+	    BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+		   data->bnctx) != 1) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd (peer): unable to get randomness");
+		goto fin;
+	}
 
 	if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
 			  data->grp->pwe, mask, data->bnctx)) {
@@ -306,7 +340,7 @@
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
 		goto fin;
 	}
-	BN_free(mask);
+	BN_clear_free(mask);
 
 	if (((x = BN_new()) == NULL) ||
 	    ((y = BN_new()) == NULL)) {
@@ -441,11 +475,11 @@
 fin:
 	os_free(scalar);
 	os_free(element);
-	BN_free(x);
-	BN_free(y);
-	BN_free(cofactor);
-	EC_POINT_free(K);
-	EC_POINT_free(point);
+	BN_clear_free(x);
+	BN_clear_free(y);
+	BN_clear_free(cofactor);
+	EC_POINT_clear_free(K);
+	EC_POINT_clear_free(point);
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
 	else
@@ -559,7 +593,7 @@
 	eap_pwd_h_final(hash, conf);
 
 	ptr = (u8 *) payload;
-	if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+	if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
 		goto fin;
 	}
@@ -637,7 +671,7 @@
 
 	if (compute_keys(data->grp, data->bnctx, data->k,
 			 data->my_scalar, data->server_scalar, conf, ptr,
-			 &cs, data->msk, data->emsk) < 0) {
+			 &cs, data->msk, data->emsk, data->session_id) < 0) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
 			   "EMSK");
 		goto fin;
@@ -650,16 +684,15 @@
 	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
-	os_free(cruft);
-	BN_free(x);
-	BN_free(y);
-	ret->methodState = METHOD_DONE;
+	bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+	BN_clear_free(x);
+	BN_clear_free(y);
 	if (data->outbuf == NULL) {
+		ret->methodState = METHOD_DONE;
 		ret->decision = DECISION_FAIL;
 		eap_pwd_state(data, FAILURE);
 	} else {
-		ret->decision = DECISION_UNCOND_SUCC;
-		eap_pwd_state(data, SUCCESS);
+		eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
 	}
 }
 
@@ -736,6 +769,11 @@
 		wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
 			   data->out_frag_pos == 0 ? "last" : "next",
 			   (int) len);
+		if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_UNCOND_SUCC;
+			eap_pwd_state(data, SUCCESS);
+		}
 		return resp;
 	}
 
@@ -748,6 +786,8 @@
 		tot_len = WPA_GET_BE16(pos);
 		wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
 			   "total length = %d", tot_len);
+		if (tot_len > 15000)
+			return NULL;
 		data->inbuf = wpabuf_alloc(tot_len);
 		if (data->inbuf == NULL) {
 			wpa_printf(MSG_INFO, "Out of memory to buffer "
@@ -768,6 +808,7 @@
 				   (int) data->in_frag_pos,
 				   (int) wpabuf_len(data->inbuf));
 			wpabuf_free(data->inbuf);
+			data->inbuf = NULL;
 			data->in_frag_pos = 0;
 			return NULL;
 		}
@@ -819,11 +860,15 @@
 	 */
 	if (data->in_frag_pos) {
 		wpabuf_free(data->inbuf);
+		data->inbuf = NULL;
 		data->in_frag_pos = 0;
 	}
 
-	if (data->outbuf == NULL)
+	if (data->outbuf == NULL) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
 		return NULL;        /* generic failure */
+	}
 
 	/*
 	 * we have output! Do we need to fragment it?
@@ -866,6 +911,11 @@
 		wpabuf_free(data->outbuf);
 		data->outbuf = NULL;
 		data->out_frag_pos = 0;
+		if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_UNCOND_SUCC;
+			eap_pwd_state(data, SUCCESS);
+		}
 	}
 
 	return resp;
@@ -913,6 +963,7 @@
 	eap->process = eap_pwd_process;
 	eap->isKeyAvailable = eap_pwd_key_available;
 	eap->getKey = eap_pwd_getkey;
+	eap->getSessionId = eap_pwd_get_session_id;
 	eap->get_emsk = eap_pwd_get_emsk;
 
 	ret = eap_peer_method_register(eap);
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 431519c..7d14907 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -108,7 +108,7 @@
 	struct eap_sake_data *data = priv;
 	os_free(data->serverid);
 	os_free(data->peerid);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -315,7 +315,7 @@
 			     data->peerid, data->peerid_len, 0,
 			     wpabuf_head(reqData), wpabuf_len(reqData),
 			     attr.mic_s, mic_s);
-	if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
+	if (os_memcmp_const(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
 		eap_sake_state(data, FAILURE);
 		ret->methodState = METHOD_DONE;
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 82ea18d..bd06df7 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -43,7 +43,7 @@
 	u8 *last_eap_identity;
 	size_t last_eap_identity_len;
 	enum {
-		CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
 	} state;
 	int result_ind, use_result_ind;
 };
@@ -57,8 +57,6 @@
 		return "CONTINUE";
 	case RESULT_SUCCESS:
 		return "RESULT_SUCCESS";
-	case RESULT_FAILURE:
-		return "RESULT_FAILURE";
 	case SUCCESS:
 		return "SUCCESS";
 	case FAILURE:
@@ -132,6 +130,20 @@
 }
 
 
+static void eap_sim_clear_keys(struct eap_sim_data *data, int reauth)
+{
+	if (!reauth) {
+		os_memset(data->mk, 0, EAP_SIM_MK_LEN);
+		os_memset(data->k_aut, 0, EAP_SIM_K_AUT_LEN);
+		os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN);
+	}
+	os_memset(data->kc, 0, 3 * EAP_SIM_KC_LEN);
+	os_memset(data->sres, 0, 3 * EAP_SIM_SRES_LEN);
+	os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN);
+	os_memset(data->emsk, 0, EAP_EMSK_LEN);
+}
+
+
 static void eap_sim_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_sim_data *data = priv;
@@ -140,11 +152,86 @@
 		os_free(data->pseudonym);
 		os_free(data->reauth_id);
 		os_free(data->last_eap_identity);
+		eap_sim_clear_keys(data, 0);
 		os_free(data);
 	}
 }
 
 
+static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data)
+{
+	char req[200], *pos, *end;
+	size_t i;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing");
+	pos = req;
+	end = pos + sizeof(req);
+	pos += os_snprintf(pos, end - pos, "GSM-AUTH");
+	for (i = 0; i < data->num_chal; i++) {
+		pos += os_snprintf(pos, end - pos, ":");
+		pos += wpa_snprintf_hex(pos, end - pos, data->rand[i],
+					GSM_RAND_LEN);
+	}
+
+	eap_sm_request_sim(sm, req);
+	return 1;
+}
+
+
+static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data,
+				  struct eap_peer_config *conf)
+{
+	char *resp, *pos;
+	size_t i;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-SIM: Use result from external SIM processing");
+
+	resp = conf->external_sim_resp;
+	conf->external_sim_resp = NULL;
+
+	if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response");
+		os_free(resp);
+		return -1;
+	}
+
+	pos = resp + 9;
+	for (i = 0; i < data->num_chal; i++) {
+		wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND",
+			    data->rand[i], GSM_RAND_LEN);
+
+		if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0)
+			goto invalid;
+		wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc",
+				data->kc[i], EAP_SIM_KC_LEN);
+		pos += EAP_SIM_KC_LEN * 2;
+		if (*pos != ':')
+			goto invalid;
+		pos++;
+
+		if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0)
+			goto invalid;
+		wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES",
+				data->sres[i], EAP_SIM_SRES_LEN);
+		pos += EAP_SIM_SRES_LEN * 2;
+		if (i + 1 < data->num_chal) {
+			if (*pos != ':')
+				goto invalid;
+			pos++;
+		}
+	}
+
+	os_free(resp);
+	return 0;
+
+invalid:
+	wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response");
+	os_free(resp);
+	return -1;
+}
+
+
 static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
 {
 	struct eap_peer_config *conf;
@@ -154,6 +241,14 @@
 	conf = eap_get_config(sm);
 	if (conf == NULL)
 		return -1;
+
+	if (sm->external_sim) {
+		if (conf->external_sim_resp)
+			return eap_sim_ext_sim_result(sm, data, conf);
+		else
+			return eap_sim_ext_sim_req(sm, data);
+	}
+
 	if (conf->pcsc) {
 		if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
 				   data->sres[0], data->kc[0]) ||
@@ -369,7 +464,7 @@
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
 			       EAP_SIM_SUBTYPE_CLIENT_ERROR);
 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 }
 
 
@@ -422,7 +517,7 @@
 				identity, identity_len);
 	}
 
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 }
 
 
@@ -440,7 +535,8 @@
 	}
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres,
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+				  (u8 *) data->sres,
 				  data->num_chal * EAP_SIM_SRES_LEN);
 }
 
@@ -482,7 +578,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s,
 				  EAP_SIM_NONCE_S_LEN);
 }
 
@@ -516,7 +612,7 @@
 		wpa_printf(MSG_DEBUG, "   AT_MAC");
 		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 	}
-	return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0);
 }
 
 
@@ -605,6 +701,7 @@
 	const u8 *identity;
 	size_t identity_len;
 	struct eap_sim_attrs eattr;
+	int res;
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
 	data->reauth = 0;
@@ -648,8 +745,13 @@
 
 	os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
 	data->num_chal = attr->num_chal;
-		
-	if (eap_sim_gsm_auth(sm, data)) {
+
+	res = eap_sim_gsm_auth(sm, data);
+	if (res > 0) {
+		wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing");
+		return NULL;
+	}
+	if (res) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed");
 		return eap_sim_client_error(data, id,
 					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
@@ -700,7 +802,7 @@
 	if (data->result_ind && attr->result_ind)
 		data->use_result_ind = 1;
 
-	if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+	if (data->state != FAILURE) {
 		eap_sim_state(data, data->use_result_ind ?
 			      RESULT_SUCCESS : SUCCESS);
 	}
@@ -864,9 +966,11 @@
 	}
 
 	if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+		struct wpabuf *res;
 		wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter "
 			   "(%d <= %d)", eattr.counter, data->counter);
 		data->counter_too_small = eattr.counter;
+
 		/* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
 		 * reauth_id must not be used to start a new reauthentication.
 		 * However, since it was used in the last EAP-Response-Identity
@@ -877,8 +981,11 @@
 		data->last_eap_identity_len = data->reauth_id_len;
 		data->reauth_id = NULL;
 		data->reauth_id_len = 0;
+
+		res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
 		os_free(decrypted);
-		return eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
+
+		return res;
 	}
 	data->counter = eattr.counter;
 
@@ -896,7 +1003,7 @@
 	if (data->result_ind && attr->result_ind)
 		data->use_result_ind = 1;
 
-	if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+	if (data->state != FAILURE) {
 		eap_sim_state(data, data->use_result_ind ?
 			      RESULT_SUCCESS : SUCCESS);
 	}
@@ -995,9 +1102,7 @@
 			DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
 		ret->methodState = data->use_result_ind ?
 			METHOD_DONE : METHOD_MAY_CONT;
-	} else if (data->state == RESULT_FAILURE)
-		ret->methodState = METHOD_CONT;
-	else if (data->state == RESULT_SUCCESS)
+	} else if (data->state == RESULT_SUCCESS)
 		ret->methodState = METHOD_CONT;
 
 	if (ret->methodState == METHOD_DONE) {
@@ -1020,6 +1125,7 @@
 	struct eap_sim_data *data = priv;
 	eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
 	data->use_result_ind = 0;
+	eap_sim_clear_keys(data, 1);
 }
 
 
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index d2066cd..5aa3fd5 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -98,13 +98,49 @@
 #endif /* EAP_UNAUTH_TLS */
 
 
+#ifdef CONFIG_HS20
+static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
+{
+	struct eap_tls_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+		sm->ssl_ctx;
+
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config,
+				  EAP_WFA_UNAUTH_TLS_TYPE)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_deinit(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+
+	return data;
+}
+#endif /* CONFIG_HS20 */
+
+
+static void eap_tls_free_key(struct eap_tls_data *data)
+{
+	if (data->key_data) {
+		bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+		data->key_data = NULL;
+	}
+}
+
+
 static void eap_tls_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
 	if (data == NULL)
 		return;
 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
-	os_free(data->key_data);
+	eap_tls_free_key(data);
 	os_free(data->session_id);
 	os_free(data);
 }
@@ -154,7 +190,7 @@
 	ret->methodState = METHOD_DONE;
 	ret->decision = DECISION_UNCOND_SUCC;
 
-	os_free(data->key_data);
+	eap_tls_free_key(data);
 	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
 						 "client EAP encryption",
 						 EAP_TLS_KEY_LEN +
@@ -240,8 +276,7 @@
 static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
-	os_free(data->key_data);
-	data->key_data = NULL;
+	eap_tls_free_key(data);
 	os_free(data->session_id);
 	data->session_id = NULL;
 	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
@@ -382,3 +417,35 @@
 	return ret;
 }
 #endif /* EAP_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_peer_wfa_unauth_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_WFA_NEW,
+				    EAP_VENDOR_WFA_UNAUTH_TLS,
+				    "WFA-UNAUTH-TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_wfa_unauth_tls_init;
+	eap->deinit = eap_tls_deinit;
+	eap->process = eap_tls_process;
+	eap->isKeyAvailable = eap_tls_isKeyAvailable;
+	eap->getKey = eap_tls_getKey;
+	eap->get_status = eap_tls_get_status;
+	eap->has_reauth_data = eap_tls_has_reauth_data;
+	eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+	eap->init_for_reauth = eap_tls_init_for_reauth;
+	eap->get_emsk = eap_tls_get_emsk;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+	return ret;
+}
+#endif /* CONFIG_HS20 */
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index be8c301..fe9bfe0 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -23,6 +23,10 @@
 		return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
 				     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
 				     code, identifier);
+	if (type == EAP_WFA_UNAUTH_TLS_TYPE)
+		return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
+				     EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
+				     code, identifier);
 	return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
 			     identifier);
 }
@@ -64,6 +68,14 @@
 		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
 	if (os_strstr(txt, "tls_disable_session_ticket=0"))
 		params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
+	if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
+		params->flags |= TLS_CONN_DISABLE_TLSv1_1;
+	if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
+		params->flags &= ~TLS_CONN_DISABLE_TLSv1_1;
+	if (os_strstr(txt, "tls_disable_tlsv1_2=1"))
+		params->flags |= TLS_CONN_DISABLE_TLSv1_2;
+	if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
+		params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
 }
 
 
@@ -78,6 +90,7 @@
 	params->dh_file = (char *) config->dh_file;
 	params->subject_match = (char *) config->subject_match;
 	params->altsubject_match = (char *) config->altsubject_match;
+	params->suffix_match = config->domain_suffix_match;
 	params->engine = config->engine;
 	params->engine_id = config->engine_id;
 	params->pin = config->pin;
@@ -99,6 +112,7 @@
 	params->dh_file = (char *) config->dh_file2;
 	params->subject_match = (char *) config->subject_match2;
 	params->altsubject_match = (char *) config->altsubject_match2;
+	params->suffix_match = config->domain_suffix_match2;
 	params->engine = config->engine2;
 	params->engine_id = config->engine2_id;
 	params->pin = config->pin2;
@@ -836,6 +850,10 @@
 		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
 				       EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
 				       &left);
+	else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+				       EAP_VENDOR_WFA_UNAUTH_TLS, reqData,
+				       &left);
 	else
 		pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
 				       &left);
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 1a5e0f8..390c216 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -87,6 +87,7 @@
 
 /* dummy type used as a flag for UNAUTH-TLS */
 #define EAP_UNAUTH_TLS_TYPE 255
+#define EAP_WFA_UNAUTH_TLS_TYPE 254
 
 
 int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c
index bc13647..25b9f12 100644
--- a/src/eap_peer/eap_tnc.c
+++ b/src/eap_peer/eap_tnc.c
@@ -243,7 +243,8 @@
 		message_length = WPA_GET_BE32(pos);
 		pos += 4;
 
-		if (message_length < (u32) (end - pos)) {
+		if (message_length < (u32) (end - pos) ||
+		    message_length > 75000) {
 			wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
 				   "Length (%d; %ld remaining in this msg)",
 				   message_length, (long) (end - pos));
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 5091bf0..771da58 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -133,6 +133,15 @@
 }
 
 
+static void eap_ttls_free_key(struct eap_ttls_data *data)
+{
+	if (data->key_data) {
+		bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+		data->key_data = NULL;
+	}
+}
+
+
 static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_ttls_data *data = priv;
@@ -141,7 +150,7 @@
 	eap_ttls_phase2_eap_deinit(sm, data);
 	os_free(data->phase2_eap_types);
 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
-	os_free(data->key_data);
+	eap_ttls_free_key(data);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
 	os_free(data);
@@ -213,7 +222,7 @@
 static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 				  struct eap_ttls_data *data)
 {
-	os_free(data->key_data);
+	eap_ttls_free_key(data);
 	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
 						 "ttls keying material",
 						 EAP_TLS_KEY_LEN);
@@ -492,16 +501,6 @@
 	wpabuf_put(msg, pos - buf);
 	*resp = msg;
 
-	if (sm->workaround) {
-		/* At least FreeRADIUS seems to be terminating
-		 * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
-		 * packet. */
-		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
-			   "allow success without tunneled response");
-		ret->methodState = METHOD_MAY_CONT;
-		ret->decision = DECISION_COND_SUCC;
-	}
-
 	return 0;
 #else /* EAP_MSCHAPv2 */
 	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
@@ -1540,8 +1539,7 @@
 static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_ttls_data *data = priv;
-	os_free(data->key_data);
-	data->key_data = NULL;
+	eap_ttls_free_key(data);
 	os_free(data->session_id);
 	data->session_id = NULL;
 	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 8edb1ca..23e9823 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -106,8 +106,10 @@
 	}
 	if (os_strncmp(pos + 9, "NONE", 4) == 0)
 		cred->encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
 	else if (os_strncmp(pos + 9, "WEP", 3) == 0)
 		cred->encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
 	else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
 		cred->encr_type = WPS_ENCR_TKIP;
 	else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
@@ -144,12 +146,13 @@
 	size_t identity_len;
 	int registrar;
 	struct wps_config cfg;
-	const char *pos;
+	const char *pos, *end;
 	const char *phase1;
 	struct wps_context *wps;
 	struct wps_credential new_ap_settings;
 	int res;
 	int nfc = 0;
+	u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
 
 	wps = sm->wps;
 	if (wps == NULL) {
@@ -209,6 +212,15 @@
 			cfg.pbc = 1;
 	}
 
+	pos = os_strstr(phase1, "dev_pw_id=");
+	if (pos) {
+		u16 id = atoi(pos + 10);
+		if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
+			nfc = 1;
+		if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
+			cfg.dev_pw_id = id;
+	}
+
 	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
 			   "configuration data");
@@ -216,9 +228,23 @@
 		return NULL;
 	}
 
-	pos = os_strstr(phase1, "dev_pw_id=");
-	if (pos && cfg.pin)
-		cfg.dev_pw_id = atoi(pos + 10);
+	pos = os_strstr(phase1, " pkhash=");
+	if (pos) {
+		size_t len;
+		pos += 8;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
+		    hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
+			wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
+			os_free(data);
+			return NULL;
+		}
+		cfg.peer_pubkey_hash = pkhash;
+	}
 
 	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
 	if (res < 0) {
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index fcf4712..8186afb 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -72,27 +72,10 @@
 	os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
 	pos += IKEV2_SPI_LEN;
 	os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
-#ifdef CCNS_PL
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-	{
-		int i;
-		u8 *tmp = pos - IKEV2_SPI_LEN;
-		/* Incorrect byte re-ordering on little endian hosts.. */
-		for (i = 0; i < IKEV2_SPI_LEN; i++)
-			*tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i];
-		for (i = 0; i < IKEV2_SPI_LEN; i++)
-			*tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i];
-	}
-#endif
-#endif /* CCNS_PL */
 
 	/* SKEYSEED = prf(Ni | Nr, g^ir) */
 	/* Use zero-padding per RFC 4306, Sect. 2.14 */
 	pad_len = data->dh->prime_len - wpabuf_len(shared);
-#ifdef CCNS_PL
-	/* Shared secret is not zero-padded correctly */
-	pad_len = 0;
-#endif /* CCNS_PL */
 	pad = os_zalloc(pad_len ? pad_len : 1);
 	if (pad == NULL) {
 		wpabuf_free(shared);
@@ -179,21 +162,12 @@
 						   "Transform Attr for AES");
 					break;
 				}
-#ifdef CCNS_PL
-				if (WPA_GET_BE16(pos) != 0x001d /* ?? */) {
-					wpa_printf(MSG_DEBUG, "IKEV2: Not a "
-						   "Key Size attribute for "
-						   "AES");
-					break;
-				}
-#else /* CCNS_PL */
 				if (WPA_GET_BE16(pos) != 0x800e) {
 					wpa_printf(MSG_DEBUG, "IKEV2: Not a "
 						   "Key Size attribute for "
 						   "AES");
 					break;
 				}
-#endif /* CCNS_PL */
 				if (WPA_GET_BE16(pos + 2) != 128) {
 					wpa_printf(MSG_DEBUG, "IKEV2: "
 						   "Unsupported AES key size "
@@ -456,14 +430,6 @@
 		return -1;
 	}
 
-#ifdef CCNS_PL
-	/* Zeros are removed incorrectly from the beginning of the nonces */
-	while (ni_len > 1 && *ni == 0) {
-		ni_len--;
-		ni++;
-	}
-#endif /* CCNS_PL */
-
 	data->i_nonce_len = ni_len;
 	os_memcpy(data->i_nonce, ni, ni_len);
 	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni",
@@ -599,7 +565,7 @@
 		return -1;
 
 	if (auth_len != prf->hash_len ||
-	    os_memcmp(auth, auth_data, auth_len) != 0) {
+	    os_memcmp_const(auth, auth_data, auth_len) != 0) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
 		wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
 			    auth, auth_len);
@@ -887,16 +853,7 @@
 	phdr->flags = 0;
 
 	p = wpabuf_put(msg, sizeof(*p));
-#ifdef CCNS_PL
-	/* Seems to require that the Proposal # is 1 even though RFC 4306
-	 * Sect 3.3.1 has following requirement "When a proposal is accepted,
-	 * all of the proposal numbers in the SA payload MUST be the same and
-	 * MUST match the number on the proposal sent that was accepted.".
-	 */
-	p->proposal_num = 1;
-#else /* CCNS_PL */
 	p->proposal_num = data->proposal.proposal_num;
-#endif /* CCNS_PL */
 	p->protocol_id = IKEV2_PROTOCOL_IKE;
 	p->num_transforms = 4;
 
@@ -906,11 +863,7 @@
 	WPA_PUT_BE16(t->transform_id, data->proposal.encr);
 	if (data->proposal.encr == ENCR_AES_CBC) {
 		/* Transform Attribute: Key Len = 128 bits */
-#ifdef CCNS_PL
-		wpabuf_put_be16(msg, 0x001d); /* ?? */
-#else /* CCNS_PL */
 		wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
-#endif /* CCNS_PL */
 		wpabuf_put_be16(msg, 128); /* 128-bit key */
 	}
 	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
@@ -1082,11 +1035,7 @@
 	phdr = wpabuf_put(msg, sizeof(*phdr));
 	phdr->next_payload = next_payload;
 	phdr->flags = 0;
-#ifdef CCNS_PL
-	wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */
-#else /* CCNS_PL */
 	wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */
-#endif /* CCNS_PL */
 	wpabuf_put_u8(msg, 0); /* SPI Size */
 	wpabuf_put_be16(msg, data->error_type);
 
@@ -1130,13 +1079,6 @@
 	data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
 	if (random_get_bytes(data->r_nonce, data->r_nonce_len))
 		return NULL;
-#ifdef CCNS_PL
-	/* Zeros are removed incorrectly from the beginning of the nonces in
-	 * key derivation; as a workaround, make sure Nr does not start with
-	 * zero.. */
-	if (data->r_nonce[0] == 0)
-		data->r_nonce[0] = 1;
-#endif /* CCNS_PL */
 	wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len);
 
 	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500);
@@ -1257,6 +1199,7 @@
 			wpabuf_free(msg);
 			return NULL;
 		}
+		wpabuf_free(plain);
 		data->state = IKEV2_FAILED;
 	} else {
 		/* HDR, N */
diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c
index 37e6735..9bc7370 100644
--- a/src/eap_peer/mschapv2.c
+++ b/src/eap_peer/mschapv2.c
@@ -117,8 +117,8 @@
 	    buf[0] != 'S' || buf[1] != '=' ||
 	    hexstr2bin((char *) (buf + 2), recv_response,
 		       MSCHAPV2_AUTH_RESPONSE_LEN) ||
-	    os_memcmp(auth_response, recv_response,
-		      MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+	    os_memcmp_const(auth_response, recv_response,
+			    MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
 		return -1;
 	return 0;
 }
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index a3ec395..7ca956e 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -13,6 +13,7 @@
 
 #include "common.h"
 #include "base64.h"
+#include "common/tnc.h"
 #include "tncc.h"
 #include "eap_common/eap_tlv_common.h"
 #include "eap_common/eap_defs.h"
@@ -25,7 +26,9 @@
 #endif /* UNICODE */
 
 
+#ifndef TNC_CONFIG_FILE
 #define TNC_CONFIG_FILE "/etc/tnc_config"
+#endif /* TNC_CONFIG_FILE */
 #define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
 #define IF_TNCCS_START \
 "<?xml version=\"1.0\"?>\n" \
@@ -38,56 +41,6 @@
 
 /* TNC IF-IMC */
 
-typedef unsigned long TNC_UInt32;
-typedef unsigned char *TNC_BufferReference;
-
-typedef TNC_UInt32 TNC_IMCID;
-typedef TNC_UInt32 TNC_ConnectionID;
-typedef TNC_UInt32 TNC_ConnectionState;
-typedef TNC_UInt32 TNC_RetryReason;
-typedef TNC_UInt32 TNC_MessageType;
-typedef TNC_MessageType *TNC_MessageTypeList;
-typedef TNC_UInt32 TNC_VendorID;
-typedef TNC_UInt32 TNC_MessageSubtype;
-typedef TNC_UInt32 TNC_Version;
-typedef TNC_UInt32 TNC_Result;
-
-typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
-	TNC_IMCID imcID,
-	char *functionName,
-	void **pOutfunctionPointer);
-
-#define TNC_RESULT_SUCCESS 0
-#define TNC_RESULT_NOT_INITIALIZED 1
-#define TNC_RESULT_ALREADY_INITIALIZED 2
-#define TNC_RESULT_NO_COMMON_VERSION 3
-#define TNC_RESULT_CANT_RETRY 4
-#define TNC_RESULT_WONT_RETRY 5
-#define TNC_RESULT_INVALID_PARAMETER 6
-#define TNC_RESULT_CANT_RESPOND 7
-#define TNC_RESULT_ILLEGAL_OPERATION 8
-#define TNC_RESULT_OTHER 9
-#define TNC_RESULT_FATAL 10
-
-#define TNC_CONNECTION_STATE_CREATE 0
-#define TNC_CONNECTION_STATE_HANDSHAKE 1
-#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
-#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
-#define TNC_CONNECTION_STATE_ACCESS_NONE 4
-#define TNC_CONNECTION_STATE_DELETE 5
-
-#define TNC_IFIMC_VERSION_1 1
-
-#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
-#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
-
-/* TNCC-TNCS Message Types */
-#define TNC_TNCCS_RECOMMENDATION		0x00000001
-#define TNC_TNCCS_ERROR				0x00000002
-#define TNC_TNCCS_PREFERREDLANGUAGE		0x00000003
-#define TNC_TNCCS_REASONSTRINGS			0x00000004
-
-
 /* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
 enum {
 	SSOH_MS_MACHINE_INVENTORY = 1,
@@ -1139,8 +1092,10 @@
 			int error = 0;
 
 			imc = tncc_parse_imc(pos + 4, line_end, &error);
-			if (error)
+			if (error) {
+				os_free(config);
 				return -1;
+			}
 			if (imc) {
 				if (last == NULL)
 					tncc->imc = imc;
diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile
index 9c41962..adfd3df 100644
--- a/src/eap_server/Makefile
+++ b/src/eap_server/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 36b230b..1253bd6 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -32,8 +32,11 @@
 			    * nt_password_hash() */
 	int phase2;
 	int force_version;
+	unsigned int remediation:1;
+	unsigned int macacl:1;
 	int ttls_auth; /* bitfield of
 			* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
+	struct hostapd_radius_attr *accept_attr;
 };
 
 struct eap_eapol_interface {
@@ -79,6 +82,7 @@
 	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
 			    int phase2, struct eap_user *user);
 	const char * (*get_eap_req_id_text)(void *ctx, size_t *len);
+	void (*log_msg)(void *ctx, const char *msg);
 };
 
 struct eap_config {
@@ -107,6 +111,10 @@
 
 	const u8 *server_id;
 	size_t server_id_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u32 tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 003e202..3a6802b 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -191,10 +191,16 @@
 
 	const u8 *server_id;
 	size_t server_id_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u32 tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
 		 int phase2);
+void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
 void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len);
 
 #endif /* EAP_I_H */
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
index 429cb72..0baa327 100644
--- a/src/eap_server/eap_methods.h
+++ b/src/eap_server/eap_methods.h
@@ -27,6 +27,7 @@
 int eap_server_md5_register(void);
 int eap_server_tls_register(void);
 int eap_server_unauth_tls_register(void);
+int eap_server_wfa_unauth_tls_register(void);
 int eap_server_mschapv2_register(void);
 int eap_server_peap_register(void);
 int eap_server_tlv_register(void);
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 54b7533..c1bb6b8 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -119,6 +119,32 @@
 }
 
 
+void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+
+	if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL)
+		return;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	va_start(ap, fmt);
+	vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+
+	sm->eapol_cb->log_msg(sm->eapol_ctx, buf);
+
+	os_free(buf);
+}
+
+
 SM_STATE(EAP, DISABLED)
 {
 	SM_ENTRY(EAP, DISABLED);
@@ -142,7 +168,7 @@
 	sm->eap_if.eapSuccess = FALSE;
 	sm->eap_if.eapFail = FALSE;
 	sm->eap_if.eapTimeout = FALSE;
-	os_free(sm->eap_if.eapKeyData);
+	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
 	sm->eap_if.eapKeyData = NULL;
 	sm->eap_if.eapKeyDataLen = 0;
 	sm->eap_if.eapKeyAvailable = FALSE;
@@ -320,7 +346,7 @@
 	sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
 	if (sm->m->isDone(sm, sm->eap_method_priv)) {
 		eap_sm_Policy_update(sm, NULL, 0);
-		os_free(sm->eap_if.eapKeyData);
+		bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
 		if (sm->m->getKey) {
 			sm->eap_if.eapKeyData = sm->m->getKey(
 				sm, sm->eap_method_priv,
@@ -343,6 +369,7 @@
 
 	SM_ENTRY(EAP, PROPOSE_METHOD);
 
+try_another_method:
 	type = eap_sm_Policy_getNextMethod(sm, &vendor);
 	if (vendor == EAP_VENDOR_IETF)
 		sm->currentMethod = type;
@@ -360,8 +387,15 @@
 				   "method %d", sm->currentMethod);
 			sm->m = NULL;
 			sm->currentMethod = EAP_TYPE_NONE;
+			goto try_another_method;
 		}
 	}
+	if (sm->m == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method");
+		eap_log_msg(sm, "Could not find suitable EAP method");
+		sm->decision = DECISION_FAILURE;
+		return;
+	}
 	if (sm->currentMethod == EAP_TYPE_IDENTITY ||
 	    sm->currentMethod == EAP_TYPE_NOTIFICATION)
 		sm->methodState = METHOD_CONTINUE;
@@ -370,6 +404,8 @@
 
 	wpa_msg(sm->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);
 }
 
 
@@ -596,7 +632,7 @@
 	if (sm->eap_if.aaaEapKeyAvailable) {
 		EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
 	} else {
-		os_free(sm->eap_if.eapKeyData);
+		bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
 		sm->eap_if.eapKeyData = NULL;
 		sm->eap_if.eapKeyDataLen = 0;
 	}
@@ -686,6 +722,7 @@
 				   "respMethod=%d currentMethod=%d",
 				   sm->rxResp, sm->respId, sm->currentId,
 				   sm->respMethod, sm->currentMethod);
+			eap_log_msg(sm, "Discard received EAP message");
 			SM_ENTER(EAP, DISCARD);
 		}
 		break;
@@ -702,6 +739,15 @@
 			SM_ENTER(EAP, METHOD_RESPONSE);
 		break;
 	case EAP_METHOD_REQUEST:
+		if (sm->m == NULL) {
+			/*
+			 * This transition is not mentioned in RFC 4137, but it
+			 * is needed to handle cleanly a case where EAP method
+			 * initialization fails.
+			 */
+			SM_ENTER(EAP, FAILURE);
+			break;
+		}
 		SM_ENTER(EAP, SEND_REQUEST);
 		break;
 	case EAP_METHOD_RESPONSE:
@@ -1214,7 +1260,7 @@
 {
 	if (user == NULL)
 		return;
-	os_free(user->password);
+	bin_clear_free(user->password, user->password_len);
 	user->password = NULL;
 	os_free(user);
 }
@@ -1281,6 +1327,10 @@
 	sm->server_id = conf->server_id;
 	sm->server_id_len = conf->server_id_len;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	sm->tls_test_flags = conf->tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
 
 	return sm;
@@ -1302,7 +1352,7 @@
 	if (sm->m && sm->eap_method_priv)
 		sm->m->reset(sm, sm->eap_method_priv);
 	wpabuf_free(sm->eap_if.eapReqData);
-	os_free(sm->eap_if.eapKeyData);
+	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
 	wpabuf_free(sm->lastReqData);
 	wpabuf_free(sm->eap_if.eapRespData);
 	os_free(sm->identity);
@@ -1311,7 +1361,7 @@
 	os_free(sm->eap_fast_a_id_info);
 	wpabuf_free(sm->eap_if.aaaEapReqData);
 	wpabuf_free(sm->eap_if.aaaEapRespData);
-	os_free(sm->eap_if.aaaEapKeyData);
+	bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen);
 	eap_user_free(sm->user);
 	wpabuf_free(sm->assoc_wps_ie);
 	wpabuf_free(sm->assoc_p2p_ie);
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 469b9a0..09b976e 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -241,7 +241,7 @@
 	os_free(data->next_reauth_id);
 	wpabuf_free(data->id_msgs);
 	os_free(data->network_name);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -336,7 +336,7 @@
 	else
 		sha1_vector(1, &addr, &len, hash);
 
-	if (os_memcmp(hash, checkcode, hash_len) != 0) {
+	if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
 		return -1;
 	}
@@ -377,7 +377,7 @@
 		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
 		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
 	}
-	buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
+	buf = eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 	if (eap_aka_add_id_msg(data, buf) < 0) {
 		wpabuf_free(buf);
 		return NULL;
@@ -534,7 +534,7 @@
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
 }
 
 
@@ -581,7 +581,7 @@
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
 }
 
 
@@ -620,7 +620,7 @@
 		wpa_printf(MSG_DEBUG, "   AT_MAC");
 		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 	}
-	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
 }
 
 
@@ -963,7 +963,7 @@
 	 */
 	if (attr->res == NULL || attr->res_len < data->res_len ||
 	    attr->res_len_bits != data->res_len * 8 ||
-	    os_memcmp(attr->res, data->res, data->res_len) != 0) {
+	    os_memcmp_const(attr->res, data->res, data->res_len) != 0) {
 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not "
 			   "include valid AT_RES (attr len=%lu, res len=%lu "
 			   "bits, expected %lu bits)",
@@ -1040,6 +1040,7 @@
 	data->auts_reported = 1;
 
 	/* Remain in CHALLENGE state to re-try after resynchronization */
+	eap_aka_fullauth(sm, data);
 }
 
 
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index b19a321..966f511 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -104,7 +104,7 @@
 	eap_eke_session_clean(&data->sess);
 	os_free(data->peerid);
 	wpabuf_free(data->msgs);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -635,8 +635,8 @@
 		return;
 	}
 	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len);
-	if (os_memcmp(auth_p, payload + data->sess.pnonce_len,
-		      data->sess.prf_len) != 0) {
+	if (os_memcmp_const(auth_p, payload + data->sess.pnonce_len,
+			    data->sess.prf_len) != 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match");
 		eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
 		return;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index fcb80dc..4691e72 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -187,7 +187,7 @@
 		switch (*pos) {
 		case PAC_OPAQUE_TYPE_PAD:
 			pos = end;
-			break;
+			goto done;
 		case PAC_OPAQUE_TYPE_KEY:
 			if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
 				wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
@@ -218,6 +218,7 @@
 
 		pos += 2 + pos[1];
 	}
+done:
 
 	if (pac_key == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in "
@@ -511,7 +512,7 @@
 	os_free(data->key_block_p);
 	wpabuf_free(data->pending_phase2_resp);
 	os_free(data->identity);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -1123,7 +1124,8 @@
 static int eap_fast_parse_tlvs(struct wpabuf *data,
 			       struct eap_fast_tlv_parse *tlv)
 {
-	int mandatory, tlv_type, len, res;
+	int mandatory, tlv_type, res;
+	size_t len;
 	u8 *pos, *end;
 
 	os_memset(tlv, 0, sizeof(*tlv));
@@ -1136,13 +1138,14 @@
 		pos += 2;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end) {
+		if (len > (size_t) (end - pos)) {
 			wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
 			return -1;
 		}
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
-			   "TLV type %d length %d%s",
-			   tlv_type, len, mandatory ? " (mandatory)" : "");
+			   "TLV type %d length %u%s",
+			   tlv_type, (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
 
 		res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
 		if (res == -2)
@@ -1196,7 +1199,7 @@
 		return -1;
 	}
 
-	if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 ||
+	if (os_memcmp_const(data->crypto_binding_nonce, b->nonce, 31) != 0 ||
 	    (data->crypto_binding_nonce[31] | 1) != b->nonce[31]) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid nonce in "
 			   "Crypto-Binding");
@@ -1210,7 +1213,7 @@
 		    (u8 *) b, bind_len);
 	hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len,
 		  b->compound_mac);
-	if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) {
+	if (os_memcmp_const(cmac, b->compound_mac, sizeof(cmac)) != 0) {
 		wpa_hexdump(MSG_MSGDUMP,
 			    "EAP-FAST: Calculated Compound MAC",
 			    b->compound_mac, sizeof(cmac));
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index 66f4271..cb369e4 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -95,7 +95,7 @@
 {
 	struct eap_gpsk_data *data = priv;
 	os_free(data->id_peer);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -433,7 +433,7 @@
 		eap_gpsk_state(data, FAILURE);
 		return;
 	}
-	if (os_memcmp(mic, pos, miclen) != 0) {
+	if (os_memcmp_const(mic, pos, miclen) != 0) {
 		wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2");
 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
@@ -502,7 +502,7 @@
 		eap_gpsk_state(data, FAILURE);
 		return;
 	}
-	if (os_memcmp(mic, pos, miclen) != 0) {
+	if (os_memcmp_const(mic, pos, miclen) != 0) {
 		wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4");
 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
index f423106..98ac3c6 100644
--- a/src/eap_server/eap_server_gtc.c
+++ b/src/eap_server/eap_server_gtc.c
@@ -175,7 +175,7 @@
 	}
 
 	if (rlen != sm->user->password_len ||
-	    os_memcmp(pos, sm->user->password, rlen) != 0) {
+	    os_memcmp_const(pos, sm->user->password, rlen) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
 		data->state = FAILURE;
 	} else {
diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c
index 51dc4e8..4501533 100644
--- a/src/eap_server/eap_server_identity.c
+++ b/src/eap_server/eap_server_identity.c
@@ -102,6 +102,7 @@
 	struct eap_identity_data *data = priv;
 	const u8 *pos;
 	size_t len;
+	char *buf;
 
 	if (data->pick_up) {
 		if (eap_identity_check(sm, data, respData)) {
@@ -119,6 +120,12 @@
 		return; /* Should not happen - frame already validated */
 
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len);
+	buf = os_malloc(len * 4 + 1);
+	if (buf) {
+		printf_encode(buf, len * 4 + 1, pos, len);
+		eap_log_msg(sm, "EAP-Response/Identity '%s'", buf);
+		os_free(buf);
+	}
 	if (sm->identity)
 		sm->update_user = TRUE;
 	os_free(sm->identity);
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 1ada0c8..65b2ef6 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -127,7 +127,7 @@
 	wpabuf_free(data->in_buf);
 	wpabuf_free(data->out_buf);
 	ikev2_initiator_deinit(&data->ikev2);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -256,7 +256,8 @@
 
 static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
 				 const struct wpabuf *respData,
-				 u8 flags, const u8 *pos, const u8 **end)
+				 u8 flags, const u8 *pos, const u8 **end,
+				 int frag_ack)
 {
 	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
 		int icv_len = eap_ikev2_validate_icv(
@@ -266,7 +267,7 @@
 			return -1;
 		/* Hide Integrity Checksum Data from further processing */
 		*end -= icv_len;
-	} else if (data->keys_ready) {
+	} else if (data->keys_ready && !frag_ack) {
 		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
 			   "included integrity checksum");
 		return -1;
@@ -365,7 +366,9 @@
 	} else
 		flags = *pos++;
 
-	if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) {
+	if (eap_ikev2_process_icv(data, respData, flags, pos, &end,
+				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
+	{
 		eap_ikev2_state(data, FAIL);
 		return;
 	}
diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c
index 5a5e290..71e8d59 100644
--- a/src/eap_server/eap_server_md5.c
+++ b/src/eap_server/eap_server_md5.c
@@ -126,7 +126,7 @@
 		return;
 	}
 
-	if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
+	if (os_memcmp_const(hash, pos, CHAP_MD5_LEN) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
 		data->state = SUCCESS;
 	} else {
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 3153d2e..f7a753d 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -91,7 +91,7 @@
 		return;
 
 	os_free(data->peer_challenge);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -290,6 +290,7 @@
 	const u8 *username, *user;
 	size_t username_len, user_len;
 	int res;
+	char *buf;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
 			       &len);
@@ -329,6 +330,13 @@
 	wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
 
+	buf = os_malloc(name_len * 4 + 1);
+	if (buf) {
+		printf_encode(buf, name_len * 4 + 1, name, name_len);
+		eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
+		os_free(buf);
+	}
+
 	/* MSCHAPv2 does not include optional domain name in the
 	 * challenge-response calculation, so remove domain prefix
 	 * (if present). */
@@ -385,7 +393,7 @@
 		return;
 	}
 
-	if (os_memcmp(nt_response, expected, 24) == 0) {
+	if (os_memcmp_const(nt_response, expected, 24) == 0) {
 		const u8 *pw_hash;
 		u8 pw_hash_buf[16], pw_hash_hash[16];
 
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 35a42ad..c87848c 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -64,7 +64,7 @@
 {
 	struct eap_pax_data *data = priv;
 	os_free(data->cid);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -268,7 +268,7 @@
 			    wpabuf_mhead(respData),
 			    wpabuf_len(respData) - EAP_PAX_ICV_LEN,
 			    NULL, 0, NULL, 0, icvbuf);
-		if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
+		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",
 				    icvbuf, EAP_PAX_ICV_LEN);
@@ -395,7 +395,7 @@
 		    data->rand.r.x, EAP_PAX_RAND_LEN,
 		    data->rand.r.y, EAP_PAX_RAND_LEN,
 		    (u8 *) data->cid, data->cid_len, mac);
-	if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
+	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");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
@@ -417,7 +417,7 @@
 		    wpabuf_head(respData),
 		    wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
 		    icvbuf);
-	if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
+	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",
 			    icvbuf, EAP_PAX_ICV_LEN);
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 68253c4..594e02d 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -22,7 +22,6 @@
 /* Maximum supported PEAP version
  * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
  * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
- * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
  */
 #define EAP_PEAP_VERSION 1
 
@@ -99,33 +98,6 @@
 }
 
 
-static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
-{
-	struct wpabuf *e;
-	struct eap_tlv_hdr *tlv;
-
-	if (buf == NULL)
-		return NULL;
-
-	/* Encapsulate EAP packet in EAP-Payload TLV */
-	wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
-	e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
-	if (e == NULL) {
-		wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
-			   "for TLV encapsulation");
-		wpabuf_free(buf);
-		return NULL;
-	}
-	tlv = wpabuf_put(e, sizeof(*tlv));
-	tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
-				     EAP_TLV_EAP_PAYLOAD_TLV);
-	tlv->length = host_to_be16(wpabuf_len(buf));
-	wpabuf_put_buf(e, buf);
-	wpabuf_free(buf);
-	return e;
-}
-
-
 static void eap_peap_req_success(struct eap_sm *sm,
 				 struct eap_peap_data *data)
 {
@@ -200,7 +172,7 @@
 	wpabuf_free(data->pending_phase2_resp);
 	os_free(data->phase2_key);
 	wpabuf_free(data->soh_response);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -239,8 +211,6 @@
 		return NULL;
 	}
 	buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
-	if (data->peap_version >= 2 && buf)
-		buf = eap_peapv2_tlv_eap_payload(buf);
 	if (buf == NULL)
 		return NULL;
 
@@ -425,8 +395,6 @@
 		len[1] = 1;
 
 		tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
-		if (data->peap_version >= 2)
-			tlv_type |= EAP_TLV_TYPE_MANDATORY;
 		wpabuf_put_be16(buf, tlv_type);
 		wpabuf_put_be16(buf, 56);
 
@@ -505,8 +473,7 @@
 		return eap_peap_build_start(sm, data, id);
 	case PHASE1:
 	case PHASE1_ID2:
-		if (data->peap_version < 2 &&
-		    tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, "
 				   "starting Phase2");
 			eap_peap_state(data, PHASE2_START);
@@ -626,7 +593,7 @@
 	buf[60] = EAP_TYPE_PEAP;
 	hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
 
-	if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+	if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
 			   "cryptobinding TLV");
 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20);
@@ -1079,47 +1046,6 @@
 		wpabuf_free(in_decrypted);
 
 		in_decrypted = nbuf;
-	} else if (data->peap_version >= 2) {
-		struct eap_tlv_hdr *tlv;
-		struct wpabuf *nmsg;
-
-		if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
-				   "EAP TLV");
-			wpabuf_free(in_decrypted);
-			return;
-		}
-		tlv = wpabuf_mhead(in_decrypted);
-		if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) !=
-		    EAP_TLV_EAP_PAYLOAD_TLV) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
-			wpabuf_free(in_decrypted);
-			return;
-		}
-		if (sizeof(*tlv) + be_to_host16(tlv->length) >
-		    wpabuf_len(in_decrypted)) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
-				   "length");
-			wpabuf_free(in_decrypted);
-			return;
-		}
-		hdr = (struct eap_hdr *) (tlv + 1);
-		if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
-			wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
-				   "EAP packet in EAP TLV");
-			wpabuf_free(in_decrypted);
-			return;
-		}
-
-		nmsg = wpabuf_alloc(be_to_host16(hdr->length));
-		if (nmsg == NULL) {
-			wpabuf_free(in_decrypted);
-			return;
-		}
-
-		wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
-		wpabuf_free(in_decrypted);
-		in_decrypted = nmsg;
 	}
 
 	hdr = wpabuf_head(in_decrypted);
@@ -1168,53 +1094,6 @@
 }
 
 
-static int eap_peapv2_start_phase2(struct eap_sm *sm,
-				   struct eap_peap_data *data)
-{
-	struct wpabuf *buf, *buf2;
-
-	wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 "
-		   "payload in the same message");
-	eap_peap_state(data, PHASE1_ID2);
-	if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY))
-		return -1;
-
-	/* TODO: which Id to use here? */
-	buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6);
-	if (buf == NULL)
-		return -1;
-
-	buf2 = eap_peapv2_tlv_eap_payload(buf);
-	if (buf2 == NULL)
-		return -1;
-
-	wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2);
-
-	buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
-				     buf2);
-	wpabuf_free(buf2);
-
-	if (buf == NULL) {
-		wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 "
-			   "data");
-		return -1;
-	}
-
-	wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request",
-			buf);
-
-	/* Append TLS data into the pending buffer after the Server Finished */
-	if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) {
-		wpabuf_free(buf);
-		return -1;
-	}
-	wpabuf_put_buf(data->ssl.tls_out, buf);
-	wpabuf_free(buf);
-
-	return 0;
-}
-
-
 static int eap_peap_process_version(struct eap_sm *sm, void *priv,
 				    int peer_version)
 {
@@ -1249,14 +1128,6 @@
 			eap_peap_state(data, FAILURE);
 			break;
 		}
-
-		if (data->peap_version >= 2 &&
-		    tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
-			if (eap_peapv2_start_phase2(sm, data)) {
-				eap_peap_state(data, FAILURE);
-				break;
-			}
-		}
 		break;
 	case PHASE2_START:
 		eap_peap_state(data, PHASE2_ID);
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index 46bedd9..db394e9 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -47,7 +47,7 @@
 {
 	struct eap_psk_data *data = priv;
 	os_free(data->id_p);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -314,7 +314,7 @@
 	}
 	os_free(buf);
 	wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN);
-	if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) {
+	if (os_memcmp_const(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P",
 			    mac, EAP_PSK_MAC_LEN);
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index b61061b..7e1278d 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -45,6 +45,7 @@
 
 	u8 msk[EAP_MSK_LEN];
 	u8 emsk[EAP_EMSK_LEN];
+	u8 session_id[1 + SHA256_MAC_LEN];
 
 	BN_CTX *bnctx;
 };
@@ -105,7 +106,7 @@
 	if (data->password == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
 			   "fail");
-		os_free(data->id_server);
+		bin_clear_free(data->id_server, data->id_server_len);
 		os_free(data);
 		return NULL;
 	}
@@ -115,15 +116,16 @@
 	data->bnctx = BN_CTX_new();
 	if (data->bnctx == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
-		os_free(data->password);
-		os_free(data->id_server);
+		bin_clear_free(data->password, data->password_len);
+		bin_clear_free(data->id_server, data->id_server_len);
 		os_free(data);
 		return NULL;
 	}
 
 	data->in_frag_pos = data->out_frag_pos = 0;
 	data->inbuf = data->outbuf = NULL;
-	data->mtu = 1020; /* default from RFC 5931, make it configurable! */
+	/* use default MTU from RFC 5931 if not configured otherwise */
+	data->mtu = sm->fragment_size > 0 ? sm->fragment_size : 1020;
 
 	return data;
 }
@@ -133,24 +135,26 @@
 {
 	struct eap_pwd_data *data = priv;
 
-	BN_free(data->private_value);
-	BN_free(data->peer_scalar);
-	BN_free(data->my_scalar);
-	BN_free(data->k);
+	BN_clear_free(data->private_value);
+	BN_clear_free(data->peer_scalar);
+	BN_clear_free(data->my_scalar);
+	BN_clear_free(data->k);
 	BN_CTX_free(data->bnctx);
-	EC_POINT_free(data->my_element);
-	EC_POINT_free(data->peer_element);
-	os_free(data->id_peer);
-	os_free(data->id_server);
-	os_free(data->password);
+	EC_POINT_clear_free(data->my_element);
+	EC_POINT_clear_free(data->peer_element);
+	bin_clear_free(data->id_peer, data->id_peer_len);
+	bin_clear_free(data->id_server, data->id_server_len);
+	bin_clear_free(data->password, data->password_len);
 	if (data->grp) {
 		EC_GROUP_free(data->grp->group);
-		EC_POINT_free(data->grp->pwe);
-		BN_free(data->grp->order);
-		BN_free(data->grp->prime);
+		EC_POINT_clear_free(data->grp->pwe);
+		BN_clear_free(data->grp->order);
+		BN_clear_free(data->grp->prime);
 		os_free(data->grp);
 	}
-	os_free(data);
+	wpabuf_free(data->inbuf);
+	wpabuf_free(data->outbuf);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -206,11 +210,15 @@
 		goto fin;
 	}
 
-	BN_rand_range(data->private_value, data->grp->order);
-	BN_rand_range(mask, data->grp->order);
-	BN_add(data->my_scalar, data->private_value, mask);
-	BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
-	       data->bnctx);
+	if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
+	    BN_rand_range(mask, data->grp->order) != 1 ||
+	    BN_add(data->my_scalar, data->private_value, mask) != 1 ||
+	    BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+		   data->bnctx) != 1) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd (server): unable to get randomness");
+		goto fin;
+	}
 
 	if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
 			  data->grp->pwe, mask, data->bnctx)) {
@@ -226,7 +234,7 @@
 			   "fail");
 		goto fin;
 	}
-	BN_free(mask);
+	BN_clear_free(mask);
 
 	if (((x = BN_new()) == NULL) ||
 	    ((y = BN_new()) == NULL)) {
@@ -278,8 +286,8 @@
 fin:
 	os_free(scalar);
 	os_free(element);
-	BN_free(x);
-	BN_free(y);
+	BN_clear_free(x);
+	BN_clear_free(y);
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
 }
@@ -402,9 +410,9 @@
 	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
-	os_free(cruft);
-	BN_free(x);
-	BN_free(y);
+	bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+	BN_clear_free(x);
+	BN_clear_free(y);
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
 }
@@ -523,6 +531,7 @@
 	 */
 	if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
 		wpabuf_free(data->outbuf);
+		data->outbuf = NULL;
 		data->out_frag_pos = 0;
 	}
 
@@ -595,7 +604,8 @@
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
 			  data->id_peer, data->id_peer_len);
 
-	if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
+	data->grp = os_zalloc(sizeof(EAP_PWD_group));
+	if (data->grp == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
 			   "group");
 		return;
@@ -718,11 +728,11 @@
 	res = 1;
 
 fin:
-	EC_POINT_free(K);
-	EC_POINT_free(point);
-	BN_free(cofactor);
-	BN_free(x);
-	BN_free(y);
+	EC_POINT_clear_free(K);
+	EC_POINT_clear_free(point);
+	BN_clear_free(cofactor);
+	BN_clear_free(x);
+	BN_clear_free(y);
 
 	if (res)
 		eap_pwd_state(data, PWD_Confirm_Req);
@@ -829,7 +839,7 @@
 	eap_pwd_h_final(hash, conf);
 
 	ptr = (u8 *) payload;
-	if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+	if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
 			   "verify");
 		goto fin;
@@ -838,15 +848,16 @@
 	wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
 	if (compute_keys(data->grp, data->bnctx, data->k,
 			 data->peer_scalar, data->my_scalar, conf,
-			 data->my_confirm, &cs, data->msk, data->emsk) < 0)
+			 data->my_confirm, &cs, data->msk, data->emsk,
+			 data->session_id) < 0)
 		eap_pwd_state(data, FAILURE);
 	else
 		eap_pwd_state(data, SUCCESS);
 
 fin:
-	os_free(cruft);
-	BN_free(x);
-	BN_free(y);
+	bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+	BN_clear_free(x);
+	BN_clear_free(y);
 }
 
 
@@ -893,6 +904,8 @@
 		tot_len = WPA_GET_BE16(pos);
 		wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total "
 			   "length = %d", tot_len);
+		if (tot_len > 15000)
+			return;
 		data->inbuf = wpabuf_alloc(tot_len);
 		if (data->inbuf == NULL) {
 			wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to "
@@ -949,6 +962,7 @@
 	 */
 	if (data->in_frag_pos) {
 		wpabuf_free(data->inbuf);
+		data->inbuf = NULL;
 		data->in_frag_pos = 0;
 	}
 }
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 68dd76b..1937621 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -83,7 +83,7 @@
 {
 	struct eap_sake_data *data = priv;
 	os_free(data->peerid);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -351,7 +351,7 @@
 			     data->peerid, data->peerid_len, 1,
 			     wpabuf_head(respData), wpabuf_len(respData),
 			     attr.mic_p, mic_p);
-	if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+	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);
 		return;
@@ -388,7 +388,7 @@
 			     data->peerid, data->peerid_len, 1,
 			     wpabuf_head(respData), wpabuf_len(respData),
 			     attr.mic_p, mic_p);
-	if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+	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);
 	} else
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index b531241..23ee2b6 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -94,7 +94,7 @@
 	struct eap_sim_data *data = priv;
 	os_free(data->next_pseudonym);
 	os_free(data->next_reauth_id);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -140,7 +140,7 @@
 	ver[1] = EAP_SIM_VERSION;
 	eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
 			ver, sizeof(ver));
-	return eap_sim_msg_finish(msg, NULL, NULL, 0);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 }
 
 
@@ -240,8 +240,8 @@
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt,
-				  EAP_SIM_NONCE_MT_LEN);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+				  data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
 }
 
 
@@ -278,7 +278,7 @@
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
 }
 
 
@@ -317,7 +317,7 @@
 		wpa_printf(MSG_DEBUG, "   AT_MAC");
 		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 	}
-	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
 }
 
 
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 447f47c..6bed62f 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -94,6 +94,28 @@
 #endif /* EAP_SERVER_UNAUTH_TLS */
 
 
+#ifdef CONFIG_HS20
+static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
+{
+	struct eap_tls_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+		eap_tls_reset(sm, data);
+		return NULL;
+	}
+
+	data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+	return data;
+}
+#endif /* CONFIG_HS20 */
+
+
 static void eap_tls_reset(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
@@ -178,6 +200,10 @@
 		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
 				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
 				       &len);
+	else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+				       EAP_VENDOR_WFA_UNAUTH_TLS, respData,
+				       &len);
 	else
 		pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
 				       respData, &len);
@@ -340,3 +366,34 @@
 	return ret;
 }
 #endif /* EAP_SERVER_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_server_wfa_unauth_tls_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_WFA_NEW,
+				      EAP_VENDOR_WFA_UNAUTH_TLS,
+				      "WFA-UNAUTH-TLS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_wfa_unauth_tls_init;
+	eap->reset = eap_tls_reset;
+	eap->buildReq = eap_tls_buildReq;
+	eap->check = eap_tls_check;
+	eap->process = eap_tls_process;
+	eap->isDone = eap_tls_isDone;
+	eap->getKey = eap_tls_getKey;
+	eap->isSuccess = eap_tls_isSuccess;
+	eap->get_emsk = eap_tls_get_emsk;
+
+	ret = eap_server_method_register(eap);
+	if (ret)
+		eap_server_method_free(eap);
+	return ret;
+}
+#endif /* CONFIG_HS20 */
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 9efb5b2..01853e6 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -25,14 +25,32 @@
 		return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
 				     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
 				     code, identifier);
+	else if (type == EAP_WFA_UNAUTH_TLS_TYPE)
+		return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
+				     EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
+				     code, identifier);
 	return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
 			     identifier);
 }
 
 
+#ifdef CONFIG_TLS_INTERNAL
+static void eap_server_tls_log_cb(void *ctx, const char *msg)
+{
+	struct eap_sm *sm = ctx;
+	eap_log_msg(sm, "TLS: %s", msg);
+}
+#endif /* CONFIG_TLS_INTERNAL */
+
+
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
 			    int verify_peer)
 {
+	if (sm->ssl_ctx == NULL) {
+		wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
+		return -1;
+	}
+
 	data->eap = sm;
 	data->phase2 = sm->init_phase2;
 
@@ -43,6 +61,13 @@
 		return -1;
 	}
 
+#ifdef CONFIG_TLS_INTERNAL
+	tls_connection_set_log_cb(data->conn, eap_server_tls_log_cb, sm);
+#ifdef CONFIG_TESTING_OPTIONS
+	tls_connection_set_test_flags(data->conn, sm->tls_test_flags);
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_TLS_INTERNAL */
+
 	if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
 		wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
 			   "of TLS peer certificate");
@@ -388,6 +413,10 @@
 		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
 				       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
 				       &left);
+	else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+				       EAP_VENDOR_WFA_UNAUTH_TLS, respData,
+				       &left);
 	else
 		pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData,
 				       &left);
diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c
index 67a3dfa..21bd26f 100644
--- a/src/eap_server/eap_server_tnc.c
+++ b/src/eap_server/eap_server_tnc.c
@@ -480,7 +480,8 @@
 		message_length = WPA_GET_BE32(pos);
 		pos += 4;
 
-		if (message_length < (u32) (end - pos)) {
+		if (message_length < (u32) (end - pos) ||
+		    message_length > 75000) {
 			wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
 				   "Length (%d; %ld remaining in this msg)",
 				   message_length, (long) (end - pos));
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 647bd2f..31e3871 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -336,7 +336,7 @@
 		data->phase2_method->reset(sm, data->phase2_priv);
 	eap_server_tls_ssl_deinit(sm, &data->ssl);
 	wpabuf_free(data->pending_phase2_eap_resp);
-	os_free(data);
+	bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -509,8 +509,8 @@
 	}
 
 	if (sm->user->password_len != user_password_len ||
-	    os_memcmp(sm->user->password, user_password, user_password_len) !=
-	    0) {
+	    os_memcmp_const(sm->user->password, user_password,
+			    user_password_len) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password");
 		eap_ttls_state(data, FAILURE);
 		return;
@@ -558,7 +558,8 @@
 		return;
 	}
 
-	if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 ||
+	if (os_memcmp_const(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN)
+	    != 0 ||
 	    password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch");
 		os_free(chal);
@@ -571,7 +572,8 @@
 	chap_md5(password[0], sm->user->password, sm->user->password_len,
 		 challenge, challenge_len, hash);
 
-	if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
+	if (os_memcmp_const(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) ==
+	    0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
 		eap_ttls_state(data, SUCCESS);
 	} else {
@@ -616,7 +618,8 @@
 		return;
 	}
 
-	if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 ||
+	if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN)
+	    != 0 ||
 	    response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch");
 		os_free(chal);
@@ -631,7 +634,7 @@
 		nt_challenge_response(challenge, sm->user->password,
 				      sm->user->password_len, nt_response);
 
-	if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
+	if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
 		eap_ttls_state(data, SUCCESS);
 	} else {
@@ -703,7 +706,8 @@
 		return;
 	}
 
-	if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 ||
+	if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN)
+	    != 0 ||
 	    response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch");
 		os_free(chal);
@@ -736,7 +740,7 @@
 	}
 
 	rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
-	if (os_memcmp(nt_response, rx_resp, 24) == 0) {
+	if (os_memcmp_const(nt_response, rx_resp, 24) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
 			   "NT-Response");
 		data->mschapv2_resp_ok = 1;
@@ -984,6 +988,16 @@
 	}
 
 	if (parse.user_name) {
+		char *nbuf;
+		nbuf = os_malloc(parse.user_name_len * 4 + 1);
+		if (nbuf) {
+			printf_encode(nbuf, parse.user_name_len * 4 + 1,
+				      parse.user_name,
+				      parse.user_name_len);
+			eap_log_msg(sm, "TTLS-User-Name '%s'", nbuf);
+			os_free(nbuf);
+		}
+
 		os_free(sm->identity);
 		sm->identity = os_malloc(parse.user_name_len);
 		if (sm->identity == NULL) {
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index 1b9d701..bc2cbe5 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -38,7 +38,6 @@
 	char imsi[20];
 	enum { PENDING, SUCCESS, FAILURE } state;
 	void *cb_session_ctx;
-	struct os_time timestamp;
 	int aka;
 	union {
 		struct {
@@ -630,7 +629,7 @@
 
 	data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (data->sock < 0) {
-		perror("socket(eap_sim_db)");
+		wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno));
 		return -1;
 	}
 
@@ -640,8 +639,13 @@
 		    "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
 	os_free(data->local_sock);
 	data->local_sock = os_strdup(addr.sun_path);
+	if (data->local_sock == NULL) {
+		close(data->sock);
+		data->sock = -1;
+		return -1;
+	}
 	if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind(eap_sim_db)");
+		wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno));
 		close(data->sock);
 		data->sock = -1;
 		return -1;
@@ -651,12 +655,16 @@
 	addr.sun_family = AF_UNIX;
 	os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
 	if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("connect(eap_sim_db)");
+		wpa_printf(MSG_INFO, "connect(eap_sim_db): %s",
+			   strerror(errno));
 		wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
 				  (u8 *) addr.sun_path,
 				  os_strlen(addr.sun_path));
 		close(data->sock);
 		data->sock = -1;
+		unlink(data->local_sock);
+		os_free(data->local_sock);
+		data->local_sock = NULL;
 		return -1;
 	}
 
@@ -804,7 +812,8 @@
 
 	if (send(data->sock, msg, len, 0) < 0) {
 		_errno = errno;
-		perror("send[EAP-SIM DB UNIX]");
+		wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+			   strerror(errno));
 	}
 
 	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
@@ -816,7 +825,8 @@
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
 			   "external server");
 		if (send(data->sock, msg, len, 0) < 0) {
-			perror("send[EAP-SIM DB UNIX]");
+			wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+				   strerror(errno));
 			return -1;
 		}
 	}
@@ -932,7 +942,6 @@
 	if (entry == NULL)
 		return EAP_SIM_DB_FAILURE;
 
-	os_get_time(&entry->timestamp);
 	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
@@ -1392,7 +1401,6 @@
 	if (entry == NULL)
 		return EAP_SIM_DB_FAILURE;
 
-	os_get_time(&entry->timestamp);
 	entry->aka = 1;
 	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
 	entry->cb_session_ctx = cb_session_ctx;
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 11f5827..91449af 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -64,6 +64,7 @@
 
 /* dummy type used as a flag for UNAUTH-TLS */
 #define EAP_UNAUTH_TLS_TYPE 255
+#define EAP_WFA_UNAUTH_TLS_TYPE 254
 
 
 struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 512ba30..632598f 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -633,7 +633,7 @@
 		return -1;
 
 	if (auth_len != prf->hash_len ||
-	    os_memcmp(auth, auth_data, auth_len) != 0) {
+	    os_memcmp_const(auth, auth_data, auth_len) != 0) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
 		wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
 			    auth, auth_len);
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index e429f1e..dc6f689 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -11,6 +11,7 @@
 
 #include "common.h"
 #include "base64.h"
+#include "common/tnc.h"
 #include "tncs.h"
 #include "eap_common/eap_tlv_common.h"
 #include "eap_common/eap_defs.h"
@@ -19,7 +20,9 @@
 /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
  * needed.. */
 
+#ifndef TNC_CONFIG_FILE
 #define TNC_CONFIG_FILE "/etc/tnc_config"
+#endif /* TNC_CONFIG_FILE */
 #define IF_TNCCS_START \
 "<?xml version=\"1.0\"?>\n" \
 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
@@ -31,75 +34,6 @@
 
 /* TNC IF-IMV */
 
-typedef unsigned long TNC_UInt32;
-typedef unsigned char *TNC_BufferReference;
-
-typedef TNC_UInt32 TNC_IMVID;
-typedef TNC_UInt32 TNC_ConnectionID;
-typedef TNC_UInt32 TNC_ConnectionState;
-typedef TNC_UInt32 TNC_RetryReason;
-typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
-typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
-typedef TNC_UInt32 TNC_MessageType;
-typedef TNC_MessageType *TNC_MessageTypeList;
-typedef TNC_UInt32 TNC_VendorID;
-typedef TNC_UInt32 TNC_Subtype;
-typedef TNC_UInt32 TNC_Version;
-typedef TNC_UInt32 TNC_Result;
-typedef TNC_UInt32 TNC_AttributeID;
-
-typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
-	TNC_IMVID imvID,
-	char *functionName,
-	void **pOutfunctionPointer);
-
-#define TNC_RESULT_SUCCESS 0
-#define TNC_RESULT_NOT_INITIALIZED 1
-#define TNC_RESULT_ALREADY_INITIALIZED 2
-#define TNC_RESULT_NO_COMMON_VERSION 3
-#define TNC_RESULT_CANT_RETRY 4
-#define TNC_RESULT_WONT_RETRY 5
-#define TNC_RESULT_INVALID_PARAMETER 6
-#define TNC_RESULT_CANT_RESPOND 7
-#define TNC_RESULT_ILLEGAL_OPERATION 8
-#define TNC_RESULT_OTHER 9
-#define TNC_RESULT_FATAL 10
-
-#define TNC_CONNECTION_STATE_CREATE 0
-#define TNC_CONNECTION_STATE_HANDSHAKE 1
-#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
-#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
-#define TNC_CONNECTION_STATE_ACCESS_NONE 4
-#define TNC_CONNECTION_STATE_DELETE 5
-
-#define TNC_IFIMV_VERSION_1 1
-
-#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
-#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
-
-/* TNCC-TNCS Message Types */
-#define TNC_TNCCS_RECOMMENDATION		0x00000001
-#define TNC_TNCCS_ERROR				0x00000002
-#define TNC_TNCCS_PREFERREDLANGUAGE		0x00000003
-#define TNC_TNCCS_REASONSTRINGS			0x00000004
-
-/* Possible TNC_IMV_Action_Recommendation values: */
-enum IMV_Action_Recommendation {
-	TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
-	TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
-	TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
-	TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
-};
-
-/* Possible TNC_IMV_Evaluation_Result values: */
-enum IMV_Evaluation_Result {
-	TNC_IMV_EVALUATION_RESULT_COMPLIANT,
-	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
-	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
-	TNC_IMV_EVALUATION_RESULT_ERROR,
-	TNC_IMV_EVALUATION_RESULT_DONT_KNOW
-};
-
 struct tnc_if_imv {
 	struct tnc_if_imv *next;
 	char *name;
@@ -1181,6 +1115,9 @@
 {
 	struct tnc_if_imv *imv;
 
+	if (tncs_global_data)
+		return 0;
+
 	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
 	if (tncs_global_data == NULL)
 		return -1;
diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile
index 9c41962..adfd3df 100644
--- a/src/eapol_auth/Makefile
+++ b/src/eapol_auth/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c
index b6e0b13..6c6969b 100644
--- a/src/eapol_auth/eapol_auth_dump.c
+++ b/src/eapol_auth/eapol_auth_dump.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - State dump
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -118,108 +118,172 @@
 }
 
 
-void eapol_auth_dump_state(FILE *f, const char *prefix,
-			   struct eapol_state_machine *sm)
+int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
+			  size_t buflen)
 {
-	fprintf(f, "%sEAPOL state machine:\n", prefix);
-	fprintf(f, "%s  aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix,
-		sm->aWhile, sm->quietWhile, sm->reAuthWhen);
+	char *pos, *end;
+	int ret;
+
+	pos = buf;
+	end = pos + buflen;
+
+	ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n"
+			  "reAuthWhen=%d\n",
+			  sm->aWhile, sm->quietWhile, sm->reAuthWhen);
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
+
 #define _SB(b) ((b) ? "TRUE" : "FALSE")
-	fprintf(f,
-		"%s  authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n"
-		"%s  authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n"
-		"%s  eapSuccess=%s eapTimeout=%s initialize=%s "
-		"keyAvailable=%s\n"
-		"%s  keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n"
-		"%s  portEnabled=%s portValid=%s reAuthenticate=%s\n",
-		prefix, _SB(sm->authAbort), _SB(sm->authFail),
-		port_state_txt(sm->authPortStatus), _SB(sm->authStart),
-		prefix, _SB(sm->authTimeout), _SB(sm->authSuccess),
-		_SB(sm->eap_if->eapFail), _SB(sm->eapolEap),
-		prefix, _SB(sm->eap_if->eapSuccess),
-		_SB(sm->eap_if->eapTimeout),
-		_SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable),
-		prefix, _SB(sm->keyDone), _SB(sm->keyRun),
-		_SB(sm->keyTxEnabled), port_type_txt(sm->portControl),
-		prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid),
-		_SB(sm->reAuthenticate));
+	ret = os_snprintf(pos, end - pos,
+			  "authAbort=%s\n"
+			  "authFail=%s\n"
+			  "authPortStatus=%s\n"
+			  "authStart=%s\n"
+			  "authTimeout=%s\n"
+			  "authSuccess=%s\n"
+			  "eapFail=%s\n"
+			  "eapolEap=%s\n"
+			  "eapSuccess=%s\n"
+			  "eapTimeout=%s\n"
+			  "initialize=%s\n"
+			  "keyAvailable=%s\n"
+			  "keyDone=%s\n"
+			  "keyRun=%s\n"
+			  "keyTxEnabled=%s\n"
+			  "portControl=%s\n"
+			  "portEnabled=%s\n"
+			  "portValid=%s\n"
+			  "reAuthenticate=%s\n",
+			  _SB(sm->authAbort),
+			  _SB(sm->authFail),
+			  port_state_txt(sm->authPortStatus),
+			  _SB(sm->authStart),
+			  _SB(sm->authTimeout),
+			  _SB(sm->authSuccess),
+			  _SB(sm->eap_if->eapFail),
+			  _SB(sm->eapolEap),
+			  _SB(sm->eap_if->eapSuccess),
+			  _SB(sm->eap_if->eapTimeout),
+			  _SB(sm->initialize),
+			  _SB(sm->eap_if->eapKeyAvailable),
+			  _SB(sm->keyDone), _SB(sm->keyRun),
+			  _SB(sm->keyTxEnabled),
+			  port_type_txt(sm->portControl),
+			  _SB(sm->eap_if->portEnabled),
+			  _SB(sm->portValid),
+			  _SB(sm->reAuthenticate));
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
 
-	fprintf(f, "%s  Authenticator PAE:\n"
-		"%s    state=%s\n"
-		"%s    eapolLogoff=%s eapolStart=%s eapRestart=%s\n"
-		"%s    portMode=%s reAuthCount=%d\n"
-		"%s    quietPeriod=%d reAuthMax=%d\n"
-		"%s    authEntersConnecting=%d\n"
-		"%s    authEapLogoffsWhileConnecting=%d\n"
-		"%s    authEntersAuthenticating=%d\n"
-		"%s    authAuthSuccessesWhileAuthenticating=%d\n"
-		"%s    authAuthTimeoutsWhileAuthenticating=%d\n"
-		"%s    authAuthFailWhileAuthenticating=%d\n"
-		"%s    authAuthEapStartsWhileAuthenticating=%d\n"
-		"%s    authAuthEapLogoffWhileAuthenticating=%d\n"
-		"%s    authAuthReauthsWhileAuthenticated=%d\n"
-		"%s    authAuthEapStartsWhileAuthenticated=%d\n"
-		"%s    authAuthEapLogoffWhileAuthenticated=%d\n",
-		prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix,
-		_SB(sm->eapolLogoff), _SB(sm->eapolStart),
-		_SB(sm->eap_if->eapRestart),
-		prefix, port_type_txt(sm->portMode), sm->reAuthCount,
-		prefix, sm->quietPeriod, sm->reAuthMax,
-		prefix, sm->authEntersConnecting,
-		prefix, sm->authEapLogoffsWhileConnecting,
-		prefix, sm->authEntersAuthenticating,
-		prefix, sm->authAuthSuccessesWhileAuthenticating,
-		prefix, sm->authAuthTimeoutsWhileAuthenticating,
-		prefix, sm->authAuthFailWhileAuthenticating,
-		prefix, sm->authAuthEapStartsWhileAuthenticating,
-		prefix, sm->authAuthEapLogoffWhileAuthenticating,
-		prefix, sm->authAuthReauthsWhileAuthenticated,
-		prefix, sm->authAuthEapStartsWhileAuthenticated,
-		prefix, sm->authAuthEapLogoffWhileAuthenticated);
+	ret = os_snprintf(pos, end - pos,
+			  "auth_pae_state=%s\n"
+			  "eapolLogoff=%s\n"
+			  "eapolStart=%s\n"
+			  "eapRestart=%s\n"
+			  "portMode=%s\n"
+			  "reAuthCount=%d\n"
+			  "quietPeriod=%d\n"
+			  "reAuthMax=%d\n"
+			  "authEntersConnecting=%d\n"
+			  "authEapLogoffsWhileConnecting=%d\n"
+			  "authEntersAuthenticating=%d\n"
+			  "authAuthSuccessesWhileAuthenticating=%d\n"
+			  "authAuthTimeoutsWhileAuthenticating=%d\n"
+			  "authAuthFailWhileAuthenticating=%d\n"
+			  "authAuthEapStartsWhileAuthenticating=%d\n"
+			  "authAuthEapLogoffWhileAuthenticating=%d\n"
+			  "authAuthReauthsWhileAuthenticated=%d\n"
+			  "authAuthEapStartsWhileAuthenticated=%d\n"
+			  "authAuthEapLogoffWhileAuthenticated=%d\n",
+			  auth_pae_state_txt(sm->auth_pae_state),
+			  _SB(sm->eapolLogoff),
+			  _SB(sm->eapolStart),
+			  _SB(sm->eap_if->eapRestart),
+			  port_type_txt(sm->portMode),
+			  sm->reAuthCount,
+			  sm->quietPeriod, sm->reAuthMax,
+			  sm->authEntersConnecting,
+			  sm->authEapLogoffsWhileConnecting,
+			  sm->authEntersAuthenticating,
+			  sm->authAuthSuccessesWhileAuthenticating,
+			  sm->authAuthTimeoutsWhileAuthenticating,
+			  sm->authAuthFailWhileAuthenticating,
+			  sm->authAuthEapStartsWhileAuthenticating,
+			  sm->authAuthEapLogoffWhileAuthenticating,
+			  sm->authAuthReauthsWhileAuthenticated,
+			  sm->authAuthEapStartsWhileAuthenticated,
+			  sm->authAuthEapLogoffWhileAuthenticated);
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
 
-	fprintf(f, "%s  Backend Authentication:\n"
-		"%s    state=%s\n"
-		"%s    eapNoReq=%s eapReq=%s eapResp=%s\n"
-		"%s    serverTimeout=%d\n"
-		"%s    backendResponses=%d\n"
-		"%s    backendAccessChallenges=%d\n"
-		"%s    backendOtherRequestsToSupplicant=%d\n"
-		"%s    backendAuthSuccesses=%d\n"
-		"%s    backendAuthFails=%d\n",
-		prefix, prefix,
-		be_auth_state_txt(sm->be_auth_state),
-		prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq),
-		_SB(sm->eap_if->eapResp),
-		prefix, sm->serverTimeout,
-		prefix, sm->backendResponses,
-		prefix, sm->backendAccessChallenges,
-		prefix, sm->backendOtherRequestsToSupplicant,
-		prefix, sm->backendAuthSuccesses,
-		prefix, sm->backendAuthFails);
+	ret = os_snprintf(pos, end - pos,
+			  "be_auth_state=%s\n"
+			  "eapNoReq=%s\n"
+			  "eapReq=%s\n"
+			  "eapResp=%s\n"
+			  "serverTimeout=%d\n"
+			  "backendResponses=%d\n"
+			  "backendAccessChallenges=%d\n"
+			  "backendOtherRequestsToSupplicant=%d\n"
+			  "backendAuthSuccesses=%d\n"
+			  "backendAuthFails=%d\n",
+			  be_auth_state_txt(sm->be_auth_state),
+			  _SB(sm->eap_if->eapNoReq),
+			  _SB(sm->eap_if->eapReq),
+			  _SB(sm->eap_if->eapResp),
+			  sm->serverTimeout,
+			  sm->backendResponses,
+			  sm->backendAccessChallenges,
+			  sm->backendOtherRequestsToSupplicant,
+			  sm->backendAuthSuccesses,
+			  sm->backendAuthFails);
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
 
-	fprintf(f, "%s  Reauthentication Timer:\n"
-		"%s    state=%s\n"
-		"%s    reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix,
-		reauth_timer_state_txt(sm->reauth_timer_state), prefix,
-		sm->reAuthPeriod, _SB(sm->reAuthEnabled));
+	ret = os_snprintf(pos, end - pos,
+			  "reauth_timer_state=%s\n"
+			  "reAuthPeriod=%d\n"
+			  "reAuthEnabled=%s\n",
+			  reauth_timer_state_txt(sm->reauth_timer_state),
+			  sm->reAuthPeriod,
+			  _SB(sm->reAuthEnabled));
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
 
-	fprintf(f, "%s  Authenticator Key Transmit:\n"
-		"%s    state=%s\n", prefix, prefix,
-		auth_key_tx_state_txt(sm->auth_key_tx_state));
+	ret = os_snprintf(pos, end - pos,
+			  "auth_key_tx_state=%s\n",
+			  auth_key_tx_state_txt(sm->auth_key_tx_state));
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
 
-	fprintf(f, "%s  Key Receive:\n"
-		"%s    state=%s\n"
-		"%s    rxKey=%s\n", prefix, prefix,
-		key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey));
+	ret = os_snprintf(pos, end - pos,
+			  "key_rx_state=%s\n"
+			  "rxKey=%s\n",
+			  key_rx_state_txt(sm->key_rx_state),
+			  _SB(sm->rxKey));
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
 
-	fprintf(f, "%s  Controlled Directions:\n"
-		"%s    state=%s\n"
-		"%s    adminControlledDirections=%s "
-		"operControlledDirections=%s\n"
-		"%s    operEdge=%s\n", prefix, prefix,
-		ctrl_dir_state_txt(sm->ctrl_dir_state),
-		prefix, ctrl_dir_txt(sm->adminControlledDirections),
-		ctrl_dir_txt(sm->operControlledDirections),
-		prefix, _SB(sm->operEdge));
+	ret = os_snprintf(pos, end - pos,
+			  "ctrl_dir_state=%s\n"
+			  "adminControlledDirections=%s\n"
+			  "operControlledDirections=%s\n"
+			  "operEdge=%s\n",
+			  ctrl_dir_state_txt(sm->ctrl_dir_state),
+			  ctrl_dir_txt(sm->adminControlledDirections),
+			  ctrl_dir_txt(sm->operControlledDirections),
+			  _SB(sm->operEdge));
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
 #undef _SB
+
+	return pos - buf;
 }
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 013d781..a76fa13 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -219,7 +219,8 @@
 	sm->eapolLogoff = FALSE;
 	if (!from_initialize) {
 		sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
-				       sm->flags & EAPOL_SM_PREAUTH);
+				       sm->flags & EAPOL_SM_PREAUTH,
+				       sm->remediation);
 	}
 }
 
@@ -276,7 +277,7 @@
 				   eap_server_get_name(0, sm->eap_type_supp));
 	}
 	sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
-			       sm->flags & EAPOL_SM_PREAUTH);
+			       sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
 }
 
 
@@ -302,7 +303,7 @@
 			   eap_server_get_name(0, sm->eap_type_authsrv),
 			   extra);
 	sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1,
-			       sm->flags & EAPOL_SM_PREAUTH);
+			       sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
 }
 
 
@@ -1001,8 +1002,13 @@
 				 struct eap_user *user)
 {
 	struct eapol_state_machine *sm = ctx;
-	return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
-					  identity_len, phase2, user);
+	int ret;
+
+	ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
+					 identity_len, phase2, user);
+	if (user->remediation)
+		sm->remediation = 1;
+	return ret;
 }
 
 
@@ -1017,7 +1023,8 @@
 static struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_eap_user,
-	eapol_sm_get_eap_req_id_text
+	eapol_sm_get_eap_req_id_text,
+	NULL
 };
 
 
@@ -1062,6 +1069,10 @@
 	}
 	if (src->pac_opaque_encr_key) {
 		dst->pac_opaque_encr_key = os_malloc(16);
+		if (dst->pac_opaque_encr_key == NULL) {
+			os_free(dst->eap_req_id_text);
+			return -1;
+		}
 		os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
 			  16);
 	} else
@@ -1070,6 +1081,7 @@
 		dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
 		if (dst->eap_fast_a_id == NULL) {
 			os_free(dst->eap_req_id_text);
+			os_free(dst->pac_opaque_encr_key);
 			return -1;
 		}
 		os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
@@ -1081,6 +1093,7 @@
 		dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
 		if (dst->eap_fast_a_id_info == NULL) {
 			os_free(dst->eap_req_id_text);
+			os_free(dst->pac_opaque_encr_key);
 			os_free(dst->eap_fast_a_id);
 			return -1;
 		}
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 3a0f450..320a0ad 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -60,7 +60,8 @@
 			   size_t datalen);
 	void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data,
 			 size_t datalen);
-	void (*finished)(void *ctx, void *sta_ctx, int success, int preauth);
+	void (*finished)(void *ctx, void *sta_ctx, int success, int preauth,
+			 int remediation);
 	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
 			    int phase2, struct eap_user *user);
 	int (*sta_entry_alive)(void *ctx, const u8 *addr);
@@ -83,8 +84,8 @@
 		 const char *identity, const char *radius_cui);
 void eapol_auth_free(struct eapol_state_machine *sm);
 void eapol_auth_step(struct eapol_state_machine *sm);
-void eapol_auth_dump_state(FILE *f, const char *prefix,
-			   struct eapol_state_machine *sm);
+int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
+			  size_t buflen);
 int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx);
 
 #endif /* EAPOL_AUTH_SM_H */
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index d7f893a..25baddb 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -173,6 +173,8 @@
 	struct eapol_authenticator *eapol;
 
 	void *sta; /* station context pointer to use in callbacks */
+
+	int remediation;
 };
 
 #endif /* EAPOL_AUTH_SM_I_H */
diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile
index 9c41962..adfd3df 100644
--- a/src/eapol_supp/Makefile
+++ b/src/eapol_supp/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 9b054fc..cf3506d 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -137,6 +137,9 @@
 	Boolean cached_pmk;
 
 	Boolean unicast_key_received, broadcast_key_received;
+
+	Boolean force_authorized_update;
+
 #ifdef CONFIG_EAP_PROXY
 	Boolean use_eap_proxy;
 	struct eap_proxy_sm *eap_proxy;
@@ -210,7 +213,6 @@
 	SM_ENTRY(SUPP_PAE, LOGOFF);
 	eapol_sm_txLogoff(sm);
 	sm->logoffSent = TRUE;
-	sm->suppPortStatus = Unauthorized;
 	eapol_sm_set_port_unauthorized(sm);
 }
 
@@ -221,7 +223,6 @@
 	sm->sPortMode = Auto;
 	sm->startCount = 0;
 	sm->logoffSent = FALSE;
-	sm->suppPortStatus = Unauthorized;
 	eapol_sm_set_port_unauthorized(sm);
 	sm->suppAbort = TRUE;
 
@@ -286,7 +287,6 @@
 	SM_ENTRY(SUPP_PAE, HELD);
 	sm->heldWhile = sm->heldPeriod;
 	eapol_enable_timer_tick(sm);
-	sm->suppPortStatus = Unauthorized;
 	eapol_sm_set_port_unauthorized(sm);
 	sm->cb_status = EAPOL_CB_FAILURE;
 }
@@ -295,7 +295,6 @@
 SM_STATE(SUPP_PAE, AUTHENTICATED)
 {
 	SM_ENTRY(SUPP_PAE, AUTHENTICATED);
-	sm->suppPortStatus = Authorized;
 	eapol_sm_set_port_authorized(sm);
 	sm->cb_status = EAPOL_CB_SUCCESS;
 }
@@ -311,7 +310,6 @@
 SM_STATE(SUPP_PAE, S_FORCE_AUTH)
 {
 	SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
-	sm->suppPortStatus = Authorized;
 	eapol_sm_set_port_authorized(sm);
 	sm->sPortMode = ForceAuthorized;
 }
@@ -320,7 +318,6 @@
 SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
 {
 	SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
-	sm->suppPortStatus = Unauthorized;
 	eapol_sm_set_port_unauthorized(sm);
 	sm->sPortMode = ForceUnauthorized;
 	eapol_sm_txLogoff(sm);
@@ -722,8 +719,8 @@
 	hmac_md5(keydata.sign_key, sign_key_len,
 		 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
 		 key->key_signature);
-	if (os_memcmp(orig_key_sign, key->key_signature,
-		      IEEE8021X_KEY_SIGN_LEN) != 0) {
+	if (os_memcmp_const(orig_key_sign, key->key_signature,
+			    IEEE8021X_KEY_SIGN_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
 			   "EAPOL-Key packet");
 		os_memcpy(key->key_signature, orig_key_sign,
@@ -879,14 +876,24 @@
 
 static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
 {
-	if (sm->ctx->port_cb)
+	int cb;
+
+	cb = sm->suppPortStatus != Authorized || sm->force_authorized_update;
+	sm->force_authorized_update = FALSE;
+	sm->suppPortStatus = Authorized;
+	if (cb && sm->ctx->port_cb)
 		sm->ctx->port_cb(sm->ctx->ctx, 1);
 }
 
 
 static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
 {
-	if (sm->ctx->port_cb)
+	int cb;
+
+	cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update;
+	sm->force_authorized_update = FALSE;
+	sm->suppPortStatus = Unauthorized;
+	if (cb && sm->ctx->port_cb)
 		sm->ctx->port_cb(sm->ctx->ctx, 0);
 }
 
@@ -933,9 +940,15 @@
 	}
 
 	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
-		int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
+		enum eapol_supp_result result;
+		if (sm->cb_status == EAPOL_CB_SUCCESS)
+			result = EAPOL_SUPP_RESULT_SUCCESS;
+		else if (eap_peer_was_failure_expected(sm->eap))
+			result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+		else
+			result = EAPOL_SUPP_RESULT_FAILURE;
 		sm->cb_status = EAPOL_CB_IN_PROGRESS;
-		sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
+		sm->ctx->cb(sm, result, sm->ctx->cb_ctx);
 	}
 }
 
@@ -1257,6 +1270,24 @@
 
 	switch (hdr->type) {
 	case IEEE802_1X_TYPE_EAP_PACKET:
+		if (sm->conf.workaround) {
+			/*
+			 * An AP has been reported to send out EAP message with
+			 * undocumented code 10 at some point near the
+			 * completion of EAP authentication. This can result in
+			 * issues with the unexpected EAP message triggering
+			 * restart of EAPOL authentication. Avoid this by
+			 * skipping the message without advancing the state
+			 * machine.
+			 */
+			const struct eap_hdr *ehdr =
+				(const struct eap_hdr *) (hdr + 1);
+			if (plen >= sizeof(*ehdr) && ehdr->code == 10) {
+				wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10");
+				break;
+			}
+		}
+
 		if (sm->cached_pmk) {
 			/* Trying to use PMKSA caching, but Authenticator did
 			 * not seem to have a matching entry. Need to restart
@@ -1314,6 +1345,13 @@
 			eapol_sm_step(sm);
 		}
 		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, "EAPOL: Received unknown EAPOL type %d",
 			   hdr->type);
@@ -1352,6 +1390,8 @@
 		return;
 	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
 		   "portEnabled=%d", enabled);
+	if (sm->portEnabled != enabled)
+		sm->force_authorized_update = TRUE;
 	sm->portEnabled = enabled;
 	eapol_sm_step(sm);
 }
@@ -1461,6 +1501,7 @@
 		eap_set_fast_reauth(sm->eap, conf->fast_reauth);
 		eap_set_workaround(sm->eap, conf->workaround);
 		eap_set_force_disabled(sm->eap, conf->eap_disabled);
+		eap_set_external_sim(sm->eap, conf->external_sim);
 	}
 }
 
@@ -1482,7 +1523,7 @@
 	size_t eap_len;
 
 #ifdef CONFIG_EAP_PROXY
-	if (sm->use_eap_proxy) {
+	if (sm && sm->use_eap_proxy) {
 		/* Get key from EAP proxy */
 		if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) {
 			wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
@@ -1523,6 +1564,24 @@
 
 
 /**
+ * eapol_sm_get_session_id - Get EAP Session-Id
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the session
+ * Returns: Pointer to the EAP Session-Id or %NULL on failure
+ *
+ * The Session-Id is available only after a successful authentication.
+ */
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+	if (sm == NULL || !eap_key_available(sm->eap)) {
+		wpa_printf(MSG_DEBUG, "EAPOL: EAP Session-Id not available");
+		return NULL;
+	}
+	return eap_get_eapSessionId(sm->eap, len);
+}
+
+
+/**
  * eapol_sm_notify_logoff - Notification of logon/logoff commands
  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
  * @logoff: Whether command was logoff
@@ -1589,7 +1648,6 @@
 		return;
 	sm->cached_pmk = FALSE;
 	sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
-	sm->suppPortStatus = Unauthorized;
 	eapol_sm_set_port_unauthorized(sm);
 
 	/* Make sure we do not start sending EAPOL-Start frames first, but
@@ -1983,6 +2041,7 @@
 #endif /* CONFIG_EAP_PROXY */
 
 	/* Initialize EAPOL state machines */
+	sm->force_authorized_update = TRUE;
 	sm->initialize = TRUE;
 	eapol_sm_step(sm);
 	sm->initialize = FALSE;
@@ -2032,3 +2091,15 @@
 		return 0;
 	return !sm->eapSuccess && sm->eapFail;
 }
+
+
+int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
+{
+#ifdef CONFIG_EAP_PROXY
+	if (sm->eap_proxy == NULL)
+		return -1;
+	return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
+#else /* CONFIG_EAP_PROXY */
+	return -1;
+#endif /* CONFIG_EAP_PROXY */
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index c4b87da..d76c8c2 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -53,11 +53,22 @@
 	 * eap_disabled - Whether EAP is disabled
 	 */
 	int eap_disabled;
+
+	/**
+	 * external_sim - Use external processing for SIM/USIM operations
+	 */
+	int external_sim;
 };
 
 struct eapol_sm;
 struct wpa_config_blob;
 
+enum eapol_supp_result {
+	EAPOL_SUPP_RESULT_FAILURE,
+	EAPOL_SUPP_RESULT_SUCCESS,
+	EAPOL_SUPP_RESULT_EXPECTED_FAILURE
+};
+
 /**
  * struct eapol_ctx - Global (for all networks) EAPOL state machine context
  */
@@ -78,7 +89,7 @@
 	/**
 	 * cb - Function to be called when EAPOL negotiation has been completed
 	 * @eapol: Pointer to EAPOL state machine data
-	 * @success: Whether the authentication was completed successfully
+	 * @result: Whether the authentication was completed successfully
 	 * @ctx: Pointer to context data (cb_ctx)
 	 *
 	 * This optional callback function will be called when the EAPOL
@@ -86,7 +97,8 @@
 	 * EAPOL state machine to process the key and terminate the EAPOL state
 	 * machine. Currently, this is used only in RSN pre-authentication.
 	 */
-	void (*cb)(struct eapol_sm *eapol, int success, void *ctx);
+	void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result,
+		   void *ctx);
 
 	/**
 	 * cb_ctx - Callback context for cb()
@@ -273,6 +285,7 @@
 			    struct eap_peer_config *config,
 			    const struct eapol_config *conf);
 int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len);
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len);
 void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
 void eapol_sm_notify_cached(struct eapol_sm *sm);
 void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt);
@@ -287,6 +300,7 @@
 void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
 			     struct ext_password_data *ext);
 int eapol_sm_failed(struct eapol_sm *sm);
+int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 {
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
index 9c41962..adfd3df 100644
--- a/src/l2_packet/Makefile
+++ b/src/l2_packet/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c
index b01e830..6896c4e 100644
--- a/src/l2_packet/l2_packet_none.c
+++ b/src/l2_packet/l2_packet_none.c
@@ -84,7 +84,8 @@
 	 * TODO: open connection for receiving frames
 	 */
 	l2->fd = -1;
-	eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+	if (l2->fd >= 0)
+		eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
 
 	return l2;
 }
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
index cffba62..adfd3df 100644
--- a/src/p2p/Makefile
+++ b/src/p2p/Makefile
@@ -2,8 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	for d in $(SUBDIRS); do make -C $$d clean; done
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index fa6a849..667b96b 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -12,6 +12,7 @@
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -41,51 +42,32 @@
  * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
  * entries will be removed
  */
-#ifdef ANDROID_P2P
-#define P2P_PEER_EXPIRATION_AGE 30
-#else
-#define P2P_PEER_EXPIRATION_AGE 300
-#endif
+#ifndef P2P_PEER_EXPIRATION_AGE
+#define P2P_PEER_EXPIRATION_AGE 60
+#endif /* P2P_PEER_EXPIRATION_AGE */
 
 #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
 
-#ifdef ANDROID_P2P
-int p2p_connection_in_progress(struct p2p_data *p2p)
-{
-	int ret = 0;
-
-	switch (p2p->state) {
-		case P2P_CONNECT:
-		case P2P_CONNECT_LISTEN:
-		case P2P_GO_NEG:
-		case P2P_WAIT_PEER_CONNECT:
-		case P2P_WAIT_PEER_IDLE:
-		case P2P_PROVISIONING:
-		case P2P_INVITE:
-		case P2P_INVITE_LISTEN:
-			ret = 1;
-			break;
-
-		default:
-			wpa_printf(MSG_DEBUG, "p2p_connection_in_progress state %d", p2p->state);
-			ret = 0;
-	}
-
-	return ret;
-}
-#endif
-
 static void p2p_expire_peers(struct p2p_data *p2p)
 {
 	struct p2p_device *dev, *n;
-	struct os_time now;
+	struct os_reltime now;
 	size_t i;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
 		if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
 			continue;
 
+		if (dev == p2p->go_neg_peer) {
+			/*
+			 * GO Negotiation is in progress with the peer, so
+			 * don't expire the peer entry until GO Negotiation
+			 * fails or times out.
+			 */
+			continue;
+		}
+
 		if (p2p->cfg->go_connected &&
 		    p2p->cfg->go_connected(p2p->cfg->cb_ctx,
 					   dev->info.p2p_device_addr)) {
@@ -93,7 +75,7 @@
 			 * We are connected as a client to a group in which the
 			 * peer is the GO, so do not expire the peer entry.
 			 */
-			os_get_time(&dev->last_seen);
+			os_get_reltime(&dev->last_seen);
 			continue;
 		}
 
@@ -107,25 +89,12 @@
 			 * The peer is connected as a client in a group where
 			 * we are the GO, so do not expire the peer entry.
 			 */
-			os_get_time(&dev->last_seen);
+			os_get_reltime(&dev->last_seen);
 			continue;
 		}
 
-#ifdef ANDROID_P2P
-		/* If Connection is in progress, don't expire the peer
-		*/
-		if (p2p_connection_in_progress(p2p))
-			continue;
-#endif
-
 		p2p_dbg(p2p, "Expiring old peer entry " MACSTR,
 			MAC2STR(dev->info.p2p_device_addr));
-
-#ifdef ANDROID_P2P
-		/* SD_FAIR_POLICY: Update the current sd_dev_list pointer to next device */
-		if(&dev->list == p2p->sd_dev_list)
-			p2p->sd_dev_list = dev->list.next;
-#endif
 		dl_list_del(&dev->list);
 		p2p_device_free(p2p, dev);
 	}
@@ -170,10 +139,6 @@
 		return "INVITE";
 	case P2P_INVITE_LISTEN:
 		return "INVITE_LISTEN";
-	case P2P_SEARCH_WHEN_READY:
-		return "SEARCH_WHEN_READY";
-	case P2P_CONTINUE_SEARCH_WHEN_READY:
-		return "CONTINUE_SEARCH_WHEN_READY";
 	default:
 		return "?";
 	}
@@ -219,6 +184,14 @@
 	p2p_dbg(p2p, "State %s -> %s",
 		p2p_state_txt(p2p->state), p2p_state_txt(new_state));
 	p2p->state = new_state;
+
+	if (new_state == P2P_IDLE && p2p->pending_channel) {
+		p2p_dbg(p2p, "Apply change in listen channel");
+		p2p->cfg->reg_class = p2p->pending_reg_class;
+		p2p->cfg->channel = p2p->pending_channel;
+		p2p->pending_reg_class = 0;
+		p2p->pending_channel = 0;
+	}
 }
 
 
@@ -247,12 +220,15 @@
 	if (p2p->go_neg_peer) {
 		p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
 		p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+		p2p->go_neg_peer->oob_pw_id = 0;
 	}
 	p2p->go_neg_peer = NULL;
 
 	os_memset(&res, 0, sizeof(res));
 	res.status = status;
 	if (peer) {
+		wpabuf_free(peer->go_neg_conf);
+		peer->go_neg_conf = NULL;
 		os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
 			  ETH_ALEN);
 		os_memcpy(res.peer_interface_addr, peer->intended_addr,
@@ -271,6 +247,12 @@
 	p2p_dbg(p2p, "Starting short listen state (state=%s)",
 		p2p_state_txt(p2p->state));
 
+	if (p2p->pending_listen_freq) {
+		/* We have a pending p2p_listen request */
+		p2p_dbg(p2p, "p2p_listen command pending already");
+		return;
+	}
+
 	freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel);
 	if (freq < 0) {
 		p2p_dbg(p2p, "Unknown regulatory class/channel");
@@ -293,14 +275,14 @@
 		return;
 	}
 
-	p2p->pending_listen_freq = freq;
-	p2p->pending_listen_sec = 0;
-	p2p->pending_listen_usec = 1024 * tu;
-
 	ies = p2p_build_probe_resp_ies(p2p);
 	if (ies == NULL)
 		return;
 
+	p2p->pending_listen_freq = freq;
+	p2p->pending_listen_sec = 0;
+	p2p->pending_listen_usec = 1024 * tu;
+
 	if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
 		    ies) < 0) {
 		p2p_dbg(p2p, "Failed to start listen mode");
@@ -317,13 +299,18 @@
 
 	p2p_dbg(p2p, "Going to listen(only) state");
 
+	if (p2p->pending_listen_freq) {
+		/* We have a pending p2p_listen request */
+		p2p_dbg(p2p, "p2p_listen command pending already");
+		return -1;
+	}
+
 	freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel);
 	if (freq < 0) {
 		p2p_dbg(p2p, "Unknown regulatory class/channel");
 		return -1;
 	}
 
-	p2p->pending_listen_freq = freq;
 	p2p->pending_listen_sec = timeout / 1000;
 	p2p->pending_listen_usec = (timeout % 1000) * 1000;
 
@@ -341,6 +328,8 @@
 	if (ies == NULL)
 		return -1;
 
+	p2p->pending_listen_freq = freq;
+
 	if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
 		p2p_dbg(p2p, "Failed to start listen mode");
 		p2p->pending_listen_freq = 0;
@@ -420,16 +409,11 @@
 	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 		count++;
 		if (oldest == NULL ||
-		    os_time_before(&dev->last_seen, &oldest->last_seen))
+		    os_reltime_before(&dev->last_seen, &oldest->last_seen))
 			oldest = dev;
 	}
 	if (count + 1 > p2p->cfg->max_peers && oldest) {
 		p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer");
-#ifdef ANDROID_P2P
-		/* SD_FAIR_POLICY: Update the current sd_dev_list pointer to next device */
-		if(&oldest->list == p2p->sd_dev_list)
-			p2p->sd_dev_list = oldest->list.next;
-#endif
 		dl_list_del(&oldest->list);
 		p2p_device_free(p2p, oldest);
 	}
@@ -528,7 +512,7 @@
 
 		os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
 			  ETH_ALEN);
-		os_get_time(&dev->last_seen);
+		os_get_reltime(&dev->last_seen);
 		os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
 		os_memcpy(dev->member_in_go_iface, go_interface_addr,
 			  ETH_ALEN);
@@ -625,6 +609,46 @@
 }
 
 
+static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
+					 size_t ies_len)
+{
+	const u8 *pos, *end;
+	u8 id, len;
+
+	wpabuf_free(dev->info.vendor_elems);
+	dev->info.vendor_elems = NULL;
+
+	end = ies + ies_len;
+
+	for (pos = ies; pos + 1 < end; pos += len) {
+		id = *pos++;
+		len = *pos++;
+
+		if (pos + len > end)
+			break;
+
+		if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3)
+			continue;
+
+		if (len >= 4) {
+			u32 type = WPA_GET_BE32(pos);
+
+			if (type == WPA_IE_VENDOR_TYPE ||
+			    type == WMM_IE_VENDOR_TYPE ||
+			    type == WPS_IE_VENDOR_TYPE ||
+			    type == P2P_IE_VENDOR_TYPE ||
+			    type == WFD_IE_VENDOR_TYPE)
+				continue;
+		}
+
+		/* Unknown vendor element - make raw IE data available */
+		if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0)
+			break;
+		wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len);
+	}
+}
+
+
 /**
  * p2p_add_device - Add peer entries based on scan results or P2P frames
  * @p2p: P2P module context from p2p_init()
@@ -644,14 +668,14 @@
  * Info attributes.
  */
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
-		   struct os_time *rx_time, int level, const u8 *ies,
+		   struct os_reltime *rx_time, int level, const u8 *ies,
 		   size_t ies_len, int scan_res)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
 	const u8 *p2p_dev_addr;
 	int i;
-	struct os_time time_now;
+	struct os_reltime time_now;
 
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ies, ies_len, &msg)) {
@@ -685,7 +709,7 @@
 	}
 
 	if (rx_time == NULL) {
-		os_get_time(&time_now);
+		os_get_reltime(&time_now);
 		rx_time = &time_now;
 	}
 
@@ -694,7 +718,7 @@
 	 * entry is newer than the one previously stored.
 	 */
 	if (dev->last_seen.sec > 0 &&
-	    os_time_before(rx_time, &dev->last_seen)) {
+	    os_reltime_before(rx_time, &dev->last_seen)) {
 		p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)",
 			(unsigned int) rx_time->sec,
 			(unsigned int) rx_time->usec,
@@ -704,7 +728,7 @@
 		return -1;
 	}
 
-	os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_time));
+	os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
 
 	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
 
@@ -774,8 +798,7 @@
 
 	p2p_parse_free(&msg);
 
-	if (p2p_pending_sd_req(p2p, dev))
-		dev->flags |= P2P_DEV_SD_SCHEDULE;
+	p2p_update_peer_vendor_elems(dev, ies, ies_len);
 
 	if (dev->flags & P2P_DEV_REPORTED)
 		return 0;
@@ -846,6 +869,8 @@
 	}
 
 	wpabuf_free(dev->info.wfd_subelems);
+	wpabuf_free(dev->info.vendor_elems);
+	wpabuf_free(dev->go_neg_conf);
 
 	os_free(dev);
 }
@@ -933,9 +958,6 @@
 	if (res < 0) {
 		p2p_dbg(p2p, "Scan request failed");
 		p2p_continue_find(p2p);
-	} else if (res == 1) {
-		p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
-		p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY);
 	} else {
 		p2p_dbg(p2p, "Running p2p_scan");
 		p2p->p2p_scan_running = 1;
@@ -972,15 +994,7 @@
 				      p2p->after_scan_tx->wait_time);
 		os_free(p2p->after_scan_tx);
 		p2p->after_scan_tx = NULL;
-#ifdef ANDROID_P2P
-		/* For SD frames, there is a scenario, where we can receive a SD request frame during p2p_scan.
-		 * At that moment, we will send the SD response from this context. After sending the SD response,
-		 * we need to continue p2p_find. But if we return 1 from here, p2p_find is going to be stopped.
-		 */
-		return 0;
-#else
 		return 1;
-#endif
 	}
 
 	op = p2p->start_after_scan;
@@ -1039,7 +1053,7 @@
 	int res;
 
 	p2p_dbg(p2p, "Starting find (type=%d)", type);
-	os_get_time(&p2p->find_start);
+	os_get_reltime(&p2p->find_start);
 	if (p2p->p2p_scan_running) {
 		p2p_dbg(p2p, "p2p_scan is already running");
 	}
@@ -1098,11 +1112,10 @@
 		eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
 		eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
 				       p2p, NULL);
-	} else if (res == 1) {
-		p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
-		res = 0;
-		p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
-		eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+	} else if (p2p->p2p_scan_running) {
+		p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
+		/* wait for the previous p2p_scan to complete */
+		res = 0; /* do not report failure */
 	} else {
 		p2p_dbg(p2p, "Failed to start p2p_scan");
 		p2p_set_state(p2p, P2P_IDLE);
@@ -1112,47 +1125,13 @@
 	return res;
 }
 
-#ifdef ANDROID_P2P
-int p2p_search_pending(struct p2p_data *p2p)
-{
-	if(p2p == NULL)
-		return 0;
-
-	if(p2p->state == P2P_SEARCH_WHEN_READY)
-		return 1;
-
-	return 0;
-}
-#endif
-
-int p2p_other_scan_completed(struct p2p_data *p2p)
-{
-	if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) {
-		p2p_set_state(p2p, P2P_SEARCH);
-		p2p_search(p2p);
-		return 1;
-	}
-	if (p2p->state != P2P_SEARCH_WHEN_READY)
-		return 0;
-	p2p_dbg(p2p, "Starting pending P2P find now that previous scan was completed");
-	if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
-		     p2p->num_req_dev_types, p2p->req_dev_types,
-		     p2p->find_dev_id, p2p->search_delay) < 0) {
-		p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
-		return 0;
-	}
-	return 1;
-}
-
 
 void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
 {
 	p2p_dbg(p2p, "Stopping find");
 	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
 	p2p_clear_timeout(p2p);
-	if (p2p->state == P2P_SEARCH ||
-	    p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY ||
-	    p2p->state == P2P_SEARCH_WHEN_READY)
+	if (p2p->state == P2P_SEARCH)
 		p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
 	p2p_set_state(p2p, P2P_IDLE);
 	p2p_free_req_dev_types(p2p);
@@ -1203,25 +1182,28 @@
 
 void p2p_stop_find(struct p2p_data *p2p)
 {
+	p2p->pending_listen_freq = 0;
 	p2p_stop_find_for_freq(p2p, 0);
 }
 
 
 static int p2p_prepare_channel_pref(struct p2p_data *p2p,
 				    unsigned int force_freq,
-				    unsigned int pref_freq)
+				    unsigned int pref_freq, int go)
 {
 	u8 op_class, op_channel;
 	unsigned int freq = force_freq ? force_freq : pref_freq;
 
-	p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u",
-		force_freq, pref_freq);
+	p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d",
+		force_freq, pref_freq, go);
 	if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) {
 		p2p_dbg(p2p, "Unsupported frequency %u MHz", freq);
 		return -1;
 	}
 
-	if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) {
+	if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) &&
+	    (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class,
+					  op_channel))) {
 		p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P",
 			freq, op_class, op_channel);
 		return -1;
@@ -1247,6 +1229,9 @@
 static void p2p_prepare_channel_best(struct p2p_data *p2p)
 {
 	u8 op_class, op_channel;
+	const int op_classes_5ghz[] = { 124, 115, 0 };
+	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
+	const int op_classes_vht[] = { 128, 0 };
 
 	p2p_dbg(p2p, "Prepare channel best");
 
@@ -1278,10 +1263,40 @@
 		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 {
+	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible VHT 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_ht40,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible HT40 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_5ghz,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference",
+			p2p->op_reg_class, p2p->op_channel);
+	} else if (p2p_channels_includes(&p2p->cfg->channels,
+					 p2p->cfg->op_reg_class,
+					 p2p->cfg->op_channel)) {
 		p2p_dbg(p2p, "Select pre-configured channel as operating channel preference");
 		p2p->op_reg_class = p2p->cfg->op_reg_class;
 		p2p->op_channel = p2p->cfg->op_channel;
+	} else if (p2p_channel_random_social(&p2p->cfg->channels,
+					     &p2p->op_reg_class,
+					     &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Select random available social channel %d from 2.4 GHz band as operating channel preference",
+			p2p->op_channel);
+	} else {
+		/* Select any random available channel from the first available
+		 * operating class */
+		p2p_channel_select(&p2p->cfg->channels, NULL,
+				   &p2p->op_reg_class,
+				   &p2p->op_channel);
+		p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference",
+			p2p->op_channel, p2p->op_reg_class);
 	}
 
 	os_memcpy(&p2p->channels, &p2p->cfg->channels,
@@ -1295,6 +1310,7 @@
  * @dev: Selected peer device
  * @force_freq: Forced frequency in MHz or 0 if not forced
  * @pref_freq: Preferred frequency in MHz or 0 if no preference
+ * @go: Whether the local end will be forced to be GO
  * Returns: 0 on success, -1 on failure (channel not supported for P2P)
  *
  * This function is used to do initial operating channel selection for GO
@@ -1303,16 +1319,25 @@
  * is available.
  */
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
-			unsigned int force_freq, unsigned int pref_freq)
+			unsigned int force_freq, unsigned int pref_freq, int go)
 {
-	p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u",
-		force_freq, pref_freq);
+	p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d",
+		force_freq, pref_freq, go);
 	if (force_freq || pref_freq) {
-		if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0)
+		if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) <
+		    0)
 			return -1;
 	} else {
 		p2p_prepare_channel_best(p2p);
 	}
+	p2p_channels_dump(p2p, "prepared channels", &p2p->channels);
+	if (go)
+		p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
+	else if (!force_freq)
+		p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels,
+				   &p2p->channels);
+	p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
+
 	p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
 		p2p->op_reg_class, p2p->op_channel,
 		force_freq ? " (forced)" : "");
@@ -1351,15 +1376,16 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq)
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+		" wps_method=%d persistent_group=%d pd_before_go_neg=%d "
+		"oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group, pd_before_go_neg);
+		wps_method, persistent_group, pd_before_go_neg, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -1368,7 +1394,8 @@
 		return -1;
 	}
 
-	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
+				go_intent == 15) < 0)
 		return -1;
 
 	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
@@ -1442,6 +1469,7 @@
 	}
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	if (p2p->p2p_scan_running) {
@@ -1461,15 +1489,15 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq)
+		  unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d  persistent_group=%d",
+		" wps_method=%d  persistent_group=%d oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group);
+		wps_method, persistent_group, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL) {
@@ -1478,7 +1506,8 @@
 		return -1;
 	}
 
-	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
+				15) < 0)
 		return -1;
 
 	p2p->ssid_set = 0;
@@ -1499,6 +1528,7 @@
 	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	return 0;
@@ -1508,7 +1538,7 @@
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
 		      struct p2p_device *dev, struct p2p_message *msg)
 {
-	os_get_time(&dev->last_seen);
+	os_get_reltime(&dev->last_seen);
 
 	p2p_copy_wps_info(p2p, dev, 0, msg);
 
@@ -1576,7 +1606,7 @@
 int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
 {
 	p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
-	p2p_random(params->passphrase, 8);
+	p2p_random(params->passphrase, p2p->cfg->passphrase_len);
 	return 0;
 }
 
@@ -1610,7 +1640,7 @@
 					       p2p->op_channel);
 		os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
 		res.ssid_len = p2p->ssid_len;
-		p2p_random(res.passphrase, 8);
+		p2p_random(res.passphrase, p2p->cfg->passphrase_len);
 	} else {
 		res.freq = peer->oper_freq;
 		if (p2p->ssid_len) {
@@ -1619,8 +1649,15 @@
 		}
 	}
 
+	p2p_channels_dump(p2p, "own channels", &p2p->channels);
+	p2p_channels_dump(p2p, "peer channels", &peer->channels);
 	p2p_channels_intersect(&p2p->channels, &peer->channels,
 			       &intersection);
+	if (go) {
+		p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
+		p2p_channels_dump(p2p, "intersection after no-GO removal",
+				  &intersection);
+	}
 	freqs = 0;
 	for (i = 0; i < intersection.reg_classes; i++) {
 		struct p2p_reg_class *c = &intersection.reg_class[i];
@@ -1643,6 +1680,9 @@
 	p2p->ssid_set = 0;
 	peer->go_neg_req_sent = 0;
 	peer->wps_method = WPS_NOT_READY;
+	peer->oob_pw_id = 0;
+	wpabuf_free(peer->go_neg_conf);
+	peer->go_neg_conf = NULL;
 
 	p2p_set_state(p2p, P2P_PROVISIONING);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1673,6 +1713,7 @@
 					   rx_freq);
 		break;
 	case P2P_INVITATION_RESP:
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
 		break;
 	case P2P_PROV_DISC_REQ:
@@ -1706,20 +1747,15 @@
 	case WLAN_PA_VENDOR_SPECIFIC:
 		data++;
 		len--;
-		if (len < 3)
+		if (len < 4)
 			return;
-		if (WPA_GET_BE24(data) != OUI_WFA)
+		if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
 			return;
 
-		data += 3;
-		len -= 3;
-		if (len < 1)
-			return;
+		data += 4;
+		len -= 4;
 
-		if (*data != P2P_OUI_TYPE)
-			return;
-
-		p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+		p2p_rx_p2p_action(p2p, sa, data, len, freq);
 		break;
 	case WLAN_PA_GAS_INITIAL_REQ:
 		p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
@@ -1752,15 +1788,10 @@
 	if (len < 4)
 		return;
 
-	if (WPA_GET_BE24(data) != OUI_WFA)
+	if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
 		return;
-	data += 3;
-	len -= 3;
-
-	if (*data != P2P_OUI_TYPE)
-		return;
-	data++;
-	len--;
+	data += 4;
+	len -= 4;
 
 	/* P2P action frame */
 	p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa));
@@ -1806,7 +1837,8 @@
 	if (p2p->invite_peer == NULL)
 		return;
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
-	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+			p2p->invite_dev_pw_id);
 }
 
 
@@ -1839,7 +1871,7 @@
 	if (dev) {
 		if (dev->country[0] == 0 && msg.listen_channel)
 			os_memcpy(dev->country, msg.listen_channel, 3);
-		os_get_time(&dev->last_seen);
+		os_get_reltime(&dev->last_seen);
 		p2p_parse_free(&msg);
 		return; /* already known */
 	}
@@ -1850,7 +1882,7 @@
 		return;
 	}
 
-	os_get_time(&dev->last_seen);
+	os_get_reltime(&dev->last_seen);
 	dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
 
 	if (msg.listen_channel) {
@@ -1884,7 +1916,7 @@
 
 	dev = p2p_get_device(p2p, addr);
 	if (dev) {
-		os_get_time(&dev->last_seen);
+		os_get_reltime(&dev->last_seen);
 		return dev; /* already known */
 	}
 
@@ -1970,6 +2002,9 @@
 		extra = wpabuf_len(p2p->wfd_ie_probe_resp);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -1979,13 +2014,21 @@
 		pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
 	}
 
-	p2p_build_wps_ie(p2p, buf, pw_id, 1);
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) {
+		p2p_dbg(p2p, "Failed to build WPS IE for Probe Response");
+		wpabuf_free(buf);
+		return NULL;
+	}
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_probe_resp)
 		wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
+		wpabuf_put_buf(buf,
+			       p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+
 	/* P2P IE */
 	len = p2p_buf_add_ie_hdr(buf);
 	p2p_buf_add_capability(buf, p2p->dev_capab &
@@ -2012,17 +2055,21 @@
 
 	if (!p2p->in_listen || !p2p->drv_in_listen) {
 		/* not in Listen state - ignore Probe Request */
+		p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
+			p2p->in_listen, p2p->drv_in_listen);
 		return P2P_PREQ_NOT_LISTEN;
 	}
 
 	if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
 	    ParseFailed) {
 		/* Ignore invalid Probe Request frames */
+		p2p_dbg(p2p, "Could not parse Probe Request frame - ignore it");
 		return P2P_PREQ_MALFORMED;
 	}
 
 	if (elems.p2p == NULL) {
 		/* not a P2P probe - ignore it */
+		p2p_dbg(p2p, "Not a P2P probe - ignore it");
 		return P2P_PREQ_NOT_P2P;
 	}
 
@@ -2030,11 +2077,15 @@
 	    os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
 		/* Not sent to the broadcast address or our P2P Device Address
 		 */
+		p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it",
+			MAC2STR(dst));
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (bssid && !is_broadcast_ether_addr(bssid)) {
 		/* Not sent to the Wildcard BSSID */
+		p2p_dbg(p2p, "Probe Req BSSID " MACSTR " not wildcard - ignore it",
+			MAC2STR(bssid));
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
@@ -2042,23 +2093,28 @@
 	    os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
 	    0) {
 		/* not using P2P Wildcard SSID - ignore */
+		p2p_dbg(p2p, "Probe Req not using P2P Wildcard SSID - ignore it");
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (supp_rates_11b_only(&elems)) {
 		/* Indicates support for 11b rates only */
+		p2p_dbg(p2p, "Probe Req with 11b rates only supported - ignore it");
 		return P2P_PREQ_NOT_P2P;
 	}
 
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
 		/* Could not parse P2P attributes */
+		p2p_dbg(p2p, "Could not parse P2P attributes in Probe Req - ignore it");
 		return P2P_PREQ_NOT_P2P;
 	}
 
 	if (msg.device_id &&
 	    os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
 		/* Device ID did not match */
+		p2p_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it",
+			MAC2STR(msg.device_id));
 		p2p_parse_free(&msg);
 		return P2P_PREQ_NOT_PROCESSED;
 	}
@@ -2067,6 +2123,7 @@
 	if (msg.wps_attributes &&
 	    !p2p_match_dev_type(p2p, msg.wps_attributes)) {
 		/* No match with Requested Device Type */
+		p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it");
 		p2p_parse_free(&msg);
 		return P2P_PREQ_NOT_PROCESSED;
 	}
@@ -2074,6 +2131,7 @@
 
 	if (!p2p->cfg->send_probe_resp) {
 		/* Response generated elsewhere */
+		p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response");
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
@@ -2164,10 +2222,12 @@
 
 	if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
 	    p2p->invite_peer &&
+	    (p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) &&
 	    os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
 	    == 0) {
 		/* Received a Probe Request from Invite peer */
 		p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout");
+		eloop_cancel_timeout(p2p_invite_start, p2p, NULL);
 		eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
 		return P2P_PREQ_PROCESSED;
 	}
@@ -2244,6 +2304,9 @@
 		extra = wpabuf_len(p2p->wfd_ie_assoc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
+
 	/*
 	 * (Re)Association Request - P2P IE
 	 * P2P Capability attribute (shall be present)
@@ -2259,6 +2322,10 @@
 		wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
+		wpabuf_put_buf(tmp,
+			       p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
+
 	peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
 
 	lpos = p2p_buf_add_ie_hdr(tmp);
@@ -2380,7 +2447,8 @@
 {
 	struct p2p_data *p2p;
 
-	if (cfg->max_peers < 1)
+	if (cfg->max_peers < 1 ||
+	    cfg->passphrase_len < 8 || cfg->passphrase_len > 63)
 		return NULL;
 
 	p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
@@ -2409,16 +2477,7 @@
 			p2p->cfg->num_pref_chan = 0;
 	}
 
-#ifdef ANDROID_P2P
-	/* 100ms listen time is too less to receive the response frames in some scenarios
-	 * increasing min listen time to 200ms.
-	 */
-	p2p->min_disc_int = 2;
-	/* SD_FAIR_POLICY: Initializing the SD current serviced pointer to NULL */
-	p2p->sd_dev_list = NULL;
-#else
 	p2p->min_disc_int = 1;
-#endif
 	p2p->max_disc_int = 3;
 	p2p->max_disc_tu = -1;
 
@@ -2438,6 +2497,11 @@
 
 	p2p->go_timeout = 100;
 	p2p->client_timeout = 20;
+	p2p->num_p2p_sd_queries = 0;
+
+	p2p_dbg(p2p, "initialized");
+	p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
+	p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
 
 	return p2p;
 }
@@ -2475,6 +2539,7 @@
 	wpabuf_free(p2p->sd_resp);
 	os_free(p2p->after_scan_tx);
 	p2p_remove_wps_vendor_extensions(p2p);
+	os_free(p2p->no_go_freq.range);
 	os_free(p2p);
 }
 
@@ -2488,10 +2553,6 @@
 		dl_list_del(&dev->list);
 		p2p_device_free(p2p, dev);
 	}
-#ifdef ANDROID_P2P
-	/* SD_FAIR_POLICY: Initializing the SD current serviced pointer to NULL */
-	p2p->sd_dev_list = NULL;
-#endif
 	p2p_free_sd_queries(p2p);
 	os_free(p2p->after_scan_tx);
 	p2p->after_scan_tx = NULL;
@@ -2512,6 +2573,7 @@
 		p2p->go_neg_peer = NULL;
 
 	dev->wps_method = WPS_NOT_READY;
+	dev->oob_pw_id = 0;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 
@@ -2669,44 +2731,18 @@
 void p2p_continue_find(struct p2p_data *p2p)
 {
 	struct p2p_device *dev;
-#ifdef ANDROID_P2P
-	int skip=1;
-#endif
 	p2p_set_state(p2p, P2P_SEARCH);
 	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
-#ifdef ANDROID_P2P
-		/* SD_FAIR_POLICY: We need to give chance to all devices in the device list
-		 * There may be a scenario, where a particular peer device have
-		 * not registered any query response. When we send a SD request to such device,
-		 * no response will be received. And if we continue to get probe responses from that device, 
-		 * and if that device happens to be on top in our device list, 
-		 * we will always continue to send SD requests always to that peer only. 
-		 * We will not be able to send SD requests to other devices in that case. 
-		 * This implementation keeps track of last serviced peer device. 
-		 * And then takes the next one from the device list, in the next iteration.
-		 */
-		if (p2p->sd_dev_list && p2p->sd_dev_list != &p2p->devices) {
-			if(skip) {
-				if ((&dev->list == p2p->sd_dev_list) ) {
-					skip = 0;
-					if (dev->list.next == &p2p->devices)
-						p2p->sd_dev_list = NULL;
-				}
-				continue;
-			}
+		if (dev->sd_pending_bcast_queries == 0) {
+			/* Initialize with total number of registered broadcast
+			 * SD queries. */
+			dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries;
 		}
-		p2p->sd_dev_list = &dev->list;
-		wpa_printf(MSG_DEBUG, "P2P: ### Servicing %p dev->flags 0x%x SD schedule %s devaddr " MACSTR,
-			p2p->sd_dev_list, dev->flags, dev->flags & P2P_DEV_SD_SCHEDULE ? "TRUE": "FALSE",
-			MAC2STR(dev->info.p2p_device_addr));
-#endif
-		if (dev->flags & P2P_DEV_SD_SCHEDULE) {
-			if (p2p_start_sd(p2p, dev) == 0)
-				return;
-			else
-				break;
-		} else if (dev->req_config_methods &&
-			   !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+
+		if (p2p_start_sd(p2p, dev) == 0)
+			return;
+		if (dev->req_config_methods &&
+		    !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
 			p2p_dbg(p2p, "Send pending Provision Discovery Request to "
 				MACSTR " (config methods 0x%x)",
 				MAC2STR(dev->info.p2p_device_addr),
@@ -2727,10 +2763,7 @@
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
 	if (!success) {
-		if (p2p->sd_peer) {
-			p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
-			p2p->sd_peer = NULL;
-		}
+		p2p->sd_peer = NULL;
 		p2p_continue_find(p2p);
 		return;
 	}
@@ -2775,7 +2808,8 @@
 			MAC2STR(dev->info.p2p_device_addr),
 			dev->req_config_methods);
 		p2p_send_prov_disc_req(p2p, dev,
-				       dev->flags & P2P_DEV_PD_FOR_JOIN, 0);
+				       dev->flags & P2P_DEV_PD_FOR_JOIN,
+				       p2p->pd_force_freq);
 		return;
 	}
 }
@@ -2809,11 +2843,7 @@
 			p2p_continue_find(p2p);
 		else if (p2p->user_initiated_pd) {
 			p2p->pending_action_state = P2P_PENDING_PD;
-#ifdef ANDROID_P2P
-			p2p_set_timeout(p2p, 0, 350000);
-#else
 			p2p_set_timeout(p2p, 0, 300000);
-#endif
 		}
 		return;
 	}
@@ -2830,19 +2860,15 @@
 	/* Wait for response from the peer */
 	if (p2p->state == P2P_SEARCH)
 		p2p_set_state(p2p, P2P_PD_DURING_FIND);
-#ifdef ANDROID_P2P
-	p2p_set_timeout(p2p, 0, 350000);
-#else
 	p2p_set_timeout(p2p, 0, 200000);
-#endif
 }
 
 
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-			 struct os_time *rx_time, int level, const u8 *ies,
+			 struct os_reltime *rx_time, int level, const u8 *ies,
 			 size_t ies_len)
 {
-	if (os_time_before(rx_time, &p2p->find_start)) {
+	if (os_reltime_before(rx_time, &p2p->find_start)) {
 		/*
 		 * The driver may have cached (e.g., in cfg80211 BSS table) the
 		 * scan results for relatively long time. To avoid reporting
@@ -2887,6 +2913,10 @@
 		wpabuf_put_buf(ies, p2p->wfd_ie_probe_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
+		wpabuf_put_buf(ies,
+			       p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
+
 	len = p2p_buf_add_ie_hdr(ies);
 	p2p_buf_add_capability(ies, p2p->dev_capab &
 			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
@@ -2913,6 +2943,10 @@
 		len += wpabuf_len(p2p->wfd_ie_probe_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p && p2p->vendor_elem &&
+	    p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
+		len += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
+
 	return len;
 }
 
@@ -3001,8 +3035,17 @@
 		struct p2p_device *dev;
 		dev = p2p_get_device(p2p, addr);
 		if (dev &&
-		    dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+		    dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
 			dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+			if ((p2p->state == P2P_SEARCH) ||
+			    (p2p->state == P2P_LISTEN_ONLY)) {
+				/* Clear our search state or Listen state since
+				 * now peer is awaiting response from our side.
+				 */
+				p2p_dbg(p2p, "Clear the P2P discovery state");
+				p2p_stop_find(p2p);
+			}
+		}
 	}
 }
 
@@ -3013,13 +3056,44 @@
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result);
-	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 	if (result == P2P_SEND_ACTION_FAILED) {
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
 		return;
 	}
+
+	dev = p2p->go_neg_peer;
+
 	if (result == P2P_SEND_ACTION_NO_ACK) {
 		/*
+		 * Retry GO Negotiation Confirmation
+		 * P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive
+		 * ACK for confirmation.
+		 */
+		if (dev && dev->go_neg_conf &&
+		    dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) {
+			p2p_dbg(p2p, "GO Negotiation Confirm retry %d",
+				dev->go_neg_conf_sent);
+			p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+			if (p2p_send_action(p2p, dev->go_neg_conf_freq,
+					    dev->info.p2p_device_addr,
+					    p2p->cfg->dev_addr,
+					    dev->info.p2p_device_addr,
+					    wpabuf_head(dev->go_neg_conf),
+					    wpabuf_len(dev->go_neg_conf), 0) >=
+			    0) {
+				dev->go_neg_conf_sent++;
+				return;
+			}
+			p2p_dbg(p2p, "Failed to re-send Action frame");
+
+			/*
+			 * Continue with the assumption that the first attempt
+			 * went through and just the ACK frame was lost.
+			 */
+		}
+
+		/*
 		 * It looks like the TX status for GO Negotiation Confirm is
 		 * often showing failure even when the peer has actually
 		 * received the frame. Since the peer may change channels
@@ -3032,7 +3106,8 @@
 		p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported");
 	}
 
-	dev = p2p->go_neg_peer;
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
 	if (dev == NULL)
 		return;
 
@@ -3056,6 +3131,10 @@
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 	switch (state) {
 	case P2P_NO_PENDING_ACTION:
+		if (p2p->send_action_in_progress) {
+			p2p->send_action_in_progress = 0;
+			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		}
 		if (p2p->after_scan_tx_in_progress) {
 			p2p->after_scan_tx_in_progress = 0;
 			if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
@@ -3211,7 +3290,12 @@
 		p2p_connect_send(p2p, p2p->go_neg_peer);
 		return;
 	}
-
+	if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
+		p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
+		p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+		p2p_set_timeout(p2p, 0, 30000);
+		return;
+	}
 	p2p_set_state(p2p, P2P_CONNECT_LISTEN);
 	p2p_listen_in_find(p2p, 0);
 }
@@ -3240,27 +3324,28 @@
 
 static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
 {
-	/*
-	 * TODO: could remain constantly in Listen state for some time if there
-	 * are no other concurrent uses for the radio. For now, go to listen
-	 * state once per second to give other uses a chance to use the radio.
-	 */
 	p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
-	p2p_set_timeout(p2p, 0, 500000);
+
+	if (p2p->cfg->is_concurrent_session_active &&
+	    p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx))
+		p2p_set_timeout(p2p, 0, 500000);
+	else
+		p2p_set_timeout(p2p, 0, 200000);
 }
 
 
 static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
 {
 	struct p2p_device *dev = p2p->go_neg_peer;
+	struct os_reltime now;
 
 	if (dev == NULL) {
 		p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait");
 		return;
 	}
 
-	dev->wait_count++;
-	if (dev->wait_count >= 120) {
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &dev->go_neg_wait_started, 120)) {
 		p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation");
 		p2p_go_neg_failed(p2p, dev, -1);
 		return;
@@ -3277,7 +3362,6 @@
 	p2p_dbg(p2p, "Service Discovery Query timeout");
 	if (p2p->sd_peer) {
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
-		p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
 		p2p->sd_peer = NULL;
 	}
 	p2p_continue_find(p2p);
@@ -3355,14 +3439,15 @@
 	if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
 		p2p_set_state(p2p, P2P_INVITE);
 		p2p_invite_send(p2p, p2p->invite_peer,
-				p2p->invite_go_dev_addr);
+				p2p->invite_go_dev_addr, p2p->invite_dev_pw_id);
 	} else {
 		if (p2p->invite_peer) {
 			p2p_dbg(p2p, "Invitation Request retry limit reached");
 			if (p2p->cfg->invitation_result)
 				p2p->cfg->invitation_result(
 					p2p->cfg->cb_ctx, -1, NULL, NULL,
-					p2p->invite_peer->info.p2p_device_addr);
+					p2p->invite_peer->info.p2p_device_addr,
+					0, 0);
 		}
 		p2p_set_state(p2p, P2P_IDLE);
 	}
@@ -3437,10 +3522,6 @@
 	case P2P_INVITE_LISTEN:
 		p2p_timeout_invite_listen(p2p);
 		break;
-	case P2P_SEARCH_WHEN_READY:
-		break;
-	case P2P_CONTINUE_SEARCH_WHEN_READY:
-		break;
 	}
 }
 
@@ -3473,6 +3554,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	}
 
 	return "??";
@@ -3523,7 +3606,7 @@
 	struct p2p_device *dev;
 	int res;
 	char *pos, *end;
-	struct os_time now;
+	struct os_reltime now;
 
 	if (info == NULL)
 		return -1;
@@ -3534,7 +3617,7 @@
 	pos = buf;
 	end = buf + buflen;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	res = os_snprintf(pos, end - pos,
 			  "age=%d\n"
 			  "listen_freq=%d\n"
@@ -3549,9 +3632,8 @@
 			  "country=%c%c\n"
 			  "oper_freq=%d\n"
 			  "req_config_methods=0x%x\n"
-			  "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+			  "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
 			  "status=%d\n"
-			  "wait_count=%u\n"
 			  "invitation_reqs=%u\n",
 			  (int) (now.sec - dev->last_seen.sec),
 			  dev->listen_freq,
@@ -3572,9 +3654,6 @@
 			  dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
 			  dev->flags & P2P_DEV_NOT_YET_READY ?
 			  "[NOT_YET_READY]" : "",
-			  dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "",
-			  dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" :
-			  "",
 			  dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
 			  "[PD_PEER_DISPLAY]" : "",
 			  dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
@@ -3596,7 +3675,6 @@
 			  dev->flags & P2P_DEV_PD_FOR_JOIN ?
 			  "[PD_FOR_JOIN]" : "",
 			  dev->status,
-			  dev->wait_count,
 			  dev->invitation_reqs);
 	if (res < 0 || res >= end - pos)
 		return pos - buf;
@@ -3838,6 +3916,11 @@
 		return;
 	}
 
+	if (p2p->cfg->presence_resp) {
+		p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status,
+					msg.noa, msg.noa_len);
+	}
+
 	if (*msg.status) {
 		p2p_dbg(p2p, "P2P Presence Request was rejected: status %u",
 			*msg.status);
@@ -3864,6 +3947,15 @@
 				       p2p_ext_listen_timeout, p2p, NULL);
 	}
 
+	if ((p2p->cfg->is_p2p_in_progress &&
+	     p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) ||
+	    (p2p->pending_action_state == P2P_PENDING_PD &&
+	     p2p->pd_retries > 0)) {
+		p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)",
+			p2p_state_txt(p2p->state));
+		return;
+	}
+
 	if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
 		/*
 		 * This should not really happen, but it looks like the Listen
@@ -3935,8 +4027,10 @@
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ie, ie_len, &msg))
 		return;
-	if (msg.minor_reason_code == NULL)
+	if (msg.minor_reason_code == NULL) {
+		p2p_parse_free(&msg);
 		return;
+	}
 
 	p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR
 		" reason_code=%u minor_reason_code=%u",
@@ -3957,8 +4051,10 @@
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ie, ie_len, &msg))
 		return;
-	if (msg.minor_reason_code == NULL)
+	if (msg.minor_reason_code == NULL) {
+		p2p_parse_free(&msg);
 		return;
+	}
 
 	p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR
 		" reason_code=%u minor_reason_code=%u",
@@ -3980,20 +4076,43 @@
 }
 
 
-int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
+			   u8 forced)
 {
 	if (p2p_channel_to_freq(reg_class, channel) < 0)
 		return -1;
 
 	p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
 		reg_class, channel);
-	p2p->cfg->reg_class = reg_class;
-	p2p->cfg->channel = channel;
+
+	/*
+	 * Listen channel was set in configuration or set by control interface;
+	 * cannot override it.
+	 */
+	if (p2p->cfg->channel_forced && forced == 0)
+		return -1;
+
+	if (p2p->state == P2P_IDLE) {
+		p2p->cfg->reg_class = reg_class;
+		p2p->cfg->channel = channel;
+		p2p->cfg->channel_forced = forced;
+	} else {
+		p2p_dbg(p2p, "Defer setting listen channel");
+		p2p->pending_reg_class = reg_class;
+		p2p->pending_channel = channel;
+		p2p->pending_channel_forced = forced;
+	}
 
 	return 0;
 }
 
 
+u8 p2p_get_listen_channel(struct p2p_data *p2p)
+{
+	return p2p->cfg->channel;
+}
+
+
 int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
 {
 	p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len));
@@ -4046,6 +4165,31 @@
 }
 
 
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+		       const struct wpa_freq_range_list *list)
+{
+	struct wpa_freq_range *tmp;
+
+	if (list == NULL || list->num == 0) {
+		os_free(p2p->no_go_freq.range);
+		p2p->no_go_freq.range = NULL;
+		p2p->no_go_freq.num = 0;
+		return 0;
+	}
+
+	tmp = os_calloc(list->num, sizeof(struct wpa_freq_range));
+	if (tmp == NULL)
+		return -1;
+	os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range));
+	os_free(p2p->no_go_freq.range);
+	p2p->no_go_freq.range = tmp;
+	p2p->no_go_freq.num = list->num;
+	p2p_dbg(p2p, "Updated no GO chan list");
+
+	return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
 			   u8 *iface_addr)
 {
@@ -4108,10 +4252,16 @@
 }
 
 
-void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+void p2p_update_channel_list(struct p2p_data *p2p,
+			     const struct p2p_channels *chan,
+			     const struct p2p_channels *cli_chan)
 {
 	p2p_dbg(p2p, "Update channel list");
 	os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
+	p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
+	os_memcpy(&p2p->cfg->cli_channels, cli_chan,
+		  sizeof(struct p2p_channels));
+	p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
 }
 
 
@@ -4190,7 +4340,7 @@
 				dev = dl_list_first(&dev->list,
 						    struct p2p_device,
 						    list);
-				if (&dev->list == &p2p->devices)
+				if (!dev || &dev->list == &p2p->devices)
 					return NULL;
 			} while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
 		}
@@ -4202,7 +4352,7 @@
 			dev = dl_list_first(&dev->list,
 					    struct p2p_device,
 					    list);
-			if (&dev->list == &p2p->devices)
+			if (!dev || &dev->list == &p2p->devices)
 				return NULL;
 		}
 	}
@@ -4210,22 +4360,12 @@
 	return &dev->info;
 }
 
-#ifdef ANDROID_P2P
-int p2p_search_in_progress(struct p2p_data *p2p)
-{
-	if (p2p == NULL)
-		return 0;
-
-	return p2p->state == P2P_SEARCH;
-}
-#endif
 
 int p2p_in_progress(struct p2p_data *p2p)
 {
 	if (p2p == NULL)
 		return 0;
-	if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY ||
-	    p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY)
+	if (p2p->state == P2P_SEARCH)
 		return 2;
 	return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
 }
@@ -4241,13 +4381,6 @@
 }
 
 
-void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay)
-{
-	if (p2p && p2p->search_delay < delay)
-		p2p->search_delay = delay;
-}
-
-
 #ifdef CONFIG_WIFI_DISPLAY
 
 static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
@@ -4257,7 +4390,7 @@
 
 	for (g = 0; g < p2p->num_groups; g++) {
 		group = p2p->groups[g];
-		p2p_group_update_ies(group);
+		p2p_group_force_beacon_update_ies(group);
 	}
 }
 
@@ -4435,3 +4568,259 @@
 	va_end(ap);
 	p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
 }
+
+
+void p2p_loop_on_known_peers(struct p2p_data *p2p,
+			     void (*peer_callback)(struct p2p_peer_info *peer,
+						   void *user_data),
+			     void *user_data)
+{
+	struct p2p_device *dev, *n;
+
+	dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
+		peer_callback(&dev->info, user_data);
+	}
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
+					      int client_freq,
+					      const u8 *go_dev_addr,
+					      const u8 *ssid, size_t ssid_len)
+{
+	struct wpabuf *buf;
+	u8 op_class, channel;
+	enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	op_class = p2p->cfg->reg_class;
+	channel = p2p->cfg->channel;
+
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+
+	if (p2p->num_groups > 0) {
+		int freq = p2p_group_get_freq(p2p->groups[0]);
+		role = P2P_GO_IN_A_GROUP;
+		if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) {
+			p2p_dbg(p2p,
+				"Unknown GO operating frequency %d MHz for NFC handover",
+				freq);
+			wpabuf_free(buf);
+			return NULL;
+		}
+	} else if (client_freq > 0) {
+		role = P2P_CLIENT_IN_A_GROUP;
+		if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) {
+			p2p_dbg(p2p,
+				"Unknown client operating frequency %d MHz for NFC handover",
+				client_freq);
+			wpabuf_free(buf);
+			return NULL;
+		}
+	}
+
+	p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
+				       channel, role);
+
+	if (p2p->num_groups > 0) {
+		/* Limit number of clients to avoid very long message */
+		p2p_buf_add_group_info(p2p->groups[0], buf, 5);
+		p2p_group_buf_add_id(p2p->groups[0], buf);
+	} else if (client_freq > 0 &&
+		   go_dev_addr && !is_zero_ether_addr(go_dev_addr) &&
+		   ssid && ssid_len > 0) {
+		/*
+		 * Add the optional P2P Group ID to indicate in which group this
+		 * device is a P2P Client.
+		 */
+		p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len);
+	}
+
+	return buf;
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	const u8 *p2p_dev_addr;
+	int freq;
+	enum p2p_role_indication role;
+
+	params->next_step = NO_ACTION;
+
+	if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+				   params->p2p_attr, params->p2p_len, &msg)) {
+		p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.p2p_device_addr)
+		p2p_dev_addr = msg.p2p_device_addr;
+	else if (msg.device_id)
+		p2p_dev_addr = msg.device_id;
+	else {
+		p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.oob_dev_password) {
+		os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+			  msg.oob_dev_password_len);
+		params->oob_dev_pw_len = msg.oob_dev_password_len;
+	}
+
+	dev = p2p_create_device(p2p, p2p_dev_addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	params->peer = &dev->info;
+
+	os_get_reltime(&dev->last_seen);
+	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+	p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+	if (!msg.oob_go_neg_channel) {
+		p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+		return -1;
+	}
+
+	if (msg.oob_go_neg_channel[3] == 0 &&
+	    msg.oob_go_neg_channel[4] == 0)
+		freq = 0;
+	else
+		freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+					   msg.oob_go_neg_channel[4]);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+		return -1;
+	}
+	role = msg.oob_go_neg_channel[5];
+
+	if (role == P2P_GO_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
+		params->go_freq = freq;
+	} else if (role == P2P_CLIENT_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz",
+			freq);
+		params->go_freq = freq;
+	} else
+		p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+	dev->oob_go_neg_freq = freq;
+
+	if (!params->sel && role != P2P_GO_IN_A_GROUP) {
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+		if (freq < 0) {
+			p2p_dbg(p2p, "Own listen channel not known");
+			return -1;
+		}
+		p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
+		dev->oob_go_neg_freq = freq;
+	}
+
+	if (msg.group_id) {
+		os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
+		params->go_ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
+			  params->go_ssid_len);
+	}
+
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		p2p_dbg(p2p, "Do not report rejected device");
+		p2p_parse_free(&msg);
+		return 0;
+	}
+
+	if (!(dev->flags & P2P_DEV_REPORTED)) {
+		p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+				    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+		dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+	}
+	p2p_parse_free(&msg);
+
+	if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
+		params->next_step = BOTH_GO;
+	else if (role == P2P_GO_IN_A_GROUP)
+		params->next_step = JOIN_GROUP;
+	else if (role == P2P_CLIENT_IN_A_GROUP) {
+		dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+		params->next_step = PEER_CLIENT;
+	} else if (p2p->num_groups > 0)
+		params->next_step = AUTH_JOIN;
+	else if (params->sel)
+		params->next_step = INIT_GO_NEG;
+	else
+		params->next_step = RESP_GO_NEG;
+
+	return 0;
+}
+
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr)
+{
+
+	p2p->authorized_oob_dev_pw_id = dev_pw_id;
+	if (dev_pw_id == 0) {
+		p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
+		return;
+	}
+
+	p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
+		dev_pw_id);
+
+	p2p->go_intent = go_intent;
+	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len)
+{
+	if (len < 8 || len > 63)
+		return -1;
+	p2p->cfg->passphrase_len = len;
+	return 0;
+}
+
+
+void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem)
+{
+	p2p->vendor_elem = vendor_elem;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 7f845b2..dee79df 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -9,6 +9,8 @@
 #ifndef P2P_H
 #define P2P_H
 
+#include "wps/wps_defs.h"
+
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
  */
@@ -50,7 +52,7 @@
 };
 
 enum p2p_wps_method {
-	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
 };
 
 /**
@@ -77,6 +79,8 @@
 
 	int ht40;
 
+	int vht;
+
 	/**
 	 * ssid - SSID of the group
 	 */
@@ -226,6 +230,14 @@
 	 * wfd_subelems - Wi-Fi Display subelements from WFD IE(s)
 	 */
 	struct wpabuf *wfd_subelems;
+
+	/**
+	 * vendor_elems - Unrecognized vendor elements
+	 *
+	 * This buffer includes any other vendor element than P2P, WPS, and WFD
+	 * IE(s) from the frame that was used to discover the peer.
+	 */
+	struct wpabuf *vendor_elems;
 };
 
 enum p2p_prov_disc_status {
@@ -263,6 +275,12 @@
 	u8 channel;
 
 	/**
+	 * channel_forced - the listen channel was forced by configuration
+	 *                  or by control interface and cannot be overridden
+	 */
+	u8 channel_forced;
+
+	/**
 	 * Regulatory class for own operational channel
 	 */
 	u8 op_reg_class;
@@ -287,6 +305,20 @@
 	struct p2p_channels channels;
 
 	/**
+	 * cli_channels - Additional client channels
+	 *
+	 * This list of channels (if any) will be used when advertising local
+	 * channels during GO Negotiation or Invitation for the cases where the
+	 * local end may become the client. This may allow the peer to become a
+	 * GO on additional channels if it supports these options. The main use
+	 * case for this is to include passive-scan channels on devices that may
+	 * not know their current location and have configured most channels to
+	 * not allow initiation of radition (i.e., another device needs to take
+	 * master responsibilities).
+	 */
+	struct p2p_channels cli_channels;
+
+	/**
 	 * num_pref_chan - Number of pref_chan entries
 	 */
 	unsigned int num_pref_chan;
@@ -371,6 +403,14 @@
 	unsigned int max_listen;
 
 	/**
+	 * passphrase_len - Passphrase length (8..63)
+	 *
+	 * This parameter controls the length of the random passphrase that is
+	 * generated at the GO.
+	 */
+	unsigned int passphrase_len;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -690,6 +730,8 @@
 	 *	persistent group (instead of invitation to join an active
 	 *	group)
 	 * @channels: Available operating channels for the group
+	 * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
+	 *	used
 	 * Returns: Status code (P2P_SC_*)
 	 *
 	 * This optional callback can be used to implement persistent reconnect
@@ -711,7 +753,8 @@
 				 const u8 *go_dev_addr, const u8 *ssid,
 				 size_t ssid_len, int *go, u8 *group_bssid,
 				 int *force_freq, int persistent_group,
-				 const struct p2p_channels *channels);
+				 const struct p2p_channels *channels,
+				 int dev_pw_id);
 
 	/**
 	 * invitation_received - Callback on Invitation Request RX
@@ -742,6 +785,9 @@
 	 * @bssid: P2P Group BSSID or %NULL if not received
 	 * @channels: Available operating channels for the group
 	 * @addr: Peer address
+	 * @freq: Frequency (in MHz) indicated during invitation or 0
+	 * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer
+	 * during invitation or 0
 	 *
 	 * This callback is used to indicate result of an Invitation procedure
 	 * started with a call to p2p_invite(). The indicated status code is
@@ -751,7 +797,7 @@
 	 */
 	void (*invitation_result)(void *ctx, int status, const u8 *bssid,
 				  const struct p2p_channels *channels,
-				  const u8 *addr);
+				  const u8 *addr, int freq, int peer_oper_freq);
 
 	/**
 	 * go_connected - Check whether we are connected to a GO
@@ -761,6 +807,34 @@
 	 * or 0 if not.
 	 */
 	int (*go_connected)(void *ctx, const u8 *dev_addr);
+
+	/**
+	 * presence_resp - Callback on Presence Response
+	 * @ctx: Callback context from cb_ctx
+	 * @src: Source address (GO's P2P Interface Address)
+	 * @status: Result of the request (P2P_SC_*)
+	 * @noa: Returned NoA value
+	 * @noa_len: Length of the NoA buffer in octets
+	 */
+	void (*presence_resp)(void *ctx, const u8 *src, u8 status,
+			      const u8 *noa, size_t noa_len);
+
+	/**
+	 * is_concurrent_session_active - Check whether concurrent session is
+	 * active on other virtual interfaces
+	 * @ctx: Callback context from cb_ctx
+	 * Returns: 1 if concurrent session is active on other virtual interface
+	 * or 0 if not.
+	 */
+	int (*is_concurrent_session_active)(void *ctx);
+
+	/**
+	 * is_p2p_in_progress - Check whether P2P operation is in progress
+	 * @ctx: Callback context from cb_ctx
+	 * Returns: 1 if P2P operation (e.g., group formation) is in progress
+	 * or 0 if not.
+	 */
+	int (*is_p2p_in_progress)(void *ctx);
 };
 
 
@@ -935,7 +1009,7 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq);
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -963,7 +1037,7 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq);
+		  unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -1065,12 +1139,14 @@
  * @persistent_group: Whether this is to reinvoke a persistent group
  * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
  *	force_freq == 0)
+ * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
+ *	case or -1 if not used
  * Returns: 0 on success, -1 on failure
  */
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq);
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id);
 
 /**
  * p2p_presence_req - Request GO presence
@@ -1228,7 +1304,7 @@
  * start of a pending operation, e.g., to start a pending GO negotiation.
  */
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-			 struct os_time *rx_time, int level, const u8 *ies,
+			 struct os_reltime *rx_time, int level, const u8 *ies,
 			 size_t ies_len);
 
 /**
@@ -1335,6 +1411,11 @@
 	size_t ssid_len;
 
 	/**
+	 * freq - Operating channel of the group
+	 */
+	int freq;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -1610,7 +1691,10 @@
  */
 void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
 
-int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel);
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
+			   u8 forced);
+
+u8 p2p_get_listen_channel(struct p2p_data *p2p);
 
 int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
 
@@ -1649,6 +1733,22 @@
 int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
 
 /**
+ * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq);
+
+/**
+ * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq);
+
+/**
  * p2p_get_pref_freq - Get channel from preferred channel list
  * @p2p: P2P module context from p2p_init()
  * @channels: List of channels
@@ -1657,7 +1757,9 @@
 unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
 			       const struct p2p_channels *channels);
 
-void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
+void p2p_update_channel_list(struct p2p_data *p2p,
+			     const struct p2p_channels *chan,
+			     const struct p2p_channels *cli_chan);
 
 /**
  * p2p_set_best_channels - Update best channel information
@@ -1694,7 +1796,7 @@
  * @group: P2P group context from p2p_group_init()
  * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
  *	on the first call and not modified later
- * Returns: A P2P Interface Address for each call and %NULL for no more members
+ * Returns: A P2P Device Address for each call and %NULL for no more members
  */
 const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
 
@@ -1716,6 +1818,26 @@
 int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr);
 
 /**
+ * p2p_group_get_config - Get the group configuration
+ * @group: P2P group context from p2p_group_init()
+ * Returns: The group configuration pointer
+ */
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group);
+
+/**
+ * p2p_loop_on_all_groups - Run the given callback on all groups
+ * @p2p: P2P module context from p2p_init()
+ * @group_callback: The callback function pointer
+ * @user_data: Some user data pointer which can be %NULL
+ *
+ * The group_callback function can stop the iteration by returning 0.
+ */
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+			    int (*group_callback)(struct p2p_group *group,
+						  void *user_data),
+			    void *user_data);
+
+/**
  * p2p_get_peer_found - Get P2P peer info structure of a found peer
  * @p2p: P2P module context from p2p_init()
  * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
@@ -1765,35 +1887,21 @@
 		      const struct p2p_channel *pref_chan);
 
 /**
+ * p2p_set_no_go_freq - Set no GO channel ranges
+ * @p2p: P2P module context from p2p_init()
+ * @list: Channel ranges or %NULL to remove restriction
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+		       const struct wpa_freq_range_list *list);
+
+/**
  * p2p_in_progress - Check whether a P2P operation is progress
  * @p2p: P2P module context from p2p_init()
  * Returns: 0 if P2P module is idle or 1 if an operation is in progress
  */
 int p2p_in_progress(struct p2p_data *p2p);
 
-#ifdef ANDROID_P2P
-/**
- * p2p_search_in_progress - Check whether a P2P SEARCH is in progress
- * @p2p: P2P module context from p2p_init()
- * Returns: 0 if P2P module is idle or 1 if an operation is in progress
- */
-int p2p_search_in_progress(struct p2p_data *p2p);
-
-/**
- * p2p_search_pending - Check whether there is a deferred P2P SEARCH
- * @p2p: P2P module context from p2p_init()
- * Returns: 0 if there is no deferred P2P search or 1 if there is one
- */
-int p2p_search_pending(struct p2p_data *p2p);
-#endif
-
-/**
- * p2p_other_scan_completed - Notify completion of non-P2P scan
- * @p2p: P2P module context from p2p_init()
- * Returns: 0 if P2P module is idle or 1 if an operation was started
- */
-int p2p_other_scan_completed(struct p2p_data *p2p);
-
 const char * p2p_wps_method_text(enum p2p_wps_method method);
 
 /**
@@ -1805,8 +1913,6 @@
 void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
 			    u8 client_timeout);
 
-void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay);
-
 int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie);
@@ -1853,4 +1959,50 @@
  */
 const char * p2p_get_state_txt(struct p2p_data *p2p);
 
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+
+struct p2p_nfc_params {
+	int sel;
+	const u8 *wsc_attr;
+	size_t wsc_len;
+	const u8 *p2p_attr;
+	size_t p2p_len;
+
+	enum {
+		NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG,
+		BOTH_GO, PEER_CLIENT
+	} next_step;
+	struct p2p_peer_info *peer;
+	u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		      WPS_OOB_DEVICE_PASSWORD_LEN];
+	size_t oob_dev_pw_len;
+	int go_freq;
+	u8 go_dev_addr[ETH_ALEN];
+	u8 go_ssid[32];
+	size_t go_ssid_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params);
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr);
+
+int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len);
+
+void p2p_loop_on_known_peers(struct p2p_data *p2p,
+			     void (*peer_callback)(struct p2p_peer_info *peer,
+						   void *user_data),
+			     void *user_data);
+
+void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 5838d35..e9b683d 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -17,8 +17,7 @@
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
 {
 	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 
 	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
 	wpabuf_put_u8(buf, dialog_token);
@@ -31,8 +30,7 @@
 {
 	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
 	wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 
 	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
 	wpabuf_put_u8(buf, dialog_token);
@@ -47,8 +45,7 @@
 	/* P2P IE header */
 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
 	len = wpabuf_put(buf, 1); /* IE length to be filled */
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 	wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
 	return len;
 }
@@ -258,6 +255,7 @@
 	wpabuf_put_data(buf, ssid, ssid_len);
 	wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
 		   MAC2STR(dev_addr));
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len);
 }
 
 
@@ -327,13 +325,32 @@
 }
 
 
-static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
-			       const char *val)
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role)
+{
+	/* OOB Group Owner Negotiation Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
+	wpabuf_put_le16(buf, 6);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, oper_class); /* Operating Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpabuf_put_u8(buf, (u8) role); /* Role indication */
+	wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
+		   "Class %u Channel %u Role %d",
+		   oper_class, channel, role);
+}
+
+
+static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+			      const char *val)
 {
 	size_t len;
 
-	wpabuf_put_be16(buf, attr);
 	len = val ? os_strlen(val) : 0;
+	if (wpabuf_tailroom(buf) < 4 + len)
+		return -1;
+	wpabuf_put_be16(buf, attr);
 #ifndef CONFIG_WPS_STRICT
 	if (len == 0) {
 		/*
@@ -341,36 +358,46 @@
 		 * attributes. As a workaround, send a space character if the
 		 * device attribute string is empty.
 		 */
+		if (wpabuf_tailroom(buf) < 3)
+			return -1;
 		wpabuf_put_be16(buf, 1);
 		wpabuf_put_u8(buf, ' ');
-		return;
+		return 0;
 	}
 #endif /* CONFIG_WPS_STRICT */
 	wpabuf_put_be16(buf, len);
 	if (val)
 		wpabuf_put_data(buf, val, len);
+	return 0;
 }
 
 
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
-		      int all_attr)
+int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
+		     int all_attr)
 {
 	u8 *len;
 	int i;
 
+	if (wpabuf_tailroom(buf) < 6)
+		return -1;
 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
 	len = wpabuf_put(buf, 1);
 	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
 
-	wps_build_version(buf);
+	if (wps_build_version(buf) < 0)
+		return -1;
 
 	if (all_attr) {
+		if (wpabuf_tailroom(buf) < 5)
+			return -1;
 		wpabuf_put_be16(buf, ATTR_WPS_STATE);
 		wpabuf_put_be16(buf, 1);
 		wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
 	}
 
 	if (pw_id >= 0) {
+		if (wpabuf_tailroom(buf) < 6)
+			return -1;
 		/* Device Password ID */
 		wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
 		wpabuf_put_be16(buf, 2);
@@ -380,33 +407,47 @@
 	}
 
 	if (all_attr) {
+		if (wpabuf_tailroom(buf) < 5)
+			return -1;
 		wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
 		wpabuf_put_be16(buf, 1);
 		wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
 
-		wps_build_uuid_e(buf, p2p->cfg->uuid);
-		p2p_add_wps_string(buf, ATTR_MANUFACTURER,
-				   p2p->cfg->manufacturer);
-		p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
-		p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
-				   p2p->cfg->model_number);
-		p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
-				   p2p->cfg->serial_number);
+		if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 ||
+		    p2p_add_wps_string(buf, ATTR_MANUFACTURER,
+				       p2p->cfg->manufacturer) < 0 ||
+		    p2p_add_wps_string(buf, ATTR_MODEL_NAME,
+				       p2p->cfg->model_name) < 0 ||
+		    p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
+				       p2p->cfg->model_number) < 0 ||
+		    p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
+				       p2p->cfg->serial_number) < 0)
+			return -1;
 
+		if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN)
+			return -1;
 		wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
 		wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
 		wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
 
-		p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+		if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name)
+		    < 0)
+			return -1;
 
+		if (wpabuf_tailroom(buf) < 6)
+			return -1;
 		wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
 		wpabuf_put_be16(buf, 2);
 		wpabuf_put_be16(buf, p2p->cfg->config_methods);
 	}
 
-	wps_build_wfa_ext(buf, 0, NULL, 0);
+	if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0)
+		return -1;
 
 	if (all_attr && p2p->cfg->num_sec_dev_types) {
+		if (wpabuf_tailroom(buf) <
+		    4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types)
+			return -1;
 		wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
 		wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
 				p2p->cfg->num_sec_dev_types);
@@ -428,4 +469,6 @@
 	}
 
 	p2p_buf_update_ie_hdr(buf, len);
+
+	return 0;
 }
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
index 76d01cf..86bae1a 100644
--- a/src/p2p/p2p_dev_disc.c
+++ b/src/p2p/p2p_dev_disc.c
@@ -68,6 +68,7 @@
 {
 	struct p2p_device *go;
 	struct wpabuf *req;
+	unsigned int wait_time;
 
 	go = p2p_get_device(p2p, dev->member_in_go_dev);
 	if (go == NULL || dev->oper_freq <= 0) {
@@ -88,9 +89,12 @@
 	os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr,
 		  ETH_ALEN);
 	p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
+	wait_time = 1000;
+	if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
+		wait_time = p2p->cfg->max_listen;
 	if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, go->info.p2p_device_addr,
-			    wpabuf_head(req), wpabuf_len(req), 1000) < 0) {
+			    wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 		wpabuf_free(req);
 		/* TODO: how to recover from failure? */
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index bd583be..bd7a2cf 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -103,6 +104,8 @@
 		return DEV_PW_USER_SPECIFIED;
 	case WPS_PBC:
 		return DEV_PW_PUSHBUTTON;
+	case WPS_NFC:
+		return DEV_PW_NFC_CONNECTION_HANDOVER;
 	default:
 		return DEV_PW_DEFAULT;
 	}
@@ -118,6 +121,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	default:
 		return "??";
 	}
@@ -131,12 +136,16 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
 		extra = wpabuf_len(p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -172,13 +181,23 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+	pw_id = p2p_wps_method_pw_id(peer->wps_method);
+	if (peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
+		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
+		wpabuf_free(buf);
+		return NULL;
+	}
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
 		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+
 	return buf;
 }
 
@@ -205,6 +224,8 @@
 	}
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (dev->oob_go_neg_freq > 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send GO Negotiation Request",
@@ -245,6 +266,7 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 	p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -253,6 +275,9 @@
 		extra = wpabuf_len(p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -307,15 +332,22 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	p2p_build_wps_ie(p2p, buf,
-			 p2p_wps_method_pw_id(peer ? peer->wps_method :
-					      WPS_NOT_READY), 0);
+	pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
+	if (peer && peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
+		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
+		wpabuf_free(buf);
+		return NULL;
+	}
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
 		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
 
 	return buf;
 }
@@ -339,6 +371,9 @@
 	int freq;
 	u8 op_reg_class, op_channel;
 	unsigned int i;
+	const int op_classes_5ghz[] = { 124, 115, 0 };
+	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
+	const int op_classes_vht[] = { 128, 0 };
 
 	if (p2p->own_freq_preference > 0 &&
 	    p2p_freq_to_channel(p2p->own_freq_preference,
@@ -403,44 +438,28 @@
 		}
 	}
 
+	/* 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) {
+		p2p_dbg(p2p, "Pick possible VHT 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 HT40 */
-	for (i = 0; i < intersection->reg_classes; i++) {
-		struct p2p_reg_class *c = &intersection->reg_class[i];
-		if (c->reg_class == 116 || c->reg_class == 117 ||
-		    c->reg_class == 126 || c->reg_class == 127) {
-			p2p_dbg(p2p, "Pick possible HT40 channel (reg_class %u channel %u) from intersection",
-				c->reg_class, c->channel[0]);
-			p2p->op_reg_class = c->reg_class;
-			p2p->op_channel = c->channel[0];
-			return;
-		}
+	if (p2p_channel_select(intersection, op_classes_ht40,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
 	}
 
 	/* Prefer a 5 GHz channel */
-	for (i = 0; i < intersection->reg_classes; i++) {
-#ifdef ANDROID_P2P
-		struct p2p_reg_class prc;
-		struct p2p_reg_class *c = &prc;
-		p2p_copy_reg_class(c, &intersection->reg_class[i]);
-#else
-		struct p2p_reg_class *c = &intersection->reg_class[i];
-#endif
-		if ((c->reg_class == 115 || c->reg_class == 124) &&
-		    c->channels) {
-			unsigned int r;
-
-			/*
-			 * Pick one of the available channels in the operating
-			 * class at random.
-			 */
-			os_get_random((u8 *) &r, sizeof(r));
-			r %= c->channels;
-			p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection",
-				c->reg_class, c->channel[r]);
-			p2p->op_reg_class = c->reg_class;
-			p2p->op_channel = c->channel[r];
-			return;
-		}
+	if (p2p_channel_select(intersection, op_classes_5ghz,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
 	}
 
 	/*
@@ -470,10 +489,17 @@
 static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
 				 u8 *status)
 {
-	struct p2p_channels intersection;
-	size_t i;
+	struct p2p_channels tmp, intersection;
 
-	p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection);
+	p2p_channels_dump(p2p, "own channels", &p2p->channels);
+	p2p_channels_dump(p2p, "peer channels", &dev->channels);
+	p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp);
+	p2p_channels_dump(p2p, "intersection", &tmp);
+	p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq);
+	p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp);
+	p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection);
+	p2p_channels_dump(p2p, "intersection with local channel list",
+			  &intersection);
 	if (intersection.reg_classes == 0 ||
 	    intersection.reg_class[0].channels == 0) {
 		*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
@@ -481,14 +507,6 @@
 		return -1;
 	}
 
-	for (i = 0; i < intersection.reg_classes; i++) {
-		struct p2p_reg_class *c;
-		c = &intersection.reg_class[i];
-		p2p_dbg(p2p, "reg_class %u", c->reg_class);
-		wpa_hexdump(MSG_DEBUG, "P2P: channels",
-			    c->channel, c->channels);
-	}
-
 	if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
 				   p2p->op_channel)) {
 		if (dev->flags & P2P_DEV_FORCE_FREQ) {
@@ -588,17 +606,53 @@
 	if (msg.status && *msg.status) {
 		p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request",
 			*msg.status);
+		if (dev && p2p->go_neg_peer == dev &&
+		    *msg.status == P2P_SC_FAIL_REJECTED_BY_USER) {
+			/*
+			 * This mechanism for using Status attribute in GO
+			 * Negotiation Request is not compliant with the P2P
+			 * specification, but some deployed devices use it to
+			 * indicate rejection of GO Negotiation in a case where
+			 * they have sent out GO Negotiation Response with
+			 * status 1. The P2P specification explicitly disallows
+			 * this. To avoid unnecessary interoperability issues
+			 * and extra frames, mark the pending negotiation as
+			 * failed and do not reply to this GO Negotiation
+			 * Request frame.
+			 */
+			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+			p2p_go_neg_failed(p2p, dev, *msg.status);
+			p2p_parse_free(&msg);
+			return;
+		}
 		goto fail;
 	}
 
 	if (dev == NULL)
 		dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
-	else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+	else if ((dev->flags & P2P_DEV_PROBE_REQ_ONLY) ||
+		  !(dev->flags & P2P_DEV_REPORTED))
 		p2p_add_dev_info(p2p, sa, dev, &msg);
+	else if (!dev->listen_freq && !dev->oper_freq) {
+		/*
+		 * This may happen if the peer entry was added based on PD
+		 * Request and no Probe Request/Response frame has been received
+		 * from this peer (or that information has timed out).
+		 */
+		p2p_dbg(p2p, "Update peer " MACSTR
+			" based on GO Neg Req since listen/oper freq not known",
+			MAC2STR(dev->info.p2p_device_addr));
+		p2p_add_dev_info(p2p, sa, dev, &msg);
+	}
+
 	if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
 		p2p_dbg(p2p, "User has rejected this peer");
 		status = P2P_SC_FAIL_REJECTED_BY_USER;
-	} else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+	} else if (dev == NULL ||
+		   (dev->wps_method == WPS_NOT_READY &&
+		    (p2p->authorized_oob_dev_pw_id == 0 ||
+		     p2p->authorized_oob_dev_pw_id !=
+		     msg.dev_password_id))) {
 		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 			MAC2STR(sa));
 		status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -685,6 +739,28 @@
 			}
 			break;
 		default:
+			if (msg.dev_password_id &&
+			    msg.dev_password_id == dev->oob_pw_id) {
+				p2p_dbg(p2p, "Peer using NFC");
+				if (dev->wps_method != WPS_NFC) {
+					p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+						p2p_wps_method_str(
+							dev->wps_method));
+					status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+					goto fail;
+				}
+				break;
+			}
+#ifdef CONFIG_WPS_NFC
+			if (p2p->authorized_oob_dev_pw_id &&
+			    msg.dev_password_id ==
+			    p2p->authorized_oob_dev_pw_id) {
+				p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+				dev->wps_method = WPS_NFC;
+				dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+				break;
+			}
+#endif /* CONFIG_WPS_NFC */
 			p2p_dbg(p2p, "Unsupported Device Password ID %d",
 				msg.dev_password_id);
 			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -780,6 +856,9 @@
 		extra = wpabuf_len(p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -824,6 +903,9 @@
 		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
 	return buf;
 }
 
@@ -832,7 +914,6 @@
 			     const u8 *data, size_t len, int rx_freq)
 {
 	struct p2p_device *dev;
-	struct wpabuf *conf;
 	int go = -1;
 	struct p2p_message msg;
 	u8 status = P2P_SC_SUCCESS;
@@ -876,7 +957,7 @@
 		if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
 			p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation");
 			dev->flags |= P2P_DEV_NOT_YET_READY;
-			dev->wait_count = 0;
+			os_get_reltime(&dev->go_neg_wait_started);
 			p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
 			p2p_set_timeout(p2p, 0, 0);
 		} else {
@@ -1010,6 +1091,17 @@
 		}
 		break;
 	default:
+		if (msg.dev_password_id &&
+		    msg.dev_password_id == dev->oob_pw_id) {
+			p2p_dbg(p2p, "Peer using NFC");
+			if (dev->wps_method != WPS_NFC) {
+				p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		}
 		p2p_dbg(p2p, "Unsupported Device Password ID %d",
 			msg.dev_password_id);
 		status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1026,10 +1118,13 @@
 	os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
 
 fail:
-	conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
-				     msg.operating_channel, go);
+	/* Store GO Negotiation Confirmation to allow retransmission */
+	wpabuf_free(dev->go_neg_conf);
+	dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token,
+						 status, msg.operating_channel,
+						 go);
 	p2p_parse_free(&msg);
-	if (conf == NULL)
+	if (dev->go_neg_conf == NULL)
 		return;
 	p2p_dbg(p2p, "Sending GO Negotiation Confirm");
 	if (status == P2P_SC_SUCCESS) {
@@ -1041,12 +1136,18 @@
 		freq = rx_freq;
 	else
 		freq = dev->listen_freq;
+
+	dev->go_neg_conf_freq = freq;
+	dev->go_neg_conf_sent = 0;
+
 	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
-			    wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
+			    wpabuf_head(dev->go_neg_conf),
+			    wpabuf_len(dev->go_neg_conf), 200) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 		p2p_go_neg_failed(p2p, dev, -1);
-	}
-	wpabuf_free(conf);
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	} else
+		dev->go_neg_conf_sent++;
 	if (status != P2P_SC_SUCCESS) {
 		p2p_dbg(p2p, "GO Negotiation failed");
 		p2p_go_neg_failed(p2p, dev, status);
@@ -1080,9 +1181,11 @@
 
 	if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
 		p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore");
+		p2p_parse_free(&msg);
 		return;
 	}
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 
 	if (msg.dialog_token != dev->dialog_token) {
 		p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
@@ -1131,16 +1234,6 @@
 		}
 	}
 
-#ifdef ANDROID_P2P
-	if (msg.operating_channel) {
-		dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
-						     msg.operating_channel[4]);
-		p2p_dbg(p2p, "P2P: Peer operating channel preference: %d MHz",
-			dev->oper_freq);
-	} else
-		dev->oper_freq = 0;
-#endif
-
 	if (!msg.channel_list) {
 		p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
 #ifdef CONFIG_P2P_STRICT
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 15e7622..da8588a 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
@@ -154,6 +155,7 @@
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (group->num_members >= group->cfg->max_clients)
 		group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+	group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
 	p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -213,6 +215,10 @@
 		extra = wpabuf_len(group->p2p->wfd_ie_beacon);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (group->p2p->vendor_elem &&
+	    group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+		extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
 	ie = wpabuf_alloc(257 + extra);
 	if (ie == NULL)
 		return NULL;
@@ -222,6 +228,11 @@
 		wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (group->p2p->vendor_elem &&
+	    group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+		wpabuf_put_buf(ie,
+			       group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
 	len = p2p_buf_add_ie_hdr(ie);
 	p2p_group_add_common_ies(group, ie);
 	p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
@@ -397,10 +408,38 @@
 #endif /* CONFIG_WIFI_DISPLAY */
 
 
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients)
+{
+	u8 *group_info;
+	int count = 0;
+	struct p2p_group_member *m;
+
+	p2p_dbg(group->p2p, "* P2P Group Info");
+	group_info = wpabuf_put(buf, 0);
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
+	wpabuf_put_le16(buf, 0); /* Length to be filled */
+	for (m = group->members; m; m = m->next) {
+		p2p_client_info(buf, m);
+		count++;
+		if (max_clients >= 0 && count >= max_clients)
+			break;
+	}
+	WPA_PUT_LE16(group_info + 1,
+		     (u8 *) wpabuf_put(buf, 0) - group_info - 3);
+}
+
+
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
+{
+	p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
+			     group->cfg->ssid_len);
+}
+
+
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	struct wpabuf *p2p_subelems, *ie;
-	struct p2p_group_member *m;
 
 	p2p_subelems = wpabuf_alloc(500);
 	if (p2p_subelems == NULL)
@@ -413,21 +452,19 @@
 	p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
 
 	/* P2P Group Info: Only when at least one P2P Client is connected */
-	if (group->members) {
-		u8 *group_info;
-		group_info = wpabuf_put(p2p_subelems, 0);
-		wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
-		wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
-		for (m = group->members; m; m = m->next)
-			p2p_client_info(p2p_subelems, m);
-		WPA_PUT_LE16(group_info + 1,
-			     (u8 *) wpabuf_put(p2p_subelems, 0) - group_info -
-			     3);
-	}
+	if (group->members)
+		p2p_buf_add_group_info(group, p2p_subelems, -1);
 
 	ie = p2p_group_encaps_probe_resp(p2p_subelems);
 	wpabuf_free(p2p_subelems);
 
+	if (group->p2p->vendor_elem &&
+	    group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) {
+		struct wpabuf *extra;
+		extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]);
+		ie = wpabuf_concat(extra, ie);
+	}
+
 #ifdef CONFIG_WIFI_DISPLAY
 	if (group->wfd_ie) {
 		struct wpabuf *wfd = wpabuf_dup(group->wfd_ie);
@@ -614,6 +651,10 @@
 		extra = wpabuf_len(group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (group->p2p->vendor_elem &&
+	    group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+		extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
+
 	/*
 	 * (Re)Association Response - P2P IE
 	 * Status attribute (shall be present when association request is
@@ -629,6 +670,11 @@
 		wpabuf_put_buf(resp, group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (group->p2p->vendor_elem &&
+	    group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+		wpabuf_put_buf(resp,
+			       group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
+
 	rlen = p2p_buf_add_ie_hdr(resp);
 	if (status != P2P_SC_SUCCESS)
 		p2p_buf_add_status(resp, status);
@@ -953,7 +999,7 @@
 	if (!iter)
 		return NULL;
 
-	return iter->addr;
+	return iter->dev_addr;
 }
 
 
@@ -980,3 +1026,36 @@
 	return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
 			 group->cfg->ssid_len) == 0;
 }
+
+
+void p2p_group_force_beacon_update_ies(struct p2p_group *group)
+{
+	group->beacon_update = 1;
+	p2p_group_update_ies(group);
+}
+
+
+int p2p_group_get_freq(struct p2p_group *group)
+{
+	return group->cfg->freq;
+}
+
+
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group)
+{
+	return group->cfg;
+}
+
+
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+			    int (*group_callback)(struct p2p_group *group,
+						  void *user_data),
+			    void *user_data)
+{
+	unsigned int i;
+
+	for (i = 0; i < p2p->num_groups; i++) {
+		if (!group_callback(p2p->groups[i], user_data))
+			break;
+	}
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 81e521e..3b60582 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -12,6 +12,10 @@
 #include "utils/list.h"
 #include "p2p.h"
 
+#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
+
+enum p2p_role_indication;
+
 enum p2p_go_state {
 	UNKNOWN_GO,
 	LOCAL_GO,
@@ -23,9 +27,11 @@
  */
 struct p2p_device {
 	struct dl_list list;
-	struct os_time last_seen;
+	struct os_reltime last_seen;
 	int listen_freq;
+	int oob_go_neg_freq;
 	enum p2p_wps_method wps_method;
+	u16 oob_pw_id;
 
 	struct p2p_peer_info info;
 
@@ -77,8 +83,6 @@
 #define P2P_DEV_PROBE_REQ_ONLY BIT(0)
 #define P2P_DEV_REPORTED BIT(1)
 #define P2P_DEV_NOT_YET_READY BIT(2)
-#define P2P_DEV_SD_INFO BIT(3)
-#define P2P_DEV_SD_SCHEDULE BIT(4)
 #define P2P_DEV_PD_PEER_DISPLAY BIT(5)
 #define P2P_DEV_PD_PEER_KEYPAD BIT(6)
 #define P2P_DEV_USER_REJECTED BIT(7)
@@ -93,9 +97,11 @@
 #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16)
 #define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
 #define P2P_DEV_NO_PREF_CHAN BIT(18)
+#define P2P_DEV_WAIT_INV_REQ_ACK BIT(19)
 	unsigned int flags;
 
 	int status; /* enum p2p_status_code */
+	struct os_reltime go_neg_wait_started;
 	unsigned int wait_count;
 	unsigned int connect_reqs;
 	unsigned int invitation_reqs;
@@ -105,6 +111,23 @@
 
 	u8 go_timeout;
 	u8 client_timeout;
+
+	/**
+	 * go_neg_conf_sent - Number of GO Negotiation Confirmation retries
+	 */
+	u8 go_neg_conf_sent;
+
+	/**
+	 * freq - Frquency on which the GO Negotiation Confirmation is sent
+	 */
+	int go_neg_conf_freq;
+
+	/**
+	 * go_neg_conf - GO Negotiation Confirmation frame
+	 */
+	struct wpabuf *go_neg_conf;
+
+	int sd_pending_bcast_queries;
 };
 
 struct p2p_sd_query {
@@ -205,16 +228,6 @@
 		 * P2P_INVITE_LISTEN - Listen during Invite
 		 */
 		P2P_INVITE_LISTEN,
-
-		/**
-		 * P2P_SEARCH_WHEN_READY - Waiting to start Search
-		 */
-		P2P_SEARCH_WHEN_READY,
-
-		/**
-		 * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search
-		 */
-		P2P_CONTINUE_SEARCH_WHEN_READY,
 	} state;
 
 	/**
@@ -237,14 +250,6 @@
 	 */
 	struct dl_list devices;
 
-#ifdef ANDROID_P2P
-	/**
-	 * sd_dev_list - device pointer to be serviced next
-	 * for service discovery
-	 */
-	struct dl_list *sd_dev_list;
-#endif
-
 	/**
 	 * go_neg_peer - Pointer to GO Negotiation peer
 	 */
@@ -257,6 +262,7 @@
 
 	const u8 *invite_go_dev_addr;
 	u8 invite_go_dev_addr_buf[ETH_ALEN];
+	int invite_dev_pw_id;
 
 	/**
 	 * sd_peer - Pointer to Service Discovery peer
@@ -268,6 +274,12 @@
 	 */
 	struct p2p_sd_query *sd_query;
 
+	/**
+	 * num_p2p_sd_queries - Total number of broadcast SD queries present in
+	 * the list
+	 */
+	int num_p2p_sd_queries;
+
 	/* GO Negotiation data */
 
 	/**
@@ -324,6 +336,8 @@
 	 */
 	struct p2p_channels channels;
 
+	struct wpa_freq_range_list no_go_freq;
+
 	enum p2p_pending_action_state {
 		P2P_NO_PENDING_ACTION,
 		P2P_PENDING_GO_NEG_REQUEST,
@@ -394,6 +408,7 @@
 	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 */
 	unsigned int num_req_dev_types;
@@ -401,7 +416,7 @@
 	u8 *find_dev_id;
 	u8 find_dev_id_buf[ETH_ALEN];
 
-	struct os_time find_start; /* time of last p2p_find start */
+	struct os_reltime find_start; /* time of last p2p_find start */
 
 	struct p2p_group **groups;
 	size_t num_groups;
@@ -450,6 +465,14 @@
 	 */
 	int pd_retries;
 
+	/**
+	 * pd_force_freq - Forced frequency for PD retries or 0 to auto-select
+	 *
+	 * This is is used during PD retries for join-a-group case to use the
+	 * correct operating frequency determined from a BSS entry for the GO.
+	 */
+	int pd_force_freq;
+
 	u8 go_timeout;
 	u8 client_timeout;
 
@@ -457,6 +480,10 @@
 	unsigned int search_delay;
 	int in_search_delay;
 
+	u8 pending_reg_class;
+	u8 pending_channel;
+	u8 pending_channel_forced;
+
 #ifdef CONFIG_WIFI_DISPLAY
 	struct wpabuf *wfd_ie_beacon;
 	struct wpabuf *wfd_ie_probe_req;
@@ -470,6 +497,10 @@
 	struct wpabuf *wfd_assoc_bssid;
 	struct wpabuf *wfd_coupled_sink_info;
 #endif /* CONFIG_WIFI_DISPLAY */
+
+	u16 authorized_oob_dev_pw_id;
+
+	struct wpabuf **vendor_elem;
 };
 
 /**
@@ -511,6 +542,8 @@
 
 	const u8 *minor_reason_code;
 
+	const u8 *oob_go_neg_channel;
+
 	/* P2P Device Info */
 	const u8 *p2p_device_info;
 	size_t p2p_device_info_len;
@@ -522,6 +555,7 @@
 
 	/* WPS IE */
 	u16 dev_password_id;
+	int dev_password_id_present;
 	u16 wps_config_methods;
 	const u8 *wps_pri_dev_type;
 	const u8 *wps_sec_dev_type_list;
@@ -536,6 +570,8 @@
 	size_t model_number_len;
 	const u8 *serial_number;
 	size_t serial_number_len;
+	const u8 *oob_dev_password;
+	size_t oob_dev_password_len;
 
 	/* DS Parameter Set IE */
 	const u8 *ds_params;
@@ -570,16 +606,26 @@
 void p2p_channels_intersect(const struct p2p_channels *a,
 			    const struct p2p_channels *b,
 			    struct p2p_channels *res);
+void p2p_channels_union(const struct p2p_channels *a,
+			const struct p2p_channels *b,
+			struct p2p_channels *res);
+void p2p_channels_remove_freqs(struct p2p_channels *chan,
+			       const struct wpa_freq_range_list *list);
 int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
 			  u8 channel);
-#ifdef ANDROID_P2P
-size_t p2p_copy_reg_class(struct p2p_reg_class *dc, struct p2p_reg_class *sc);
-#endif
+void p2p_channels_dump(struct p2p_data *p2p, const char *title,
+		       const struct p2p_channels *chan);
+int p2p_channel_select(struct p2p_channels *chans, const int *classes,
+		       u8 *op_class, u8 *op_channel);
+int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
+			      u8 *op_channel);
 
 /* p2p_parse.c */
 int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
 int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
 int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_len, struct p2p_message *msg);
 void p2p_parse_free(struct p2p_message *msg);
 int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
 int p2p_group_info_parse(const u8 *gi, size_t gi_len,
@@ -602,7 +648,12 @@
 int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
 				size_t group_id_len);
 void p2p_group_update_ies(struct p2p_group *group);
+void p2p_group_force_beacon_update_ies(struct p2p_group *group);
 struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients);
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf);
+int p2p_group_get_freq(struct p2p_group *group);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -634,8 +685,11 @@
 void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
 				   u16 interval);
 void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
-		      int all_attr);
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role);
+int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
+		     int all_attr);
 
 /* p2p_sd.c */
 struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -681,7 +735,7 @@
 void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 				 const u8 *data, size_t len);
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-		    const u8 *go_dev_addr);
+		    const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
 void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
 
@@ -709,7 +763,7 @@
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
 		      struct p2p_device *dev, struct p2p_message *msg);
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
-		   struct os_time *rx_time, int level, const u8 *ies,
+		   struct os_reltime *rx_time, int level, const u8 *ies,
 		   size_t ies_len, int scan_res);
 struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
 struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
@@ -727,7 +781,8 @@
 		    size_t len, unsigned int wait_time);
 void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
-			unsigned int force_freq, unsigned int pref_freq);
+			unsigned int force_freq, unsigned int pref_freq,
+			int go);
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
 void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 6b3dafb..ef01a66 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -10,13 +10,15 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "p2p_i.h"
 #include "p2p.h"
 
 
 static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
 						struct p2p_device *peer,
-						const u8 *go_dev_addr)
+						const u8 *go_dev_addr,
+						int dev_pw_id)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -44,6 +46,9 @@
 		extra = wpabuf_len(wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -85,6 +90,14 @@
 		wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
+
+	if (dev_pw_id >= 0) {
+		/* WSC IE in Invitation Request for NFC static handover */
+		p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
+	}
+
 	return buf;
 }
 
@@ -221,14 +234,18 @@
 		goto fail;
 	}
 
+	p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels);
+	p2p_channels_dump(p2p, "peer channels", &dev->channels);
 	p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
 			       &intersection);
+	p2p_channels_dump(p2p, "intersection", &intersection);
 
 	if (p2p->cfg->invitation_process) {
 		status = p2p->cfg->invitation_process(
 			p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
 			msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
-			&go, group_bssid, &op_freq, persistent, &intersection);
+			&go, group_bssid, &op_freq, persistent, &intersection,
+			msg.dev_password_id_present ? msg.dev_password_id : -1);
 	}
 
 	if (op_freq) {
@@ -281,7 +298,9 @@
 			}
 		}
 
-		if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+		/* Reselect the channel only for the case of the GO */
+		if (go &&
+		    !p2p_channels_includes(&intersection, p2p->op_reg_class,
 					   p2p->op_channel)) {
 			p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect",
 				p2p->op_reg_class, p2p->op_channel);
@@ -296,7 +315,7 @@
 				status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 				goto fail;
 			}
-		} else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+		} else if (go && !(dev->flags & P2P_DEV_FORCE_FREQ) &&
 			   !p2p->cfg->cfg_op_channel) {
 			p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u",
 				p2p->op_reg_class, p2p->op_channel);
@@ -352,12 +371,17 @@
 		p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
 	} else
 		p2p->inv_group_bssid_ptr = NULL;
-	if (msg.group_id_len - ETH_ALEN <= 32) {
-		os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
-			  msg.group_id_len - ETH_ALEN);
-		p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+	if (msg.group_id) {
+		if (msg.group_id_len - ETH_ALEN <= 32) {
+			os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+				  msg.group_id_len - ETH_ALEN);
+			p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+		}
+		os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
+	} else {
+		p2p->inv_ssid_len = 0;
+		os_memset(p2p->inv_go_dev_addr, 0, ETH_ALEN);
 	}
-	os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
 	p2p->inv_status = status;
 	p2p->inv_op_freq = op_freq;
 
@@ -407,7 +431,7 @@
 		return;
 	}
 
-	if (!msg.channel_list) {
+	if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) {
 		p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from "
 			MACSTR, MAC2STR(sa));
 #ifdef CONFIG_P2P_STRICT
@@ -416,6 +440,9 @@
 #endif /* CONFIG_P2P_STRICT */
 		/* Try to survive without peer channel list */
 		channels = &p2p->channels;
+	} else if (!msg.channel_list) {
+		/* Non-success cases are not required to include Channel List */
+		channels = &p2p->channels;
 	} else if (p2p_peer_channels_check(p2p, &p2p->channels, dev,
 					   msg.channel_list,
 					   msg.channel_list_len) < 0) {
@@ -428,9 +455,25 @@
 		channels = &intersection;
 	}
 
-	if (p2p->cfg->invitation_result)
+	if (p2p->cfg->invitation_result) {
+		int peer_oper_freq = 0;
+		int freq = p2p_channel_to_freq(p2p->op_reg_class,
+					       p2p->op_channel);
+		if (freq < 0)
+			freq = 0;
+
+		if (msg.operating_channel) {
+			peer_oper_freq = p2p_channel_to_freq(
+				msg.operating_channel[3],
+				msg.operating_channel[4]);
+			if (peer_oper_freq < 0)
+				peer_oper_freq = 0;
+		}
+
 		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
-					    msg.group_bssid, channels, sa);
+					    msg.group_bssid, channels, sa,
+					    freq, peer_oper_freq);
+	}
 
 	p2p_parse_free(&msg);
 
@@ -441,12 +484,14 @@
 
 
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-		    const u8 *go_dev_addr)
+		    const u8 *go_dev_addr, int dev_pw_id)
 {
 	struct wpabuf *req;
 	int freq;
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send Invitation Request",
@@ -454,7 +499,7 @@
 		return -1;
 	}
 
-	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
 	if (req == NULL)
 		return -1;
 	if (p2p->state != P2P_IDLE)
@@ -466,10 +511,12 @@
 	dev->invitation_reqs++;
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
-			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+			    wpabuf_head(req), wpabuf_len(req), 500) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 		/* Use P2P find to recover and retry */
 		p2p_set_timeout(p2p, 0, 0);
+	} else {
+		dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK;
 	}
 
 	wpabuf_free(req);
@@ -487,12 +534,15 @@
 		return;
 	}
 
+	if (success)
+		p2p->invite_peer->flags &= ~P2P_DEV_WAIT_INV_REQ_ACK;
+
 	/*
 	 * Use P2P find, if needed, to find the other device from its listen
 	 * channel.
 	 */
 	p2p_set_state(p2p, P2P_INVITE);
-	p2p_set_timeout(p2p, 0, success ? 350000 : 100000);
+	p2p_set_timeout(p2p, 0, success ? 500000 : 100000);
 }
 
 
@@ -519,7 +569,7 @@
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq)
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id)
 {
 	struct p2p_device *dev;
 
@@ -537,15 +587,22 @@
 		p2p->invite_go_dev_addr = NULL;
 	wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID",
 			  ssid, ssid_len);
+	if (dev_pw_id >= 0) {
+		p2p_dbg(p2p, "Invitation to use Device Password ID %d",
+			dev_pw_id);
+	}
+	p2p->invite_dev_pw_id = dev_pw_id;
 
 	dev = p2p_get_device(p2p, peer);
-	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
+			    dev->oob_go_neg_freq <= 0)) {
 		p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR,
 			MAC2STR(peer));
 		return -1;
 	}
 
-	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
+				role != P2P_INVITE_ROLE_CLIENT) < 0)
 		return -1;
 
 	if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq &&
@@ -576,5 +633,5 @@
 	os_memcpy(p2p->inv_ssid, ssid, ssid_len);
 	p2p->inv_ssid_len = ssid_len;
 	p2p->inv_persistent = persistent_group;
-	return p2p_invite_send(p2p, dev, go_dev_addr);
+	return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
 }
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 097a31d..d6144a0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -268,6 +268,19 @@
 		wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
 			   *msg->minor_reason_code);
 		break;
+	case P2P_ATTR_OOB_GO_NEG_CHANNEL:
+		if (len < 6) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg "
+				   "Channel attribute (length %d)", len);
+			return -1;
+		}
+		msg->oob_go_neg_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: "
+			   "Country %c%c(0x%02x) Operating Class %d "
+			   "Channel Number %d Role %d",
+			   data[0], data[1], data[2], data[3], data[4],
+			   data[5]);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
 			   "(length %d)", id, len);
@@ -340,6 +353,7 @@
 		msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
 		wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
 			   msg->dev_password_id);
+		msg->dev_password_id_present = 1;
 	}
 	if (attr.primary_dev_type) {
 		char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -367,6 +381,9 @@
 	msg->serial_number = attr.serial_number;
 	msg->serial_number_len = attr.serial_number_len;
 
+	msg->oob_dev_password = attr.oob_dev_password;
+	msg->oob_dev_password_len = attr.oob_dev_password_len;
+
 	return 0;
 }
 
@@ -450,6 +467,33 @@
 }
 
 
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_len, struct p2p_message *msg)
+{
+	os_memset(msg, 0, sizeof(*msg));
+
+	msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len);
+	if (msg->wps_attributes &&
+	    p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len);
+	if (msg->p2p_attributes &&
+	    p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+		if (msg->p2p_attributes)
+			wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+					msg->p2p_attributes);
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 /**
  * p2p_parse_free - Free temporary data from P2P parsing
  * @msg: Parsed attributes
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 54aa428..e101367 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -53,6 +54,9 @@
 		extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -77,6 +81,9 @@
 		wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+
 	return buf;
 }
 
@@ -111,6 +118,9 @@
 		extra = wpabuf_len(wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
+
 	buf = wpabuf_alloc(100 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -125,6 +135,9 @@
 		wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
+
 	return buf;
 }
 
@@ -224,7 +237,8 @@
 			    p2p->cfg->dev_addr,
 			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
-	}
+	} else
+		p2p->send_action_in_progress = 1;
 
 	wpabuf_free(resp);
 
@@ -437,6 +451,7 @@
 	}
 
 	p2p->user_initiated_pd = user_initiated_pd;
+	p2p->pd_force_freq = force_freq;
 
 	if (p2p->user_initiated_pd)
 		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
@@ -472,4 +487,5 @@
 	p2p->user_initiated_pd = 0;
 	os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
 	p2p->pd_retries = 0;
+	p2p->pd_force_freq = 0;
 }
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 795ca6b..6235b1d 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -52,6 +52,7 @@
 {
 	struct p2p_sd_query *q;
 	int wsd = 0;
+	int count = 0;
 
 	if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
 		return NULL; /* peer does not support SD */
@@ -64,8 +65,19 @@
 		/* Use WSD only if the peer indicates support or it */
 		if (q->wsd && !wsd)
 			continue;
-		if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
-			return q;
+		/* if the query is a broadcast query */
+		if (q->for_all_peers) {
+			/*
+			 * check if there are any broadcast queries pending for
+			 * this device
+			 */
+			if (dev->sd_pending_bcast_queries <= 0)
+				return NULL;
+			/* query number that needs to be send to the device */
+			if (count == dev->sd_pending_bcast_queries - 1)
+				return q;
+			count++;
+		}
 		if (!q->for_all_peers &&
 		    os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
 		    0)
@@ -76,14 +88,37 @@
 }
 
 
+static void p2p_decrease_sd_bc_queries(struct p2p_data *p2p, int query_number)
+{
+	struct p2p_device *dev;
+
+	p2p->num_p2p_sd_queries--;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (query_number <= dev->sd_pending_bcast_queries - 1) {
+			/*
+			 * Query not yet sent to the device and it is to be
+			 * removed, so update the pending count.
+			*/
+			dev->sd_pending_bcast_queries--;
+		}
+	}
+}
+
+
 static int p2p_unlink_sd_query(struct p2p_data *p2p,
 			       struct p2p_sd_query *query)
 {
 	struct p2p_sd_query *q, *prev;
+	int query_number = 0;
+
 	q = p2p->sd_queries;
 	prev = NULL;
 	while (q) {
 		if (q == query) {
+			/* If the query is a broadcast query, decrease one from
+			 * all the devices */
+			if (query->for_all_peers)
+				p2p_decrease_sd_bc_queries(p2p, query_number);
 			if (prev)
 				prev->next = q->next;
 			else
@@ -92,6 +127,8 @@
 				p2p->sd_query = NULL;
 			return 1;
 		}
+		if (q->for_all_peers)
+			query_number++;
 		prev = q;
 		q = q->next;
 	}
@@ -118,6 +155,7 @@
 		q = q->next;
 		p2p_free_sd_query(prev);
 	}
+	p2p->num_p2p_sd_queries = 0;
 }
 
 
@@ -133,8 +171,7 @@
 
 	/* ANQP Query Request Frame */
 	len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 	wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
 	wpabuf_put_buf(buf, tlvs);
 	gas_anqp_set_element_len(buf, len_pos);
@@ -180,8 +217,7 @@
 	if (tlvs) {
 		/* ANQP Query Response Frame */
 		len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, P2P_OUI_TYPE);
+		wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 		 /* Service Update Indicator */
 		wpabuf_put_le16(buf, update_indic);
 		wpabuf_put_buf(buf, tlvs);
@@ -212,8 +248,7 @@
 		/* ANQP Query Response Frame */
 		wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */
 		wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, P2P_OUI_TYPE);
+		wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 		/* Service Update Indicator */
 		wpabuf_put_le16(buf, update_indic);
 	}
@@ -231,6 +266,7 @@
 	int ret = 0;
 	struct p2p_sd_query *query;
 	int freq;
+	unsigned int wait_time;
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
 	if (freq <= 0) {
@@ -255,13 +291,26 @@
 	p2p->sd_query = query;
 	p2p->pending_action_state = P2P_PENDING_SD;
 
+	wait_time = 5000;
+	if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
+		wait_time = p2p->cfg->max_listen;
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
-			    wpabuf_head(req), wpabuf_len(req), 5000) < 0) {
+			    wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 		ret = -1;
 	}
 
+	/* Update the pending broadcast SD query count for this device */
+	dev->sd_pending_bcast_queries--;
+
+	/*
+	 * If there are no pending broadcast queries for this device, mark it as
+	 * done (-1).
+	 */
+	if (dev->sd_pending_bcast_queries == 0)
+		dev->sd_pending_bcast_queries = -1;
+
 	wpabuf_free(req);
 
 	return ret;
@@ -345,17 +394,12 @@
 		return;
 	}
 
-	if (WPA_GET_BE24(pos) != OUI_WFA) {
-		p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
 		return;
 	}
-	pos += 3;
-
-	if (*pos != P2P_OUI_TYPE) {
-		p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-		return;
-	}
-	pos++;
+	pos += 4;
 
 	if (pos + 2 > end)
 		return;
@@ -430,15 +474,7 @@
 	u16 slen;
 	u16 update_indic;
 
-#ifdef ANDROID_P2P
-	if (p2p->state != P2P_SD_DURING_FIND) {
-		p2p_dbg(p2p, "P2P: #### Not ignoring unexpected GAS Initial Response from "
-			MACSTR " state %d", MAC2STR(sa), p2p->state);
-	}
-	if (p2p->sd_peer == NULL ||
-#else
 	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
-#endif
 	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
 		p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from "
 			MACSTR, MAC2STR(sa));
@@ -531,17 +567,12 @@
 		return;
 	}
 
-	if (WPA_GET_BE24(pos) != OUI_WFA) {
-		p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
 		return;
 	}
-	pos += 3;
-
-	if (*pos != P2P_OUI_TYPE) {
-		p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-		return;
-	}
-	pos++;
+	pos += 4;
 
 	if (pos + 2 > end)
 		return;
@@ -549,8 +580,6 @@
 	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
 	pos += 2;
 
-	p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
-	p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
 	p2p->sd_peer = NULL;
 
 	if (p2p->sd_query) {
@@ -653,15 +682,7 @@
 
 	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
 
-#ifdef ANDROID_P2P
-	if (p2p->state != P2P_SD_DURING_FIND) {
-		p2p_dbg(p2p, "P2P: #### Not ignoring unexpected GAS Comeback Response from "
-			MACSTR " state %d", MAC2STR(sa), p2p->state);
-	}
-	if (p2p->sd_peer == NULL ||
-#else
 	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
-#endif
 	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
 		p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from "
 			MACSTR, MAC2STR(sa));
@@ -765,17 +786,12 @@
 	if (pos + 4 > end)
 		return;
 
-	if (WPA_GET_BE24(pos) != OUI_WFA) {
-		p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
 		return;
 	}
-	pos += 3;
-
-	if (*pos != P2P_OUI_TYPE) {
-		p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-		return;
-	}
-	pos++;
+	pos += 4;
 
 	if (pos + 2 > end)
 		return;
@@ -803,8 +819,6 @@
 		return;
 	}
 
-	p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
-	p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
 	p2p->sd_peer = NULL;
 
 	if (p2p->sd_query) {
@@ -835,19 +849,6 @@
 		      const struct wpabuf *tlvs)
 {
 	struct p2p_sd_query *q;
-#ifdef ANDROID_P2P
-	/* Currently, supplicant doesn't support more than one pending broadcast SD request.
-	 * So reject if application is registering another one before cancelling the existing one.
-	 */
-	for (q = p2p->sd_queries; q; q = q->next) {
-		if( (q->for_all_peers == 1) && (!dst)) {
-				wpa_printf(MSG_ERROR, "P2P: Already one pending"
-					" Broadcast request. Please cancel the current one"
-					" before adding a new one");
-				return NULL;
-		}
-	}
-#endif
 
 	q = os_zalloc(sizeof(*q));
 	if (q == NULL)
@@ -870,8 +871,16 @@
 
 	if (dst == NULL) {
 		struct p2p_device *dev;
-		dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
-			dev->flags &= ~P2P_DEV_SD_INFO;
+
+		p2p->num_p2p_sd_queries++;
+
+		/* Update all the devices for the newly added broadcast query */
+		dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+			if (dev->sd_pending_bcast_queries <= 0)
+				dev->sd_pending_bcast_queries = 1;
+			else
+				dev->sd_pending_bcast_queries++;
+		}
 	}
 
 	return q;
@@ -901,9 +910,6 @@
 {
 	if (p2p_unlink_sd_query(p2p, req)) {
 		p2p_dbg(p2p, "Cancel pending SD query %p", req);
-#ifdef ANDROID_P2P
-		p2p->sd_dev_list = NULL;
-#endif
 		p2p_free_sd_query(req);
 		return 0;
 	}
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index a4c48f6..189300a 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -94,6 +94,14 @@
 		if (channel < 149 || channel > 161)
 			return -1;
 		return 5000 + 5 * channel;
+	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+		if (channel < 36 || channel > 161)
+			return -1;
+		return 5000 + 5 * channel;
+	case 180: /* 60 GHz band, channels 1..4 */
+		if (channel < 1 || channel > 4)
+			return -1;
+		return 56160 + 2160 * channel;
 	}
 	return -1;
 }
@@ -204,6 +212,105 @@
 }
 
 
+static void p2p_op_class_union(struct p2p_reg_class *cl,
+			       const struct p2p_reg_class *b_cl)
+{
+	size_t i, j;
+
+	for (i = 0; i < b_cl->channels; i++) {
+		for (j = 0; j < cl->channels; j++) {
+			if (b_cl->channel[i] == cl->channel[j])
+				break;
+		}
+		if (j == cl->channels) {
+			if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS)
+				return;
+			cl->channel[cl->channels++] = b_cl->channel[i];
+		}
+	}
+}
+
+
+/**
+ * p2p_channels_union - Union of channel lists
+ * @a: First set of channels
+ * @b: Second set of channels
+ * @res: Data structure for returning the union of channels
+ */
+void p2p_channels_union(const struct p2p_channels *a,
+			const struct p2p_channels *b,
+			struct p2p_channels *res)
+{
+	size_t i, j;
+
+	if (a != res)
+		os_memcpy(res, a, sizeof(*res));
+
+	for (i = 0; i < res->reg_classes; i++) {
+		struct p2p_reg_class *cl = &res->reg_class[i];
+		for (j = 0; j < b->reg_classes; j++) {
+			const struct p2p_reg_class *b_cl = &b->reg_class[j];
+			if (cl->reg_class != b_cl->reg_class)
+				continue;
+			p2p_op_class_union(cl, b_cl);
+		}
+	}
+
+	for (j = 0; j < b->reg_classes; j++) {
+		const struct p2p_reg_class *b_cl = &b->reg_class[j];
+
+		for (i = 0; i < res->reg_classes; i++) {
+			struct p2p_reg_class *cl = &res->reg_class[i];
+			if (cl->reg_class == b_cl->reg_class)
+				break;
+		}
+
+		if (i == res->reg_classes) {
+			if (res->reg_classes == P2P_MAX_REG_CLASSES)
+				return;
+			os_memcpy(&res->reg_class[res->reg_classes++],
+				  b_cl, sizeof(struct p2p_reg_class));
+		}
+	}
+}
+
+
+void p2p_channels_remove_freqs(struct p2p_channels *chan,
+			       const struct wpa_freq_range_list *list)
+{
+	size_t o, c;
+
+	if (list == NULL)
+		return;
+
+	o = 0;
+	while (o < chan->reg_classes) {
+		struct p2p_reg_class *op = &chan->reg_class[o];
+
+		c = 0;
+		while (c < op->channels) {
+			int freq = p2p_channel_to_freq(op->reg_class,
+						       op->channel[c]);
+			if (freq > 0 && freq_range_list_includes(list, freq)) {
+				op->channels--;
+				os_memmove(&op->channel[c],
+					   &op->channel[c + 1],
+					   op->channels - c);
+			} else
+				c++;
+		}
+
+		if (op->channels == 0) {
+			chan->reg_classes--;
+			os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1],
+				   (chan->reg_classes - o) *
+				   sizeof(struct p2p_reg_class));
+		} else
+			o++;
+	}
+}
+
+
 /**
  * p2p_channels_includes - Check whether a channel is included in the list
  * @channels: List of supported channels
@@ -244,39 +351,9 @@
 }
 
 
-#ifdef ANDROID_P2P
-static int p2p_block_op_freq(unsigned int freq)
-{
-	return (freq >= 5170 && freq < 5745);
-}
-
-
-size_t p2p_copy_reg_class(struct p2p_reg_class *dc, struct p2p_reg_class *sc)
-{
-	unsigned int i;
-
-	dc->reg_class = sc->reg_class;
-	dc->channels = 0;
-	for (i=0; i < sc->channels; i++) {
-		if (!p2p_block_op_freq(p2p_channel_to_freq(sc->reg_class,
-							   sc->channel[i]))) {
-			dc->channel[dc->channels] = sc->channel[i];
-			dc->channels++;
-		}
-	}
-	return dc->channels;
-}
-#endif
-
-
 int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
 {
 	u8 op_reg_class, op_channel;
-
-#ifdef ANDROID_P2P
-	if (p2p_block_op_freq(freq))
-		return 0;
-#endif
 	if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
 		return 0;
 	return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
@@ -284,29 +361,142 @@
 }
 
 
+int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq)
+{
+	u8 op_reg_class, op_channel;
+	if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
+		return 0;
+	return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+				     op_channel) &&
+		!freq_range_list_includes(&p2p->no_go_freq, freq);
+}
+
+
+int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq)
+{
+	u8 op_reg_class, op_channel;
+	if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
+		return 0;
+	return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+				     op_channel) ||
+		p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class,
+				      op_channel);
+}
+
+
 unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
 			       const struct p2p_channels *channels)
 {
 	unsigned int i;
 	int freq = 0;
+	const struct p2p_channels *tmpc = channels ?
+		channels : &p2p->cfg->channels;
 
-	if (channels == NULL) {
-		if (p2p->cfg->num_pref_chan) {
-			freq = p2p_channel_to_freq(
-				p2p->cfg->pref_chan[0].op_class,
-				p2p->cfg->pref_chan[0].chan);
-			if (freq < 0)
-				freq = 0;
-		}
-		return freq;
-	}
+	if (tmpc == NULL)
+		return 0;
 
 	for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
 		freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class,
 					   p2p->cfg->pref_chan[i].chan);
-		if (p2p_channels_includes_freq(channels, freq))
+		if (p2p_channels_includes_freq(tmpc, freq))
 			return freq;
 	}
+	return 0;
+}
+
+
+void p2p_channels_dump(struct p2p_data *p2p, const char *title,
+		       const struct p2p_channels *chan)
+{
+	char buf[500], *pos, *end;
+	size_t i, j;
+	int ret;
+
+	pos = buf;
+	end = pos + sizeof(buf);
+
+	for (i = 0; i < chan->reg_classes; i++) {
+		const struct p2p_reg_class *c;
+		c = &chan->reg_class[i];
+		ret = os_snprintf(pos, end - pos, " %u:", c->reg_class);
+		if (ret < 0 || ret >= end - pos)
+			break;
+		pos += ret;
+
+		for (j = 0; j < c->channels; j++) {
+			ret = os_snprintf(pos, end - pos, "%s%u",
+					  j == 0 ? "" : ",",
+					  c->channel[j]);
+			if (ret < 0 || ret >= end - pos)
+				break;
+			pos += ret;
+		}
+	}
+	*pos = '\0';
+
+	p2p_dbg(p2p, "%s:%s", title, buf);
+}
+
+
+static u8 p2p_channel_pick_random(const u8 *channels, unsigned int num_channels)
+{
+	unsigned int r;
+	os_get_random((u8 *) &r, sizeof(r));
+	r %= num_channels;
+	return channels[r];
+}
+
+
+int p2p_channel_select(struct p2p_channels *chans, const int *classes,
+		       u8 *op_class, u8 *op_channel)
+{
+	unsigned int i, j;
+
+	for (j = 0; classes == NULL || classes[j]; j++) {
+		for (i = 0; i < chans->reg_classes; i++) {
+			struct p2p_reg_class *c = &chans->reg_class[i];
+
+			if (c->channels == 0)
+				continue;
+
+			if (classes == NULL || c->reg_class == classes[j]) {
+				/*
+				 * Pick one of the available channels in the
+				 * operating class at random.
+				 */
+				*op_class = c->reg_class;
+				*op_channel = p2p_channel_pick_random(
+					c->channel, c->channels);
+				return 0;
+			}
+		}
+		if (classes == NULL)
+			break;
+	}
+
+	return -1;
+}
+
+
+int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
+			      u8 *op_channel)
+{
+	u8 chan[3];
+	unsigned int num_channels = 0;
+
+	/* Try to find available social channels from 2.4 GHz */
+	if (p2p_channels_includes(chans, 81, 1))
+		chan[num_channels++] = 1;
+	if (p2p_channels_includes(chans, 81, 6))
+		chan[num_channels++] = 6;
+	if (p2p_channels_includes(chans, 81, 11))
+		chan[num_channels++] = 11;
+
+	if (num_channels == 0)
+		return -1;
+
+	*op_class = 81;
+	*op_channel = p2p_channel_pick_random(chan, num_channels);
 
 	return 0;
 }
diff --git a/src/pae/Makefile b/src/pae/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/pae/Makefile
@@ -0,0 +1,8 @@
+all:
+	@echo Nothing to be made.
+
+clean:
+	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
new file mode 100644
index 0000000..cf43c59
--- /dev/null
+++ b/src/pae/ieee802_1x_cp.c
@@ -0,0 +1,744 @@
+/*
+ * IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "utils/state_machine.h"
+#include "ieee802_1x_kay.h"
+#include "ieee802_1x_secy_ops.h"
+#include "pae/ieee802_1x_cp.h"
+
+#define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
+#define STATE_MACHINE_DEBUG_PREFIX "CP"
+
+static u8 default_cs_id[] = CS_ID_GCM_AES_128;
+
+/* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
+enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
+
+struct ieee802_1x_cp_sm {
+	enum cp_states {
+		CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED,
+		CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT,
+		CP_TRANSMITTING, CP_ABANDON, CP_RETIRE
+	} CP_state;
+	Boolean changed;
+
+	/* CP -> Client */
+	Boolean port_valid;
+
+	/* Logon -> CP */
+	enum connect_type connect;
+	u8 *authorization_data;
+
+	/* KaY -> CP */
+	Boolean chgd_server; /* clear by CP */
+	Boolean elected_self;
+	u8 *authorization_data1;
+	enum confidentiality_offset cipher_offset;
+	u8 *cipher_suite;
+	Boolean new_sak; /* clear by CP */
+	struct ieee802_1x_mka_ki distributed_ki;
+	u8 distributed_an;
+	Boolean using_receive_sas;
+	Boolean all_receiving;
+	Boolean server_transmitting;
+	Boolean using_transmit_sa;
+
+	/* CP -> KaY */
+	struct ieee802_1x_mka_ki *lki;
+	u8 lan;
+	Boolean ltx;
+	Boolean lrx;
+	struct ieee802_1x_mka_ki *oki;
+	u8 oan;
+	Boolean otx;
+	Boolean orx;
+
+	/* CP -> SecY */
+	Boolean protect_frames;
+	enum validate_frames validate_frames;
+
+	Boolean replay_protect;
+	u32 replay_window;
+
+	u8 *current_cipher_suite;
+	enum confidentiality_offset confidentiality_offset;
+	Boolean controlled_port_enabled;
+
+	/* SecY -> CP */
+	Boolean port_enabled; /* SecY->CP */
+
+	/* private */
+	u32 transmit_when;
+	u32 transmit_delay;
+	u32 retire_when;
+	u32 retire_delay;
+
+	/* not defined IEEE Std 802.1X-2010 */
+	struct ieee802_1x_kay *kay;
+};
+
+static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
+					      void *timeout_ctx);
+static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx,
+						void *timeout_ctx);
+
+
+static int changed_cipher(struct ieee802_1x_cp_sm *sm)
+{
+	return sm->confidentiality_offset != sm->cipher_offset ||
+		os_memcmp(sm->current_cipher_suite, sm->cipher_suite,
+			  CS_ID_LEN) != 0;
+}
+
+
+static int changed_connect(struct ieee802_1x_cp_sm *sm)
+{
+	return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm);
+}
+
+
+SM_STATE(CP, INIT)
+{
+	SM_ENTRY(CP, INIT);
+
+	sm->controlled_port_enabled = FALSE;
+	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+
+	sm->port_valid = FALSE;
+
+	os_free(sm->lki);
+	sm->lki = NULL;
+	sm->ltx = FALSE;
+	sm->lrx = FALSE;
+
+	os_free(sm->oki);
+	sm->oki = NULL;
+	sm->otx = FALSE;
+	sm->orx = FALSE;
+
+	sm->port_enabled = TRUE;
+	sm->chgd_server = FALSE;
+}
+
+
+SM_STATE(CP, CHANGE)
+{
+	SM_ENTRY(CP, CHANGE);
+
+	sm->port_valid = FALSE;
+	sm->controlled_port_enabled = FALSE;
+	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+
+	if (sm->lki)
+		ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
+	if (sm->oki)
+		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
+}
+
+
+SM_STATE(CP, ALLOWED)
+{
+	SM_ENTRY(CP, ALLOWED);
+
+	sm->protect_frames = FALSE;
+	sm->replay_protect = FALSE;
+	sm->validate_frames = Checked;
+
+	sm->port_valid = FALSE;
+	sm->controlled_port_enabled = TRUE;
+
+	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, AUTHENTICATED)
+{
+	SM_ENTRY(CP, AUTHENTICATED);
+
+	sm->protect_frames = FALSE;
+	sm->replay_protect = FALSE;
+	sm->validate_frames = Checked;
+
+	sm->port_valid = FALSE;
+	sm->controlled_port_enabled = TRUE;
+
+	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, SECURED)
+{
+	struct ieee802_1x_cp_conf conf;
+
+	SM_ENTRY(CP, SECURED);
+
+	sm->chgd_server = FALSE;
+
+	ieee802_1x_kay_cp_conf(sm->kay, &conf);
+	sm->protect_frames = conf.protect;
+	sm->replay_protect = conf.replay_protect;
+	sm->validate_frames = conf.validate;
+
+	/* NOTE: now no other than default cipher suiter(AES-GCM-128) */
+	os_memcpy(sm->current_cipher_suite, sm->cipher_suite, CS_ID_LEN);
+	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite,
+					     CS_ID_LEN);
+
+	sm->confidentiality_offset = sm->cipher_offset;
+
+	sm->port_valid = TRUE;
+
+	secy_cp_control_confidentiality_offset(sm->kay,
+					       sm->confidentiality_offset);
+	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, RECEIVE)
+{
+	SM_ENTRY(CP, RECEIVE);
+	/* RECEIVE state machine not keep with Figure 12-2 in
+	 * IEEE Std 802.1X-2010 */
+	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) {
+		wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__);
+		return;
+	}
+	os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki));
+	sm->lan = sm->distributed_an;
+	sm->ltx = FALSE;
+	sm->lrx = FALSE;
+	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+					  sm->ltx, sm->lrx);
+	ieee802_1x_kay_create_sas(sm->kay, sm->lki);
+	ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki);
+	sm->new_sak = FALSE;
+	sm->all_receiving = FALSE;
+}
+
+
+SM_STATE(CP, RECEIVING)
+{
+	SM_ENTRY(CP, RECEIVING);
+
+	sm->lrx = TRUE;
+	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+					  sm->ltx, sm->lrx);
+	sm->transmit_when = sm->transmit_delay;
+	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+	eloop_register_timeout(sm->transmit_when / 1000, 0,
+			       ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+	/* the electedSelf have been set before CP entering to RECEIVING
+	 * but the CP will transmit from RECEIVING to READY under
+	 * the !electedSelf when KaY is not key server */
+	ieee802_1x_cp_sm_step(sm);
+	sm->using_receive_sas = FALSE;
+	sm->server_transmitting = FALSE;
+}
+
+
+SM_STATE(CP, READY)
+{
+	SM_ENTRY(CP, READY);
+
+	ieee802_1x_kay_enable_new_info(sm->kay);
+}
+
+
+SM_STATE(CP, TRANSMIT)
+{
+	SM_ENTRY(CP, TRANSMIT);
+
+	sm->controlled_port_enabled = TRUE;
+	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+	sm->ltx = TRUE;
+	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+					  sm->ltx, sm->lrx);
+	ieee802_1x_kay_enable_tx_sas(sm->kay,  sm->lki);
+	sm->all_receiving = FALSE;
+	sm->server_transmitting = FALSE;
+}
+
+
+SM_STATE(CP, TRANSMITTING)
+{
+	SM_ENTRY(CP, TRANSMITTING);
+	sm->retire_when = sm->orx ? sm->retire_delay : 0;
+	sm->otx = FALSE;
+	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+				       sm->otx, sm->orx);
+	ieee802_1x_kay_enable_new_info(sm->kay);
+	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
+	eloop_register_timeout(sm->retire_when / 1000, 0,
+			       ieee802_1x_cp_retire_when_timeout, sm, NULL);
+	sm->using_transmit_sa = FALSE;
+}
+
+
+SM_STATE(CP, ABANDON)
+{
+	SM_ENTRY(CP, ABANDON);
+	sm->lrx = FALSE;
+	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+					  sm->ltx, sm->lrx);
+	ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
+
+	os_free(sm->lki);
+	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 */
+	os_free(sm->oki);
+	sm->oki = NULL;
+	sm->orx = FALSE;
+	sm->otx = FALSE;
+	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+				       sm->otx, sm->orx);
+}
+
+
+/**
+ * CP state machine handler entry
+ */
+SM_STEP(CP)
+{
+	if (!sm->port_enabled)
+		SM_ENTER(CP, INIT);
+
+	switch (sm->CP_state) {
+	case CP_BEGIN:
+		SM_ENTER(CP, INIT);
+		break;
+
+	case CP_INIT:
+		SM_ENTER(CP, CHANGE);
+		break;
+
+	case CP_CHANGE:
+		if (sm->connect == UNAUTHENTICATED)
+			SM_ENTER(CP, ALLOWED);
+		else if (sm->connect == AUTHENTICATED)
+			SM_ENTER(CP, AUTHENTICATED);
+		else if (sm->connect == SECURE)
+			SM_ENTER(CP, SECURED);
+		break;
+
+	case CP_ALLOWED:
+		if (sm->connect != UNAUTHENTICATED)
+			SM_ENTER(CP, CHANGE);
+		break;
+
+	case CP_AUTHENTICATED:
+		if (sm->connect != AUTHENTICATED)
+			SM_ENTER(CP, CHANGE);
+		break;
+
+	case CP_SECURED:
+		if (changed_connect(sm))
+			SM_ENTER(CP, CHANGE);
+		else if (sm->new_sak)
+			SM_ENTER(CP, RECEIVE);
+		break;
+
+	case CP_RECEIVE:
+		if (sm->using_receive_sas)
+			SM_ENTER(CP, RECEIVING);
+		break;
+
+	case CP_RECEIVING:
+		if (sm->new_sak || changed_connect(sm))
+			SM_ENTER(CP, ABANDON);
+		if (!sm->elected_self)
+			SM_ENTER(CP, READY);
+		if (sm->elected_self &&
+		    (sm->all_receiving || !sm->transmit_when))
+			SM_ENTER(CP, TRANSMIT);
+		break;
+
+	case CP_TRANSMIT:
+		if (sm->using_transmit_sa)
+			SM_ENTER(CP, TRANSMITTING);
+		break;
+
+	case CP_TRANSMITTING:
+		if (!sm->retire_when || changed_connect(sm))
+			SM_ENTER(CP, RETIRE);
+		break;
+
+	case CP_RETIRE:
+		if (changed_connect(sm))
+			SM_ENTER(CP, CHANGE);
+		else if (sm->new_sak)
+			SM_ENTER(CP, RECEIVE);
+		break;
+
+	case CP_READY:
+		if (sm->new_sak || changed_connect(sm))
+			SM_ENTER(CP, RECEIVE);
+		if (sm->server_transmitting)
+			SM_ENTER(CP, TRANSMIT);
+		break;
+	case CP_ABANDON:
+		if (changed_connect(sm))
+			SM_ENTER(CP, RETIRE);
+		else if (sm->new_sak)
+			SM_ENTER(CP, RECEIVE);
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "CP: the state machine is not defined");
+		break;
+	}
+}
+
+
+/**
+ * ieee802_1x_cp_sm_init -
+ */
+struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(
+	struct ieee802_1x_kay *kay,
+	struct ieee802_1x_cp_conf *pcp_conf)
+{
+	struct ieee802_1x_cp_sm *sm;
+
+	sm = os_zalloc(sizeof(*sm));
+	if (sm == NULL) {
+		wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
+		return NULL;
+	}
+
+	sm->kay = kay;
+
+	sm->port_valid = FALSE;
+
+	sm->chgd_server = FALSE;
+
+	sm->protect_frames = pcp_conf->protect;
+	sm->validate_frames = pcp_conf->validate;
+	sm->replay_protect = pcp_conf->replay_protect;
+	sm->replay_window = pcp_conf->replay_window;
+
+	sm->controlled_port_enabled = FALSE;
+
+	sm->lki = NULL;
+	sm->lrx = FALSE;
+	sm->ltx = FALSE;
+	sm->oki = NULL;
+	sm->orx = FALSE;
+	sm->otx = FALSE;
+
+	sm->cipher_suite = os_zalloc(CS_ID_LEN);
+	sm->current_cipher_suite = os_zalloc(CS_ID_LEN);
+	if (!sm->cipher_suite || !sm->current_cipher_suite) {
+		wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
+		os_free(sm->cipher_suite);
+		os_free(sm->current_cipher_suite);
+		os_free(sm);
+		return NULL;
+	}
+	os_memcpy(sm->current_cipher_suite, default_cs_id, CS_ID_LEN);
+	os_memcpy(sm->cipher_suite, default_cs_id, CS_ID_LEN);
+	sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
+	sm->confidentiality_offset = sm->cipher_offset;
+	sm->transmit_delay = MKA_LIFE_TIME;
+	sm->retire_delay = MKA_SAK_RETIRE_TIME;
+	sm->CP_state = CP_BEGIN;
+	sm->changed = FALSE;
+	sm->authorization_data = NULL;
+
+	wpa_printf(MSG_DEBUG, "CP: state machine created");
+
+	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+	secy_cp_control_confidentiality_offset(sm->kay,
+					       sm->confidentiality_offset);
+
+	SM_ENTER(CP, INIT);
+	SM_STEP_RUN(CP);
+
+	return sm;
+}
+
+
+static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm)
+{
+	enum cp_states prev_state;
+	int i;
+
+	for (i = 0; i < 100; i++) {
+		prev_state = sm->CP_state;
+		SM_STEP_RUN(CP);
+		if (prev_state == sm->CP_state)
+			break;
+	}
+}
+
+
+static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = eloop_ctx;
+	ieee802_1x_cp_step_run(sm);
+}
+
+
+/**
+ * ieee802_1x_cp_sm_deinit -
+ */
+void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
+{
+	wpa_printf(MSG_DEBUG, "CP: state machine removed");
+	if (!sm)
+		return;
+
+	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
+	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
+	os_free(sm->lki);
+	os_free(sm->oki);
+	os_free(sm->cipher_suite);
+	os_free(sm->current_cipher_suite);
+	os_free(sm->authorization_data);
+	os_free(sm);
+}
+
+
+/**
+ * ieee802_1x_cp_connect_pending
+ */
+void ieee802_1x_cp_connect_pending(void *cp_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+	sm->connect = PENDING;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_unauthenticated
+ */
+void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx;
+
+	sm->connect = UNAUTHENTICATED;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_authenticated
+ */
+void ieee802_1x_cp_connect_authenticated(void *cp_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+	sm->connect = AUTHENTICATED;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_secure
+ */
+void ieee802_1x_cp_connect_secure(void *cp_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+	sm->connect = SECURE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_chgdserver -
+ */
+void ieee802_1x_cp_signal_chgdserver(void *cp_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+	sm->chgd_server = TRUE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_electedself -
+ */
+void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->elected_self = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_authorizationdata -
+ */
+void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	os_free(sm->authorization_data);
+	sm->authorization_data = os_zalloc(len);
+	if (sm->authorization_data)
+		os_memcpy(sm->authorization_data, pdata, len);
+}
+
+
+/**
+ * ieee802_1x_cp_set_ciphersuite -
+ */
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	os_memcpy(sm->cipher_suite, pid, CS_ID_LEN);
+}
+
+
+/**
+ * ieee802_1x_cp_set_offset -
+ */
+void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->cipher_offset = offset;
+}
+
+
+/**
+ * ieee802_1x_cp_signal_newsak -
+ */
+void ieee802_1x_cp_signal_newsak(void *cp_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->new_sak = TRUE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_distributedki -
+ */
+void ieee802_1x_cp_set_distributedki(void *cp_ctx,
+				     const struct ieee802_1x_mka_ki *dki)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki));
+}
+
+
+/**
+ * ieee802_1x_cp_set_distributedan -
+ */
+void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->distributed_an = an;
+}
+
+
+/**
+ * ieee802_1x_cp_set_usingreceivesas -
+ */
+void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->using_receive_sas = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_allreceiving -
+ */
+void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->all_receiving = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_servertransmitting -
+ */
+void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->server_transmitting = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_usingtransmitsas -
+ */
+void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status)
+{
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	sm->using_transmit_sa = status;
+}
+
+
+/**
+ * ieee802_1x_cp_sm_step - Advance EAPOL state machines
+ * @sm: EAPOL state machine
+ *
+ * This function is called to advance CP state machines after any change
+ * that could affect their state.
+ */
+void ieee802_1x_cp_sm_step(void *cp_ctx)
+{
+	/*
+	 * Run ieee802_1x_cp_step_run from a registered timeout
+	 * to make sure that other possible timeouts/events are processed
+	 * and to avoid long function call chains.
+	 */
+	struct ieee802_1x_cp_sm *sm = cp_ctx;
+	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
+	eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL);
+}
+
+
+static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
+					      void *timeout_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = eloop_ctx;
+	sm->retire_when = 0;
+	ieee802_1x_cp_step_run(sm);
+}
+
+
+static void
+ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct ieee802_1x_cp_sm *sm = eloop_ctx;
+	sm->transmit_when = 0;
+	ieee802_1x_cp_step_run(sm);
+}
diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h
new file mode 100644
index 0000000..773c930
--- /dev/null
+++ b/src/pae/ieee802_1x_cp.h
@@ -0,0 +1,50 @@
+/*
+ * IEEE Std 802.1X-2010 Controlled Port of PAE state machine - CP state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_CP_H
+#define IEEE802_1X_CP_H
+
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct ieee802_1x_cp_sm;
+struct ieee802_1x_kay;
+struct ieee802_1x_mka_ki;
+
+struct ieee802_1x_cp_conf {
+	Boolean protect;
+	Boolean replay_protect;
+	enum validate_frames validate;
+	u32 replay_window;
+};
+
+
+struct ieee802_1x_cp_sm *
+ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay,
+		      struct ieee802_1x_cp_conf *pcp_conf);
+void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm);
+void ieee802_1x_cp_sm_step(void *cp_ctx);
+void ieee802_1x_cp_connect_pending(void *cp_ctx);
+void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx);
+void ieee802_1x_cp_connect_authenticated(void *cp_ctx);
+void ieee802_1x_cp_connect_secure(void *cp_ctx);
+void ieee802_1x_cp_signal_chgdserver(void *cp_ctx);
+void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len);
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid);
+void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset);
+void ieee802_1x_cp_signal_newsak(void *cp_ctx);
+void ieee802_1x_cp_set_distributedki(void *cp_ctx,
+				     const struct ieee802_1x_mka_ki *dki);
+void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an);
+void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status);
+
+#endif /* IEEE802_1X_CP_H */
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
new file mode 100644
index 0000000..56c195a
--- /dev/null
+++ b/src/pae/ieee802_1x_kay.c
@@ -0,0 +1,3527 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <time.h>
+#include "includes.h"
+#include "common.h"
+#include "list.h"
+#include "eloop.h"
+#include "wpabuf.h"
+#include "state_machine.h"
+#include "l2_packet/l2_packet.h"
+#include "common/eapol_common.h"
+#include "crypto/aes_wrap.h"
+#include "ieee802_1x_cp.h"
+#include "ieee802_1x_key.h"
+#include "ieee802_1x_kay.h"
+#include "ieee802_1x_kay_i.h"
+#include "ieee802_1x_secy_ops.h"
+
+
+#define DEFAULT_SA_KEY_LEN	16
+#define DEFAULT_ICV_LEN		16
+#define MAX_ICV_LEN		32  /* 32 bytes, 256 bits */
+
+#define PENDING_PN_EXHAUSTION 0xC0000000
+
+/* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */
+#define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 }
+static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009;
+
+/* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */
+static struct macsec_ciphersuite cipher_suite_tbl[] = {
+	/* GCM-AES-128 */
+	{
+		CS_ID_GCM_AES_128,
+		CS_NAME_GCM_AES_128,
+		MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+		16,
+
+		0 /* index */
+	},
+};
+#define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
+#define DEFAULT_CS_INDEX  0
+
+static struct mka_alg mka_alg_tbl[] = {
+	{
+		MKA_ALGO_AGILITY_2009,
+		/* 128-bit CAK, KEK, ICK, ICV */
+		16, 16,	16, 16,
+		ieee802_1x_cak_128bits_aes_cmac,
+		ieee802_1x_ckn_128bits_aes_cmac,
+		ieee802_1x_kek_128bits_aes_cmac,
+		ieee802_1x_ick_128bits_aes_cmac,
+		ieee802_1x_icv_128bits_aes_cmac,
+
+		1, /* index */
+	},
+};
+#define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
+
+
+static int is_ki_equal(struct ieee802_1x_mka_ki *ki1,
+		       struct ieee802_1x_mka_ki *ki2)
+{
+	return os_memcmp(ki1->mi, ki2->mi, MI_LEN) == 0 &&
+		ki1->kn == ki2->kn;
+}
+
+
+struct mka_param_body_handler {
+	int (*body_tx)(struct ieee802_1x_mka_participant *participant,
+		       struct wpabuf *buf);
+	int (*body_rx)(struct ieee802_1x_mka_participant *participant,
+		       const u8 *mka_msg, size_t msg_len);
+	int (*body_length)(struct ieee802_1x_mka_participant *participant);
+	Boolean (*body_present)(struct ieee802_1x_mka_participant *participant);
+};
+
+
+static void set_mka_param_body_len(void *body, unsigned int len)
+{
+	struct ieee802_1x_mka_hdr *hdr = body;
+	hdr->length = (len >> 8) & 0x0f;
+	hdr->length1 = len & 0xff;
+}
+
+
+static unsigned int get_mka_param_body_len(const void *body)
+{
+	const struct ieee802_1x_mka_hdr *hdr = body;
+	return (hdr->length << 8) | hdr->length1;
+}
+
+
+static int get_mka_param_body_type(const void *body)
+{
+	const struct ieee802_1x_mka_hdr *hdr = body;
+	return hdr->type;
+}
+
+
+/**
+ * ieee802_1x_mka_dump_basic_body -
+ */
+static void
+ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body)
+{
+	size_t body_len;
+
+	if (!body)
+		return;
+
+	body_len = get_mka_param_body_len(body);
+	wpa_printf(MSG_DEBUG, "*** MKA Basic Parameter set ***");
+	wpa_printf(MSG_DEBUG, "\tVersion.......: %d", body->version);
+	wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority);
+	wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server);
+	wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired);
+	wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capbility);
+	wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+	wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR,
+		   MAC2STR(body->actor_sci.addr));
+	wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d",
+		   be_to_host16(body->actor_sci.port));
+	wpa_hexdump(MSG_DEBUG, "\tMember Id.....:",
+		    body->actor_mi, sizeof(body->actor_mi));
+	wpa_printf(MSG_DEBUG, "\tMessage Number: %d",
+		   be_to_host32(body->actor_mn));
+	wpa_hexdump(MSG_DEBUG, "\tAlgo Agility..:",
+		    body->algo_agility, sizeof(body->algo_agility));
+	wpa_hexdump_ascii(MSG_DEBUG, "\tCAK Name......:", body->ckn,
+			  body_len + MKA_HDR_LEN - sizeof(*body));
+}
+
+
+/**
+ * ieee802_1x_mka_dump_peer_body -
+ */
+static void
+ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body)
+{
+	size_t body_len;
+	size_t i;
+	u8 *mi;
+	u32 mn;
+
+	if (body == NULL)
+		return;
+
+	body_len = get_mka_param_body_len(body);
+	if (body->type == MKA_LIVE_PEER_LIST) {
+		wpa_printf(MSG_DEBUG, "*** Live Peer List ***");
+		wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+	} else if (body->type == MKA_POTENTIAL_PEER_LIST) {
+		wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***");
+		wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+	}
+
+	for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) {
+		mi = body->peer + i;
+		os_memcpy(&mn, mi + MI_LEN, sizeof(mn));
+		wpa_hexdump_ascii(MSG_DEBUG, "\tMember Id.....:", mi, MI_LEN);
+		wpa_printf(MSG_DEBUG, "\tMessage Number: %d", be_to_host32(mn));
+	}
+}
+
+
+/**
+ * ieee802_1x_mka_dump_dist_sak_body -
+ */
+static void
+ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body)
+{
+	size_t body_len;
+
+	if (body == NULL)
+		return;
+
+	body_len = get_mka_param_body_len(body);
+	wpa_printf(MSG_INFO, "*** Distributed SAK ***");
+	wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan);
+	wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d",
+		   body->confid_offset);
+	wpa_printf(MSG_INFO, "\tBody Length...........: %d", (int) body_len);
+	if (!body_len)
+		return;
+
+	wpa_printf(MSG_INFO, "\tKey Number............: %d",
+		   be_to_host32(body->kn));
+	wpa_hexdump(MSG_INFO, "\tAES Key Wrap of SAK...:", body->sak, 24);
+}
+
+
+static const char * yes_no(int val)
+{
+	return val ? "Yes" : "No";
+}
+
+
+/**
+ * ieee802_1x_mka_dump_sak_use_body -
+ */
+static void
+ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
+{
+	int body_len;
+
+	if (body == NULL)
+		return;
+
+	body_len = get_mka_param_body_len(body);
+	wpa_printf(MSG_DEBUG, "*** MACsec SAK Use ***");
+	wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan);
+	wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx));
+	wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx));
+	wpa_printf(MSG_DEBUG, "\tOld Key AN....: %d", body->oan);
+	wpa_printf(MSG_DEBUG, "\tOld Key Tx....: %s", yes_no(body->otx));
+	wpa_printf(MSG_DEBUG, "\tOld Key Rx....: %s", yes_no(body->orx));
+	wpa_printf(MSG_DEBUG, "\tPlain Key Tx....: %s", yes_no(body->ptx));
+	wpa_printf(MSG_DEBUG, "\tPlain Key Rx....: %s", yes_no(body->prx));
+	wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s",
+		   yes_no(body->delay_protect));
+	wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len);
+	if (!body_len)
+		return;
+
+	wpa_hexdump(MSG_DEBUG, "\tKey Server MI....:",
+		    body->lsrv_mi, sizeof(body->lsrv_mi));
+	wpa_printf(MSG_DEBUG, "\tKey Number.......: %u",
+		   be_to_host32(body->lkn));
+	wpa_printf(MSG_DEBUG, "\tLowest PN........: %u",
+		   be_to_host32(body->llpn));
+	wpa_hexdump_ascii(MSG_DEBUG, "\tOld Key Server MI....:",
+			  body->osrv_mi, sizeof(body->osrv_mi));
+	wpa_printf(MSG_DEBUG, "\tOld Key Number.......: %u",
+		   be_to_host32(body->okn));
+	wpa_printf(MSG_DEBUG, "\tOld Lowest PN........: %u",
+		   be_to_host32(body->olpn));
+}
+
+
+/**
+ * ieee802_1x_kay_get_participant -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn)
+{
+	struct ieee802_1x_mka_participant *participant;
+
+	dl_list_for_each(participant, &kay->participant_list,
+			 struct ieee802_1x_mka_participant, list) {
+		if (os_memcmp(participant->ckn.name, ckn,
+			      participant->ckn.len) == 0)
+			return participant;
+	}
+
+	wpa_printf(MSG_DEBUG, "KaY: participant is not found");
+
+	return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_get_principal_participant -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay)
+{
+	struct ieee802_1x_mka_participant *participant;
+
+	dl_list_for_each(participant, &kay->participant_list,
+			 struct ieee802_1x_mka_participant, list) {
+		if (participant->principal)
+			return participant;
+	}
+
+	wpa_printf(MSG_DEBUG, "KaY: principal participant is not founded");
+	return NULL;
+}
+
+
+static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers,
+						const u8 *mi)
+{
+	struct ieee802_1x_kay_peer *peer;
+
+	dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) {
+		if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
+			return peer;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_potential_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_potential_peer(
+	struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+	return get_peer_mi(&participant->potential_peers, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_live_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_live_peer(
+	struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+	return get_peer_mi(&participant->live_peers, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_peer(struct ieee802_1x_mka_participant *participant,
+			  const u8 *mi)
+{
+	return ieee802_1x_kay_is_in_live_peer(participant, mi) ||
+		ieee802_1x_kay_is_in_potential_peer(participant, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
+			const u8 *mi)
+{
+	struct ieee802_1x_kay_peer *peer;
+
+	peer = get_peer_mi(&participant->live_peers, mi);
+	if (peer)
+		return peer;
+
+	return get_peer_mi(&participant->potential_peers, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant,
+			     const u8 *mi)
+{
+	return get_peer_mi(&participant->live_peers, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_cipher_suite
+ */
+static struct macsec_ciphersuite *
+ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
+				u8 *cs_id)
+{
+	unsigned int i;
+
+	for (i = 0; i < CS_TABLE_SIZE; i++) {
+		if (os_memcmp(cipher_suite_tbl[i].id, cs_id, CS_ID_LEN) == 0)
+			break;
+	}
+	if (i >= CS_TABLE_SIZE)
+		return NULL;
+
+	return &cipher_suite_tbl[i];
+}
+
+
+/**
+ * ieee802_1x_kay_get_peer_sci
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant,
+			    const struct ieee802_1x_mka_sci *sci)
+{
+	struct ieee802_1x_kay_peer *peer;
+
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+			return peer;
+	}
+
+	dl_list_for_each(peer, &participant->potential_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+			return peer;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_init_receive_sa -
+ */
+static struct receive_sa *
+ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
+			       struct data_key *key)
+{
+	struct receive_sa *psa;
+
+	if (!psc || !key)
+		return NULL;
+
+	psa = os_zalloc(sizeof(*psa));
+	if (!psa) {
+		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+		return NULL;
+	}
+
+	psa->pkey = key;
+	psa->lowest_pn = lowest_pn;
+	psa->next_pn = lowest_pn;
+	psa->an = an;
+	psa->sc = psc;
+
+	os_get_time(&psa->created_time);
+	psa->in_use = FALSE;
+
+	dl_list_add(&psc->sa_list, &psa->list);
+	wpa_printf(MSG_DEBUG,
+		   "KaY: Create receive SA(AN: %d lowest_pn: %u of SC(channel: %d)",
+		   (int) an, lowest_pn, psc->channel);
+
+	return psa;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_receive_sa -
+ */
+static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
+{
+	psa->pkey = NULL;
+	wpa_printf(MSG_DEBUG,
+		   "KaY: Delete receive SA(an: %d) of SC(channel: %d)",
+		   psa->an, psa->sc->channel);
+	dl_list_del(&psa->list);
+	os_free(psa);
+}
+
+
+/**
+ * ieee802_1x_kay_init_receive_sc -
+ */
+static struct receive_sc *
+ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci,
+			       int channel)
+{
+	struct receive_sc *psc;
+
+	if (!psci)
+		return NULL;
+
+	psc = os_zalloc(sizeof(*psc));
+	if (!psc) {
+		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+		return NULL;
+	}
+
+	os_memcpy(&psc->sci, psci, sizeof(psc->sci));
+	psc->channel = channel;
+
+	os_get_time(&psc->created_time);
+	psc->receiving = FALSE;
+
+	dl_list_init(&psc->sa_list);
+	wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel);
+	wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci));
+
+	return psc;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_receive_sc -
+ **/
+static void
+ieee802_1x_kay_deinit_receive_sc(
+	struct ieee802_1x_mka_participant *participant, struct receive_sc *psc)
+{
+	struct receive_sa *psa, *pre_sa;
+
+	wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)",
+		   psc->channel);
+	dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
+			      list)  {
+		secy_disable_receive_sa(participant->kay, psa);
+		ieee802_1x_kay_deinit_receive_sa(psa);
+	}
+	dl_list_del(&psc->list);
+	os_free(psc);
+}
+
+
+/**
+ * ieee802_1x_kay_create_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
+				u8 *mi, u32 mn)
+{
+	struct ieee802_1x_kay_peer *peer;
+	struct receive_sc *rxsc;
+	u32 sc_ch = 0;
+
+	peer = os_zalloc(sizeof(*peer));
+	if (peer == NULL) {
+		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+		return NULL;
+	}
+
+	os_memcpy(peer->mi, mi, MI_LEN);
+	peer->mn = mn;
+	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+	peer->sak_used = FALSE;
+	os_memcpy(&peer->sci, &participant->current_peer_sci,
+		  sizeof(peer->sci));
+	dl_list_add(&participant->live_peers, &peer->list);
+
+	secy_get_available_receive_sc(participant->kay, &sc_ch);
+
+	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+	if (!rxsc)
+		return NULL;
+
+	dl_list_add(&participant->rxsc_list, &rxsc->list);
+	secy_create_receive_sc(participant->kay, rxsc);
+
+	wpa_printf(MSG_DEBUG, "KaY: Live peer created");
+	wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+	wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+	wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+	return peer;
+}
+
+
+/**
+ * ieee802_1x_kay_create_potential_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_create_potential_peer(
+	struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn)
+{
+	struct ieee802_1x_kay_peer *peer;
+
+	peer = os_zalloc(sizeof(*peer));
+	if (peer == NULL) {
+		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+		return NULL;
+	}
+
+	os_memcpy(peer->mi, mi, MI_LEN);
+	peer->mn = mn;
+	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+	peer->sak_used = FALSE;
+
+	dl_list_add(&participant->potential_peers, &peer->list);
+
+	wpa_printf(MSG_DEBUG, "KaY: potential peer created");
+	wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+	wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+	wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+	return peer;
+}
+
+
+/**
+ * ieee802_1x_kay_move_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
+			      u8 *mi, u32 mn)
+{
+	struct ieee802_1x_kay_peer *peer;
+	struct receive_sc *rxsc;
+	u32 sc_ch = 0;
+
+	dl_list_for_each(peer, &participant->potential_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
+			break;
+	}
+
+	os_memcpy(&peer->sci, &participant->current_peer_sci,
+		  sizeof(peer->sci));
+	peer->mn = mn;
+	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+
+	wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer");
+	wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+	wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+	wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+	dl_list_del(&peer->list);
+	dl_list_add_tail(&participant->live_peers, &peer->list);
+
+	secy_get_available_receive_sc(participant->kay, &sc_ch);
+
+	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+	if (!rxsc)
+		return NULL;
+
+	dl_list_add(&participant->rxsc_list, &rxsc->list);
+	secy_create_receive_sc(participant->kay, rxsc);
+
+	return peer;
+}
+
+
+
+/**
+ *  ieee802_1x_mka_basic_body_present -
+ */
+static Boolean
+ieee802_1x_mka_basic_body_present(
+	struct ieee802_1x_mka_participant *participant)
+{
+	return TRUE;
+}
+
+
+/**
+ * ieee802_1x_mka_basic_body_length -
+ */
+static int
+ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant)
+{
+	int length;
+
+	length = sizeof(struct ieee802_1x_mka_basic_body);
+	length += participant->ckn.len;
+	return (length + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_basic_body
+ */
+static int
+ieee802_1x_mka_encode_basic_body(
+	struct ieee802_1x_mka_participant *participant,
+	struct wpabuf *buf)
+{
+	struct ieee802_1x_mka_basic_body *body;
+	struct ieee802_1x_kay *kay = participant->kay;
+	unsigned int length = ieee802_1x_mka_basic_body_length(participant);
+
+	body = wpabuf_put(buf, length);
+
+	body->version = kay->mka_version;
+	body->priority = kay->actor_priority;
+	if (participant->is_elected)
+		body->key_server = participant->is_key_server;
+	else
+		body->key_server = participant->can_be_key_server;
+
+	body->macsec_desired = kay->macsec_desired;
+	body->macsec_capbility = kay->macsec_capable;
+	set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+	os_memcpy(body->actor_sci.addr, kay->actor_sci.addr,
+		  sizeof(kay->actor_sci.addr));
+	body->actor_sci.port = host_to_be16(kay->actor_sci.port);
+
+	os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi));
+	participant->mn = participant->mn + 1;
+	body->actor_mn = host_to_be32(participant->mn);
+	os_memcpy(body->algo_agility, participant->kay->algo_agility,
+		  sizeof(body->algo_agility));
+
+	os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len);
+
+	ieee802_1x_mka_dump_basic_body(body);
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_basic_body -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
+				 size_t msg_len)
+{
+	struct ieee802_1x_mka_participant *participant;
+	const struct ieee802_1x_mka_basic_body *body;
+	struct ieee802_1x_kay_peer *peer;
+
+	body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
+
+	if (body->version > MKA_VERSION_ID) {
+		wpa_printf(MSG_DEBUG,
+			   "KaY: peer's version(%d) greater than mka current version(%d)",
+			   body->version, MKA_VERSION_ID);
+	}
+	if (kay->is_obliged_key_server && body->key_server) {
+		wpa_printf(MSG_DEBUG, "I must be as key server");
+		return NULL;
+	}
+
+	participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+	if (!participant) {
+		wpa_printf(MSG_DEBUG, "Peer is not included in my CA");
+		return NULL;
+	}
+
+	/* If the peer's MI is my MI, I will choose new MI */
+	if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
+		os_get_random(participant->mi, sizeof(participant->mi));
+		participant->mn = 0;
+	}
+
+	os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
+	participant->current_peer_id.mn =  be_to_host32(body->actor_mn);
+	os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr,
+		  sizeof(participant->current_peer_sci.addr));
+	participant->current_peer_sci.port = be_to_host16(body->actor_sci.port);
+
+	/* handler peer */
+	peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
+	if (!peer) {
+		/* Check duplicated SCI */
+		/* TODO: What policy should be applied to detect duplicated SCI
+		 * is active attacker or a valid peer whose MI is be changed?
+		 */
+		peer = ieee802_1x_kay_get_peer_sci(participant,
+						   &body->actor_sci);
+		if (peer) {
+			wpa_printf(MSG_WARNING,
+				   "KaY: duplicated SCI detected, Maybe active attacker");
+			dl_list_del(&peer->list);
+			os_free(peer);
+		}
+
+		peer = ieee802_1x_kay_create_potential_peer(
+			participant, body->actor_mi,
+			be_to_host32(body->actor_mn));
+		if (!peer)
+			return NULL;
+
+		peer->macsec_desired = body->macsec_desired;
+		peer->macsec_capbility = body->macsec_capbility;
+		peer->is_key_server = (Boolean) body->key_server;
+		peer->key_server_priority = body->priority;
+	} else if (peer->mn < be_to_host32(body->actor_mn)) {
+		peer->mn = be_to_host32(body->actor_mn);
+		peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+		peer->macsec_desired = body->macsec_desired;
+		peer->macsec_capbility = body->macsec_capbility;
+		peer->is_key_server = (Boolean) body->key_server;
+		peer->key_server_priority = body->priority;
+	} else {
+		wpa_printf(MSG_WARNING, "KaY: The peer MN have received");
+		return NULL;
+	}
+
+	return participant;
+}
+
+
+/**
+ * ieee802_1x_mka_live_peer_body_present
+ */
+static Boolean
+ieee802_1x_mka_live_peer_body_present(
+	struct ieee802_1x_mka_participant *participant)
+{
+	return !dl_list_empty(&participant->live_peers);
+}
+
+
+/**
+ * ieee802_1x_kay_get_live_peer_length
+ */
+static int
+ieee802_1x_mka_get_live_peer_length(
+	struct ieee802_1x_mka_participant *participant)
+{
+	int len = MKA_HDR_LEN;
+	struct ieee802_1x_kay_peer *peer;
+
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list)
+		len += sizeof(struct ieee802_1x_mka_peer_id);
+
+	return (len + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_live_peer_body -
+ */
+static int
+ieee802_1x_mka_encode_live_peer_body(
+	struct ieee802_1x_mka_participant *participant,
+	struct wpabuf *buf)
+{
+	struct ieee802_1x_mka_peer_body *body;
+	struct ieee802_1x_kay_peer *peer;
+	unsigned int length;
+	struct ieee802_1x_mka_peer_id *body_peer;
+
+	length = ieee802_1x_mka_get_live_peer_length(participant);
+	body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
+
+	body->type = MKA_LIVE_PEER_LIST;
+	set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		body_peer = wpabuf_put(buf,
+				       sizeof(struct ieee802_1x_mka_peer_id));
+		os_memcpy(body_peer->mi, peer->mi, MI_LEN);
+		body_peer->mn = host_to_be32(peer->mn);
+		body_peer++;
+	}
+
+	ieee802_1x_mka_dump_peer_body(body);
+	return 0;
+}
+
+/**
+ * ieee802_1x_mka_potential_peer_body_present
+ */
+static Boolean
+ieee802_1x_mka_potential_peer_body_present(
+	struct ieee802_1x_mka_participant *participant)
+{
+	return !dl_list_empty(&participant->potential_peers);
+}
+
+
+/**
+ * ieee802_1x_kay_get_potential_peer_length
+ */
+static int
+ieee802_1x_mka_get_potential_peer_length(
+	struct ieee802_1x_mka_participant *participant)
+{
+	int len = MKA_HDR_LEN;
+	struct ieee802_1x_kay_peer *peer;
+
+	dl_list_for_each(peer, &participant->potential_peers,
+			 struct ieee802_1x_kay_peer, list)
+		len += sizeof(struct ieee802_1x_mka_peer_id);
+
+	return (len + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_potential_peer_body -
+ */
+static int
+ieee802_1x_mka_encode_potential_peer_body(
+	struct ieee802_1x_mka_participant *participant,
+	struct wpabuf *buf)
+{
+	struct ieee802_1x_mka_peer_body *body;
+	struct ieee802_1x_kay_peer *peer;
+	unsigned int length;
+	struct ieee802_1x_mka_peer_id *body_peer;
+
+	length = ieee802_1x_mka_get_potential_peer_length(participant);
+	body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
+
+	body->type = MKA_POTENTIAL_PEER_LIST;
+	set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+	dl_list_for_each(peer, &participant->potential_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		body_peer = wpabuf_put(buf,
+				       sizeof(struct ieee802_1x_mka_peer_id));
+		os_memcpy(body_peer->mi, peer->mi, MI_LEN);
+		body_peer->mn = host_to_be32(peer->mn);
+		body_peer++;
+	}
+
+	ieee802_1x_mka_dump_peer_body(body);
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_i_in_peerlist -
+ */
+static Boolean
+ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
+			     const u8 *mka_msg, size_t msg_len)
+{
+	Boolean included = FALSE;
+	struct ieee802_1x_mka_hdr *hdr;
+	size_t body_len;
+	size_t left_len;
+	int body_type;
+	u32 peer_mn;
+	const u8 *peer_mi;
+	const u8 *pos;
+	size_t i;
+
+	pos = mka_msg;
+	left_len = msg_len;
+	while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
+		hdr = (struct ieee802_1x_mka_hdr *) pos;
+		body_len = get_mka_param_body_len(hdr);
+		body_type = get_mka_param_body_type(hdr);
+
+		if (body_type != MKA_LIVE_PEER_LIST &&
+		    body_type != MKA_POTENTIAL_PEER_LIST)
+			goto SKIP_PEER;
+
+		ieee802_1x_mka_dump_peer_body(
+			(struct ieee802_1x_mka_peer_body *)pos);
+
+		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+			wpa_printf(MSG_ERROR,
+				   "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+				   (int) left_len, (int) MKA_HDR_LEN,
+				   (int) body_len, DEFAULT_ICV_LEN);
+			goto SKIP_PEER;
+		}
+
+		if ((body_len % 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "KaY: MKA Peer Packet Body Length (%d bytes) should multiple of 16 octets",
+				   (int) body_len);
+			goto SKIP_PEER;
+		}
+
+		for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+			peer_mi = MKA_HDR_LEN + pos + i;
+			os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+			peer_mn = be_to_host32(peer_mn);
+			if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0 &&
+			    peer_mn == participant->mn) {
+				included = TRUE;
+				break;
+			}
+		}
+
+		if (included)
+			return TRUE;
+
+SKIP_PEER:
+		left_len -= body_len + MKA_HDR_LEN;
+		pos += body_len + MKA_HDR_LEN;
+	}
+
+	return FALSE;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_live_peer_body -
+ */
+static int ieee802_1x_mka_decode_live_peer_body(
+	struct ieee802_1x_mka_participant *participant,
+	const u8 *peer_msg, size_t msg_len)
+{
+	const struct ieee802_1x_mka_hdr *hdr;
+	struct ieee802_1x_kay_peer *peer;
+	size_t body_len;
+	u32 peer_mn;
+	const u8 *peer_mi;
+	size_t i;
+	Boolean is_included;
+
+	is_included = ieee802_1x_kay_is_in_live_peer(
+		participant, participant->current_peer_id.mi);
+
+	hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
+	body_len = get_mka_param_body_len(hdr);
+
+	for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+		peer_mi = MKA_HDR_LEN + peer_msg + i;
+		os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+		peer_mn = be_to_host32(peer_mn);
+
+		/* it is myself */
+		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
+			/* My message id is used by other participant */
+			if (peer_mn > participant->mn) {
+				os_get_random(participant->mi,
+					      sizeof(participant->mi));
+				participant->mn = 0;
+			}
+			continue;
+		}
+		if (!is_included)
+			continue;
+
+		peer = ieee802_1x_kay_get_peer(participant, peer_mi);
+		if (NULL != peer) {
+			peer->mn = peer_mn;
+			peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+		} else {
+			if (!ieee802_1x_kay_create_potential_peer(
+				participant, peer_mi, peer_mn)) {
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_potential_peer_body -
+ */
+static int
+ieee802_1x_mka_decode_potential_peer_body(
+	struct ieee802_1x_mka_participant *participant,
+	const u8 *peer_msg, size_t msg_len)
+{
+	struct ieee802_1x_mka_hdr *hdr;
+	size_t body_len;
+	u32 peer_mn;
+	const u8 *peer_mi;
+	size_t i;
+
+	hdr = (struct ieee802_1x_mka_hdr *) peer_msg;
+	body_len = get_mka_param_body_len(hdr);
+
+	for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+		peer_mi = MKA_HDR_LEN + peer_msg + i;
+		os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+		peer_mn = be_to_host32(peer_mn);
+
+		/* it is myself */
+		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
+			/* My message id is used by other participant */
+			if (peer_mn > participant->mn) {
+				os_get_random(participant->mi,
+					      sizeof(participant->mi));
+				participant->mn = 0;
+			}
+			continue;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_sak_use_body_present
+ */
+static Boolean
+ieee802_1x_mka_sak_use_body_present(
+	struct ieee802_1x_mka_participant *participant)
+{
+	if (participant->to_use_sak)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+
+/**
+ * ieee802_1x_mka_get_sak_use_length
+ */
+static int
+ieee802_1x_mka_get_sak_use_length(
+	struct ieee802_1x_mka_participant *participant)
+{
+	int length = MKA_HDR_LEN;
+
+	if (participant->kay->macsec_desired && participant->advised_desired)
+		length = sizeof(struct ieee802_1x_mka_sak_use_body);
+	else
+		length = MKA_HDR_LEN;
+
+	length = (length + 0x3) & ~0x3;
+
+	return length;
+}
+
+
+/**
+ *
+ */
+static u32
+ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
+		       struct ieee802_1x_mka_ki *ki)
+{
+	struct receive_sa *rxsa;
+	struct receive_sc *rxsc;
+	u32 lpn = 0;
+
+	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+		dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
+		{
+			if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
+				secy_get_receive_lowest_pn(principal->kay,
+							   rxsa);
+
+				lpn = lpn > rxsa->lowest_pn ?
+					lpn : rxsa->lowest_pn;
+				break;
+			}
+		}
+	}
+
+	if (lpn == 0)
+		lpn = 1;
+
+	return lpn;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_sak_use_body -
+ */
+static int
+ieee802_1x_mka_encode_sak_use_body(
+	struct ieee802_1x_mka_participant *participant,
+	struct wpabuf *buf)
+{
+	struct ieee802_1x_mka_sak_use_body *body;
+	unsigned int length;
+	u32 pn = 1;
+
+	length = ieee802_1x_mka_get_sak_use_length(participant);
+	body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_sak_use_body));
+
+	body->type = MKA_SAK_USE;
+	set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+	if (length == MKA_HDR_LEN) {
+		body->ptx = TRUE;
+		body->prx = TRUE;
+		body->lan = 0;
+		body->lrx = FALSE;
+		body->ltx = FALSE;
+		body->delay_protect = FALSE;
+		return 0;
+	}
+
+	/* data protect, lowest accept packet number */
+	body->delay_protect = participant->kay->macsec_replay_protect;
+	pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
+	if (pn > participant->kay->pn_exhaustion) {
+		wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
+		if (participant->is_key_server)
+			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 */
+	if (participant->kay->macsec_protect)
+		body->ptx = FALSE;
+	else
+		body->ptx = TRUE;
+
+	if (participant->kay->macsec_validate == Strict)
+		body->prx = FALSE;
+	else
+		body->prx = TRUE;
+
+	/* latest key: rx, tx, key server member identifier key number */
+	body->lan = participant->lan;
+	os_memcpy(body->lsrv_mi, participant->lki.mi,
+		  sizeof(body->lsrv_mi));
+	body->lkn = host_to_be32(participant->lki.kn);
+	body->lrx = participant->lrx;
+	body->ltx = participant->ltx;
+
+	/* old key: rx, tx, key server member identifier key number */
+	body->oan = participant->oan;
+	if (participant->oki.kn != participant->lki.kn &&
+	    participant->oki.kn != 0) {
+		body->otx = TRUE;
+		body->orx = TRUE;
+		os_memcpy(body->osrv_mi, participant->oki.mi,
+			  sizeof(body->osrv_mi));
+		body->okn = host_to_be32(participant->oki.kn);
+	} else {
+		body->otx = FALSE;
+		body->orx = FALSE;
+	}
+
+	/* set CP's variable */
+	if (body->ltx) {
+		if (!participant->kay->tx_enable)
+			participant->kay->tx_enable = TRUE;
+
+		if (!participant->kay->port_enable)
+			participant->kay->port_enable = TRUE;
+	}
+	if (body->lrx) {
+		if (!participant->kay->rx_enable)
+			participant->kay->rx_enable = TRUE;
+	}
+
+	ieee802_1x_mka_dump_sak_use_body(body);
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_sak_use_body -
+ */
+static int
+ieee802_1x_mka_decode_sak_use_body(
+	struct ieee802_1x_mka_participant *participant,
+	const u8 *mka_msg, size_t msg_len)
+{
+	struct ieee802_1x_mka_hdr *hdr;
+	struct ieee802_1x_mka_sak_use_body *body;
+	struct ieee802_1x_kay_peer *peer;
+	struct transmit_sa *txsa;
+	struct data_key *sa_key = NULL;
+	size_t body_len;
+	struct ieee802_1x_mka_ki ki;
+	u32 lpn;
+	Boolean all_receiving;
+	Boolean founded;
+
+	if (!participant->principal) {
+		wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
+		return -1;
+	}
+	peer = ieee802_1x_kay_get_live_peer(participant,
+					    participant->current_peer_id.mi);
+	if (!peer) {
+		wpa_printf(MSG_WARNING, "KaY: the peer is not my live peer");
+		return -1;
+	}
+
+	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+	body_len = get_mka_param_body_len(hdr);
+	body = (struct ieee802_1x_mka_sak_use_body *) mka_msg;
+	ieee802_1x_mka_dump_sak_use_body(body);
+
+	if ((body_len != 0) && (body_len < 40)) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 40, or more octets",
+			   (int) body_len);
+		return -1;
+	}
+
+	/* TODO: what action should I take when peer does not support MACsec */
+	if (body_len == 0) {
+		wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec");
+		return 0;
+	}
+
+	/* TODO: when the plain tx or rx of peer is true, should I change
+	 * the attribute of controlled port
+	 */
+	if (body->prx)
+		wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE");
+
+	if (body->ptx)
+		wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
+
+	/* check latest key is valid */
+	if (body->ltx || body->lrx) {
+		founded = FALSE;
+		os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
+		ki.kn = ntohl(body->lkn);
+		dl_list_for_each(sa_key, &participant->sak_list,
+				 struct data_key, list) {
+			if (is_ki_equal(&sa_key->key_identifier, &ki)) {
+				founded = TRUE;
+				break;
+			}
+		}
+		if (!founded) {
+			wpa_printf(MSG_WARNING, "KaY: Latest key is invalid");
+			return -1;
+		}
+		if (os_memcmp(participant->lki.mi, body->lsrv_mi,
+			      sizeof(participant->lki.mi)) == 0 &&
+		    ntohl(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(
+				participant->kay->cp, TRUE);
+			ieee802_1x_cp_sm_step(participant->kay->cp);
+		}
+	}
+
+	/* check old key is valid */
+	if (body->otx || body->orx) {
+		if (os_memcmp(participant->oki.mi, body->osrv_mi,
+			      sizeof(participant->oki.mi)) != 0 ||
+		    ntohl(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 && (!ntohl(body->llpn) || !ntohl(body->olpn))) {
+		wpa_printf(MSG_WARNING,
+			   "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE");
+		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(participant->kay->cp, TRUE);
+		ieee802_1x_cp_sm_step(participant->kay->cp);
+	}
+
+	/* if i'm key server, and detects peer member pn exhaustion, rekey.*/
+	lpn = ntohl(body->llpn);
+	if (lpn > participant->kay->pn_exhaustion) {
+		if (participant->is_key_server) {
+			participant->new_sak = TRUE;
+			wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
+		}
+	}
+
+	founded = FALSE;
+	dl_list_for_each(txsa, &participant->txsc->sa_list,
+			 struct transmit_sa, list) {
+		if (sa_key != NULL && txsa->pkey == sa_key) {
+			founded = TRUE;
+			break;
+		}
+	}
+	if (!founded) {
+		wpa_printf(MSG_WARNING, "KaY: Can't find txsa");
+		return -1;
+	}
+
+	/* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
+	 * npn is larger than txsa's npn, set it to txsa.
+	 */
+	secy_get_transmit_next_pn(participant->kay, txsa);
+	if (lpn > txsa->next_pn) {
+		secy_set_transmit_next_pn(participant->kay, txsa);
+		wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn);
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_dist_sak_body_present
+ */
+static Boolean
+ieee802_1x_mka_dist_sak_body_present(
+	struct ieee802_1x_mka_participant *participant)
+{
+	if (!participant->to_dist_sak || !participant->new_key)
+		return FALSE;
+
+	return TRUE;
+}
+
+
+/**
+ * ieee802_1x_kay_get_dist_sak_length
+ */
+static int
+ieee802_1x_mka_get_dist_sak_length(
+	struct ieee802_1x_mka_participant *participant)
+{
+	int length;
+	int cs_index = participant->kay->macsec_csindex;
+
+	if (participant->advised_desired) {
+		length = sizeof(struct ieee802_1x_mka_dist_sak_body);
+		if (cs_index != DEFAULT_CS_INDEX)
+			length += CS_ID_LEN;
+
+		length += cipher_suite_tbl[cs_index].sak_len + 8;
+	} else {
+		length = MKA_HDR_LEN;
+	}
+	length = (length + 0x3) & ~0x3;
+
+	return length;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_dist_sak_body -
+ */
+static int
+ieee802_1x_mka_encode_dist_sak_body(
+	struct ieee802_1x_mka_participant *participant,
+	struct wpabuf *buf)
+{
+	struct ieee802_1x_mka_dist_sak_body *body;
+	struct data_key *sak;
+	unsigned int length;
+	int cs_index;
+	int sak_pos;
+
+	length = ieee802_1x_mka_get_dist_sak_length(participant);
+	body = wpabuf_put(buf, length);
+	body->type = MKA_DISTRIBUTED_SAK;
+	set_mka_param_body_len(body, length - MKA_HDR_LEN);
+	if (length == MKA_HDR_LEN) {
+		body->confid_offset = 0;
+		body->dan = 0;
+		return 0;
+	}
+
+	sak = participant->new_key;
+	body->confid_offset = sak->confidentiality_offset;
+	body->dan = sak->an;
+	body->kn = host_to_be32(sak->key_identifier.kn);
+	cs_index = participant->kay->macsec_csindex;
+	sak_pos = 0;
+	if (cs_index != DEFAULT_CS_INDEX) {
+		os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN);
+		sak_pos = CS_ID_LEN;
+	}
+	if (aes_wrap(participant->kek.key,
+		     cipher_suite_tbl[cs_index].sak_len / 8,
+		     sak->key, body->sak + sak_pos)) {
+		wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
+		return -1;
+	}
+
+	ieee802_1x_mka_dump_dist_sak_body(body);
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_init_data_key -
+ */
+static struct data_key *
+ieee802_1x_kay_init_data_key(const struct key_conf *conf)
+{
+	struct data_key *pkey;
+
+	if (!conf)
+		return NULL;
+
+	pkey = os_zalloc(sizeof(*pkey));
+	if (pkey == NULL) {
+		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+		return NULL;
+	}
+
+	pkey->key = os_zalloc(conf->key_len);
+	if (pkey->key == NULL) {
+		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+		os_free(pkey);
+		return NULL;
+	}
+
+	os_memcpy(pkey->key, conf->key, conf->key_len);
+	os_memcpy(&pkey->key_identifier, &conf->ki,
+		  sizeof(pkey->key_identifier));
+	pkey->confidentiality_offset = conf->offset;
+	pkey->an = conf->an;
+	pkey->transmits = conf->tx;
+	pkey->receives = conf->rx;
+	os_get_time(&pkey->created_time);
+
+	pkey->user = 1;
+
+	return pkey;
+}
+
+
+/**
+ * ieee802_1x_kay_decode_dist_sak_body -
+ */
+static int
+ieee802_1x_mka_decode_dist_sak_body(
+	struct ieee802_1x_mka_participant *participant,
+	const u8 *mka_msg, size_t msg_len)
+{
+	struct ieee802_1x_mka_hdr *hdr;
+	struct ieee802_1x_mka_dist_sak_body *body;
+	struct ieee802_1x_kay_peer *peer;
+	struct macsec_ciphersuite *cs;
+	size_t body_len;
+	struct key_conf *conf;
+	struct data_key *sa_key = NULL;
+	struct ieee802_1x_mka_ki sak_ki;
+	int sak_len;
+	u8 *wrap_sak;
+	u8 *unwrap_sak;
+
+	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+	body_len = get_mka_param_body_len(hdr);
+	if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 28, 36, or more octets",
+			   (int) body_len);
+		return -1;
+	}
+
+	if (!participant->principal) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: I can't accept the distributed SAK as I am not principal");
+		return -1;
+	}
+	if (participant->is_key_server) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: I can't accept the distributed SAK as myself is key server ");
+		return -1;
+	}
+	if (!participant->kay->macsec_desired ||
+	    participant->kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: I am not MACsec-desired or without MACsec capable");
+		return -1;
+	}
+
+	peer = ieee802_1x_kay_get_live_peer(participant,
+					    participant->current_peer_id.mi);
+	if (!peer) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: The key server is not in my live peers list");
+		return -1;
+	}
+	if (os_memcmp(&participant->kay->key_server_sci,
+		      &peer->sci, sizeof(struct ieee802_1x_mka_sci)) != 0) {
+		wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
+		return -1;
+	}
+	if (body_len == 0) {
+		participant->kay->authenticated = TRUE;
+		participant->kay->secured = FALSE;
+		participant->kay->failed = FALSE;
+		participant->advised_desired = FALSE;
+		ieee802_1x_cp_connect_authenticated(participant->kay->cp);
+		ieee802_1x_cp_sm_step(participant->kay->cp);
+		wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec");
+		participant->to_use_sak = TRUE;
+		return 0;
+	}
+	participant->advised_desired = TRUE;
+	participant->kay->authenticated = FALSE;
+	participant->kay->secured = TRUE;
+	participant->kay->failed = FALSE;
+	ieee802_1x_cp_connect_secure(participant->kay->cp);
+	ieee802_1x_cp_sm_step(participant->kay->cp);
+
+	body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
+	ieee802_1x_mka_dump_dist_sak_body(body);
+	dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list)
+	{
+		if (os_memcmp(sa_key->key_identifier.mi,
+			      participant->current_peer_id.mi, MI_LEN) == 0 &&
+		    sa_key->key_identifier.kn == be_to_host32(body->kn)) {
+			wpa_printf(MSG_WARNING, "KaY:The Key has installed");
+			return 0;
+		}
+	}
+	if (body_len == 28) {
+		sak_len = DEFAULT_SA_KEY_LEN;
+		wrap_sak =  body->sak;
+		participant->kay->macsec_csindex = DEFAULT_CS_INDEX;
+	} else {
+		cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak);
+		if (!cs) {
+			wpa_printf(MSG_ERROR,
+				   "KaY: I can't support the Cipher Suite advised by key server");
+			return -1;
+		}
+		sak_len = cs->sak_len;
+		wrap_sak = body->sak + CS_ID_LEN;
+		participant->kay->macsec_csindex = cs->index;
+	}
+
+	unwrap_sak = os_zalloc(sak_len);
+	if (!unwrap_sak) {
+		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+		return -1;
+	}
+	if (aes_unwrap(participant->kek.key, sak_len >> 3, wrap_sak,
+		       unwrap_sak)) {
+		wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
+		os_free(unwrap_sak);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len);
+
+	conf = os_zalloc(sizeof(*conf));
+	if (!conf) {
+		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+		os_free(unwrap_sak);
+		return -1;
+	}
+	conf->key_len = sak_len;
+
+	conf->key = os_zalloc(conf->key_len);
+	if (!conf->key) {
+		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+		os_free(unwrap_sak);
+		os_free(conf);
+		return -1;
+	}
+
+	os_memcpy(conf->key, unwrap_sak, conf->key_len);
+
+	os_memcpy(&sak_ki.mi, &participant->current_peer_id.mi,
+		  sizeof(sak_ki.mi));
+	sak_ki.kn = be_to_host32(body->kn);
+
+	os_memcpy(conf->ki.mi, sak_ki.mi, MI_LEN);
+	conf->ki.kn = sak_ki.kn;
+	conf->an = body->dan;
+	conf->offset = body->confid_offset;
+	conf->rx = TRUE;
+	conf->tx = TRUE;
+
+	sa_key = ieee802_1x_kay_init_data_key(conf);
+	if (!sa_key) {
+		os_free(unwrap_sak);
+		os_free(conf->key);
+		os_free(conf);
+		return -1;
+	}
+
+	dl_list_add(&participant->sak_list, &sa_key->list);
+
+	ieee802_1x_cp_set_ciphersuite(
+		participant->kay->cp,
+		cipher_suite_tbl[participant->kay->macsec_csindex].id);
+	ieee802_1x_cp_sm_step(participant->kay->cp);
+	ieee802_1x_cp_set_offset(participant->kay->cp, body->confid_offset);
+	ieee802_1x_cp_sm_step(participant->kay->cp);
+	ieee802_1x_cp_set_distributedki(participant->kay->cp, &sak_ki);
+	ieee802_1x_cp_set_distributedan(participant->kay->cp, body->dan);
+	ieee802_1x_cp_signal_newsak(participant->kay->cp);
+	ieee802_1x_cp_sm_step(participant->kay->cp);
+
+	participant->to_use_sak = TRUE;
+
+	os_free(unwrap_sak);
+	os_free(conf->key);
+	os_free(conf);
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_icv_body_present
+ */
+static Boolean
+ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant)
+{
+	return TRUE;
+}
+
+
+/**
+ * ieee802_1x_kay_get_icv_length
+ */
+static int
+ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
+{
+	int length;
+
+	length = sizeof(struct ieee802_1x_mka_icv_body);
+	length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
+
+	return (length + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_icv_body -
+ */
+static int
+ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
+			       struct wpabuf *buf)
+{
+	struct ieee802_1x_mka_icv_body *body;
+	unsigned int length;
+	u8 cmac[MAX_ICV_LEN];
+
+	length = ieee802_1x_mka_get_icv_length(participant);
+	if (length != DEFAULT_ICV_LEN)  {
+		body = wpabuf_put(buf, MKA_HDR_LEN);
+		body->type = MKA_ICV_INDICATOR;
+		set_mka_param_body_len(body, length - MKA_HDR_LEN);
+	}
+
+	if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
+		    participant->ick.key, wpabuf_head(buf), buf->used, cmac)) {
+		wpa_printf(MSG_ERROR, "KaY, omac1_aes_128 failed");
+		return -1;
+	}
+
+	if (length != DEFAULT_ICV_LEN)  {
+		os_memcpy(wpabuf_put(buf, length - MKA_HDR_LEN), cmac,
+			  length - MKA_HDR_LEN);
+	} else {
+		os_memcpy(wpabuf_put(buf, length), cmac, length);
+	}
+
+	return 0;
+}
+
+/**
+ * ieee802_1x_mka_decode_icv_body -
+ */
+static u8 *
+ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
+			       const u8 *mka_msg, size_t msg_len)
+{
+	struct ieee802_1x_mka_hdr *hdr;
+	struct ieee802_1x_mka_icv_body *body;
+	size_t body_len;
+	size_t left_len;
+	int body_type;
+	const u8 *pos;
+
+	pos = mka_msg;
+	left_len = msg_len;
+	while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
+		hdr = (struct ieee802_1x_mka_hdr *) pos;
+		body_len = get_mka_param_body_len(hdr);
+		body_type = get_mka_param_body_type(hdr);
+
+		if (left_len < (body_len + MKA_HDR_LEN))
+			break;
+
+		if (body_type != MKA_ICV_INDICATOR) {
+			left_len -= MKA_HDR_LEN + body_len;
+			pos += MKA_HDR_LEN + body_len;
+			continue;
+		}
+
+		body = (struct ieee802_1x_mka_icv_body *)pos;
+		if (body_len
+			< mka_alg_tbl[participant->kay->mka_algindex].icv_len) {
+			return NULL;
+		}
+
+		return body->icv;
+	}
+
+	return (u8 *) (mka_msg + msg_len - DEFAULT_ICV_LEN);
+}
+
+
+/**
+ * ieee802_1x_mka_decode_dist_cak_body-
+ */
+static int
+ieee802_1x_mka_decode_dist_cak_body(
+	struct ieee802_1x_mka_participant *participant,
+	const u8 *mka_msg, size_t msg_len)
+{
+	struct ieee802_1x_mka_hdr *hdr;
+	size_t body_len;
+
+	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+	body_len = get_mka_param_body_len(hdr);
+	if (body_len < 28) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 28 or more octets",
+			   (int) body_len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_kmd_body -
+ */
+static int
+ieee802_1x_mka_decode_kmd_body(
+	struct ieee802_1x_mka_participant *participant,
+	const u8 *mka_msg, size_t msg_len)
+{
+	struct ieee802_1x_mka_hdr *hdr;
+	size_t body_len;
+
+	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+	body_len = get_mka_param_body_len(hdr);
+	if (body_len < 5) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 5 or more octets",
+			   (int) body_len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_announce_body -
+ */
+static int ieee802_1x_mka_decode_announce_body(
+	struct ieee802_1x_mka_participant *participant,
+	const u8 *mka_msg, size_t msg_len)
+{
+	return 0;
+}
+
+
+static struct mka_param_body_handler mak_body_handler[] = {
+	/* basic parameter set */
+	{
+		ieee802_1x_mka_encode_basic_body,
+		NULL,
+		ieee802_1x_mka_basic_body_length,
+		ieee802_1x_mka_basic_body_present
+	},
+
+	/* live peer list parameter set */
+	{
+		ieee802_1x_mka_encode_live_peer_body,
+		ieee802_1x_mka_decode_live_peer_body,
+		ieee802_1x_mka_get_live_peer_length,
+		ieee802_1x_mka_live_peer_body_present
+	},
+
+	/* potential peer list parameter set */
+	{
+		ieee802_1x_mka_encode_potential_peer_body,
+		ieee802_1x_mka_decode_potential_peer_body,
+		ieee802_1x_mka_get_potential_peer_length,
+		ieee802_1x_mka_potential_peer_body_present
+	},
+
+	/* sak use parameter set */
+	{
+		ieee802_1x_mka_encode_sak_use_body,
+		ieee802_1x_mka_decode_sak_use_body,
+		ieee802_1x_mka_get_sak_use_length,
+		ieee802_1x_mka_sak_use_body_present
+	},
+
+	/* distribute sak parameter set */
+	{
+		ieee802_1x_mka_encode_dist_sak_body,
+		ieee802_1x_mka_decode_dist_sak_body,
+		ieee802_1x_mka_get_dist_sak_length,
+		ieee802_1x_mka_dist_sak_body_present
+	},
+
+	/* distribute cak parameter set */
+	{
+		NULL,
+		ieee802_1x_mka_decode_dist_cak_body,
+		NULL,
+		NULL
+	},
+
+	/* kmd parameter set */
+	{
+		NULL,
+		ieee802_1x_mka_decode_kmd_body,
+		NULL,
+		NULL
+	},
+
+	/* announce parameter set */
+	{
+		NULL,
+		ieee802_1x_mka_decode_announce_body,
+		NULL,
+		NULL
+	},
+
+	/* icv parameter set */
+	{
+		ieee802_1x_mka_encode_icv_body,
+		NULL,
+		ieee802_1x_mka_get_icv_length,
+		ieee802_1x_mka_icv_body_present
+	},
+};
+
+
+/**
+ * ieee802_1x_kay_deinit_data_key -
+ */
+void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
+{
+	if (!pkey)
+		return;
+
+	pkey->user--;
+	if (pkey->user > 1)
+		return;
+
+	dl_list_del(&pkey->list);
+	os_free(pkey->key);
+	os_free(pkey);
+}
+
+
+/**
+ * ieee802_1x_kay_generate_new_sak -
+ */
+static int
+ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
+{
+	struct data_key *sa_key = NULL;
+	struct key_conf *conf;
+	struct ieee802_1x_kay_peer *peer;
+	struct ieee802_1x_kay *kay = participant->kay;
+	int ctx_len, ctx_offset;
+	u8 *context;
+
+	/* check condition for generating a fresh SAK:
+	 * must have one live peer
+	 * and MKA life time elapse since last distribution
+	 * or potential peer is empty
+	 */
+	if (dl_list_empty(&participant->live_peers)) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: Live peers list must not empty when generating fresh SAK");
+		return -1;
+	}
+
+	/* FIXME: A fresh SAK not generated until
+	 * the live peer list contains at least one peer and
+	 * MKA life time has elapsed since the prior SAK was first distributed,
+	 * or the Key server's potential peer is empty
+	 * but I can't understand the second item, so
+	 * here only check first item and ingore
+	 *   && (!dl_list_empty(&participant->potential_peers))) {
+	 */
+	if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: Life time have not elapsed since prior SAK distributed");
+		return -1;
+	}
+
+	conf = os_zalloc(sizeof(*conf));
+	if (!conf) {
+		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+		return -1;
+	}
+	conf->key_len = cipher_suite_tbl[kay->macsec_csindex].sak_len;
+
+	conf->key = os_zalloc(conf->key_len);
+	if (!conf->key) {
+		os_free(conf);
+		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+		return -1;
+	}
+
+	ctx_len = conf->key_len + sizeof(kay->dist_kn);
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list)
+		ctx_len += sizeof(peer->mi);
+	ctx_len += sizeof(participant->mi);
+
+	context = os_zalloc(ctx_len);
+	if (!context) {
+		os_free(conf->key);
+		os_free(conf);
+		return -1;
+	}
+	ctx_offset = 0;
+	os_get_random(context + ctx_offset, conf->key_len);
+	ctx_offset += conf->key_len;
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
+		ctx_offset += sizeof(peer->mi);
+	}
+	os_memcpy(context + ctx_offset, participant->mi,
+		  sizeof(participant->mi));
+	ctx_offset += sizeof(participant->mi);
+	os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
+
+	if (conf->key_len == 16) {
+		ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
+						context, ctx_len, conf->key);
+	} else if (conf->key_len == 32) {
+		ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
+						context, ctx_len, conf->key);
+	} else {
+		wpa_printf(MSG_ERROR, "KaY: SAK Length not support");
+		os_free(conf->key);
+		os_free(conf);
+		os_free(context);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK",
+		    conf->key, conf->key_len);
+
+	os_memcpy(conf->ki.mi, participant->mi, MI_LEN);
+	conf->ki.kn = participant->kay->dist_kn;
+	conf->an = participant->kay->dist_an;
+	conf->offset = kay->macsec_confidentiality;
+	conf->rx = TRUE;
+	conf->tx = TRUE;
+
+	sa_key = ieee802_1x_kay_init_data_key(conf);
+	if (!sa_key) {
+		os_free(conf->key);
+		os_free(conf);
+		os_free(context);
+		return -1;
+	}
+	participant->new_key = sa_key;
+
+	dl_list_add(&participant->sak_list, &sa_key->list);
+	ieee802_1x_cp_set_ciphersuite(participant->kay->cp,
+				      cipher_suite_tbl[kay->macsec_csindex].id);
+	ieee802_1x_cp_sm_step(kay->cp);
+	ieee802_1x_cp_set_offset(kay->cp, conf->offset);
+	ieee802_1x_cp_sm_step(kay->cp);
+	ieee802_1x_cp_set_distributedki(kay->cp, &conf->ki);
+	ieee802_1x_cp_set_distributedan(kay->cp, conf->an);
+	ieee802_1x_cp_signal_newsak(kay->cp);
+	ieee802_1x_cp_sm_step(kay->cp);
+
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list)
+		peer->sak_used = FALSE;
+
+	participant->kay->dist_kn++;
+	participant->kay->dist_an++;
+	if (participant->kay->dist_an > 3)
+		participant->kay->dist_an = 0;
+
+	participant->kay->dist_time = time(NULL);
+
+	os_free(conf->key);
+	os_free(conf);
+	os_free(context);
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_elect_key_server - elect the key server
+ * when to elect: whenever the live peers list changes
+ */
+static int
+ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
+{
+	struct ieee802_1x_kay_peer *peer;
+	struct ieee802_1x_kay_peer *key_server = NULL;
+	struct ieee802_1x_kay *kay = participant->kay;
+	Boolean i_is_key_server;
+	int i;
+
+	if (participant->is_obliged_key_server) {
+		participant->new_sak = TRUE;
+		participant->to_dist_sak = FALSE;
+		ieee802_1x_cp_set_electedself(kay->cp, TRUE);
+		return 0;
+	}
+
+	/* elect the key server among the peers */
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		if (!peer->is_key_server)
+			continue;
+
+		if (!key_server) {
+			key_server = peer;
+			continue;
+		}
+
+		if (peer->key_server_priority <
+		    key_server->key_server_priority) {
+			key_server = peer;
+		} else if (peer->key_server_priority ==
+			   key_server->key_server_priority) {
+			for (i = 0; i < 6; i++) {
+				if (peer->sci.addr[i] <
+				    key_server->sci.addr[i])
+					key_server = peer;
+			}
+		}
+	}
+
+	/* elect the key server between me and the above elected peer */
+	i_is_key_server = FALSE;
+	if (key_server && participant->can_be_key_server) {
+		if (kay->actor_priority
+			   < key_server->key_server_priority) {
+			i_is_key_server = TRUE;
+		} else if (kay->actor_priority
+					== key_server->key_server_priority) {
+			for (i = 0; i < 6; i++) {
+				if (kay->actor_sci.addr[i]
+					< key_server->sci.addr[i]) {
+					i_is_key_server = TRUE;
+				}
+			}
+		}
+	}
+
+	if (!key_server && !i_is_key_server) {
+		participant->principal = FALSE;
+		participant->is_key_server = FALSE;
+		participant->is_elected = FALSE;
+		return 0;
+	}
+
+	if (i_is_key_server) {
+		ieee802_1x_cp_set_electedself(kay->cp, TRUE);
+		if (os_memcmp(&kay->key_server_sci, &kay->actor_sci,
+			      sizeof(kay->key_server_sci))) {
+			ieee802_1x_cp_signal_chgdserver(kay->cp);
+			ieee802_1x_cp_sm_step(kay->cp);
+		}
+
+		participant->is_key_server = TRUE;
+		participant->principal = TRUE;
+		participant->new_sak = TRUE;
+		wpa_printf(MSG_DEBUG, "KaY: I is elected as key server");
+		participant->to_dist_sak = FALSE;
+		participant->is_elected = TRUE;
+
+		os_memcpy(&kay->key_server_sci, &kay->actor_sci,
+			  sizeof(kay->key_server_sci));
+		kay->key_server_priority = kay->actor_priority;
+	}
+
+	if (key_server) {
+		ieee802_1x_cp_set_electedself(kay->cp, FALSE);
+		if (os_memcmp(&kay->key_server_sci, &key_server->sci,
+			      sizeof(kay->key_server_sci))) {
+			ieee802_1x_cp_signal_chgdserver(kay->cp);
+			ieee802_1x_cp_sm_step(kay->cp);
+		}
+
+		participant->is_key_server = FALSE;
+		participant->principal = TRUE;
+		participant->is_elected = TRUE;
+
+		os_memcpy(&kay->key_server_sci, &key_server->sci,
+			  sizeof(kay->key_server_sci));
+		kay->key_server_priority = key_server->key_server_priority;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_decide_macsec_use - the key server determinate
+ *		 how to use MACsec: whether use MACsec and its capability
+ * protectFrames will be advised if the key server and one of its live peers are
+ * MACsec capable and one of those request MACsec protection
+ */
+static int
+ieee802_1x_kay_decide_macsec_use(
+	struct ieee802_1x_mka_participant *participant)
+{
+	struct ieee802_1x_kay *kay = participant->kay;
+	struct ieee802_1x_kay_peer *peer;
+	enum macsec_cap less_capability;
+	Boolean has_peer;
+
+	if (!participant->is_key_server)
+		return -1;
+
+	/* key server self is MACsec-desired and requesting MACsec */
+	if (!kay->macsec_desired) {
+		participant->advised_desired = FALSE;
+		return -1;
+	}
+	if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
+		participant->advised_desired = FALSE;
+		return -1;
+	}
+	less_capability = kay->macsec_capable;
+
+	/* at least one of peers is MACsec-desired and requesting MACsec */
+	has_peer = FALSE;
+	dl_list_for_each(peer, &participant->live_peers,
+			 struct ieee802_1x_kay_peer, list) {
+		if (!peer->macsec_desired)
+			continue;
+
+		if (peer->macsec_capbility == MACSEC_CAP_NOT_IMPLEMENTED)
+			continue;
+
+		less_capability = (less_capability < peer->macsec_capbility) ?
+			less_capability : peer->macsec_capbility;
+		has_peer = TRUE;
+	}
+
+	if (has_peer) {
+		participant->advised_desired = TRUE;
+		participant->advised_capability = less_capability;
+		kay->authenticated = FALSE;
+		kay->secured = TRUE;
+		kay->failed = FALSE;
+		ieee802_1x_cp_connect_secure(kay->cp);
+		ieee802_1x_cp_sm_step(kay->cp);
+	} else {
+		participant->advised_desired = FALSE;
+		participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED;
+		participant->to_use_sak = FALSE;
+		kay->authenticated = TRUE;
+		kay->secured = FALSE;
+		kay->failed = FALSE;
+		kay->ltx_kn = 0;
+		kay->ltx_an = 0;
+		kay->lrx_kn = 0;
+		kay->lrx_an = 0;
+		kay->otx_kn = 0;
+		kay->otx_an = 0;
+		kay->orx_kn = 0;
+		kay->orx_an = 0;
+		ieee802_1x_cp_connect_authenticated(kay->cp);
+		ieee802_1x_cp_sm_step(kay->cp);
+	}
+
+	return 0;
+}
+
+static const u8 pae_group_addr[ETH_ALEN] = {
+	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
+};
+
+
+/**
+ * ieee802_1x_kay_encode_mkpdu -
+ */
+static int
+ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
+			    struct wpabuf *pbuf)
+{
+	unsigned int i;
+	struct ieee8023_hdr *ether_hdr;
+	struct ieee802_1x_hdr *eapol_hdr;
+
+	ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
+	os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
+	os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
+		  sizeof(ether_hdr->dest));
+	ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
+
+	eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
+	eapol_hdr->version = EAPOL_VERSION;
+	eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
+	eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used);
+
+	for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
+		if (mak_body_handler[i].body_present &&
+		    mak_body_handler[i].body_present(participant)) {
+			if (mak_body_handler[i].body_tx(participant, pbuf))
+				return -1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * ieee802_1x_participant_send_mkpdu -
+ */
+static int
+ieee802_1x_participant_send_mkpdu(
+	struct ieee802_1x_mka_participant *participant)
+{
+	struct wpabuf *buf;
+	struct ieee802_1x_kay *kay = participant->kay;
+	size_t length = 0;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU");
+	length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
+	for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
+		if (mak_body_handler[i].body_present &&
+		    mak_body_handler[i].body_present(participant))
+			length += mak_body_handler[i].body_length(participant);
+	}
+
+	buf = wpabuf_alloc(length);
+	if (!buf) {
+		wpa_printf(MSG_ERROR, "KaY: out of memory");
+		return -1;
+	}
+
+	if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
+		wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail!");
+		return -1;
+	}
+
+	l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+
+	kay->active = TRUE;
+	participant->active = TRUE;
+
+	return 0;
+}
+
+
+static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
+/**
+ * ieee802_1x_participant_timer -
+ */
+static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct ieee802_1x_mka_participant *participant;
+	struct ieee802_1x_kay *kay;
+	struct ieee802_1x_kay_peer *peer, *pre_peer;
+	time_t now = time(NULL);
+	Boolean lp_changed;
+	struct receive_sc *rxsc, *pre_rxsc;
+	struct transmit_sa *txsa, *pre_txsa;
+
+	participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
+	kay = participant->kay;
+	if (participant->cak_life) {
+		if (now > participant->cak_life) {
+			kay->authenticated = FALSE;
+			kay->secured = FALSE;
+			kay->failed = TRUE;
+			ieee802_1x_kay_delete_mka(kay, &participant->ckn);
+			return;
+		}
+	}
+
+	/* should delete MKA instance if there are not live peers
+	 * when the MKA life elapsed since its creating */
+	if (participant->mka_life) {
+		if (dl_list_empty(&participant->live_peers)) {
+			if (now > participant->mka_life) {
+				kay->authenticated = FALSE;
+				kay->secured = FALSE;
+				kay->failed = TRUE;
+				ieee802_1x_kay_delete_mka(kay,
+							  &participant->ckn);
+				return;
+			}
+		} else {
+			participant->mka_life = 0;
+		}
+	}
+
+	lp_changed = FALSE;
+	dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
+			      struct ieee802_1x_kay_peer, list) {
+		if (now > peer->expire) {
+			wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
+			wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
+				    sizeof(peer->mi));
+			wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+			dl_list_for_each_safe(rxsc, pre_rxsc,
+					      &participant->rxsc_list,
+					      struct receive_sc, list) {
+				if (os_memcmp(&rxsc->sci, &peer->sci,
+					      sizeof(rxsc->sci)) == 0) {
+					secy_delete_receive_sc(kay, rxsc);
+					ieee802_1x_kay_deinit_receive_sc(
+						participant, rxsc);
+				}
+			}
+			dl_list_del(&peer->list);
+			os_free(peer);
+			lp_changed = TRUE;
+		}
+	}
+
+	if (lp_changed) {
+		if (dl_list_empty(&participant->live_peers)) {
+			participant->advised_desired = FALSE;
+			participant->advised_capability =
+				MACSEC_CAP_NOT_IMPLEMENTED;
+			participant->to_use_sak = FALSE;
+			kay->authenticated = TRUE;
+			kay->secured = FALSE;
+			kay->failed = FALSE;
+			kay->ltx_kn = 0;
+			kay->ltx_an = 0;
+			kay->lrx_kn = 0;
+			kay->lrx_an = 0;
+			kay->otx_kn = 0;
+			kay->otx_an = 0;
+			kay->orx_kn = 0;
+			kay->orx_an = 0;
+			dl_list_for_each_safe(txsa, pre_txsa,
+					      &participant->txsc->sa_list,
+					      struct transmit_sa, list) {
+				secy_disable_transmit_sa(kay, txsa);
+				ieee802_1x_kay_deinit_transmit_sa(txsa);
+			}
+
+			ieee802_1x_cp_connect_authenticated(kay->cp);
+			ieee802_1x_cp_sm_step(kay->cp);
+		} else {
+			ieee802_1x_kay_elect_key_server(participant);
+			ieee802_1x_kay_decide_macsec_use(participant);
+		}
+	}
+
+	dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
+			      struct ieee802_1x_kay_peer, list) {
+		if (now > peer->expire) {
+			wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
+			wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
+				    sizeof(peer->mi));
+			wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+			dl_list_del(&peer->list);
+			os_free(peer);
+		}
+	}
+
+	if (participant->new_sak) {
+		if (!ieee802_1x_kay_generate_new_sak(participant))
+			participant->to_dist_sak = TRUE;
+
+		participant->new_sak = FALSE;
+	}
+
+	if (participant->retry_count < MAX_RETRY_CNT) {
+		ieee802_1x_participant_send_mkpdu(participant);
+		participant->retry_count++;
+	}
+
+	eloop_register_timeout(MKA_HELLO_TIME / 1000, 0,
+			       ieee802_1x_participant_timer,
+			       participant, NULL);
+}
+
+
+/**
+ * ieee802_1x_kay_init_transmit_sa -
+ */
+static struct transmit_sa *
+ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
+				struct data_key *key)
+{
+	struct transmit_sa *psa;
+
+	key->tx_latest = TRUE;
+	key->rx_latest = TRUE;
+
+	psa = os_zalloc(sizeof(*psa));
+	if (!psa) {
+		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+		return NULL;
+	}
+
+	if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 &&
+	    key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50)
+		psa->confidentiality = TRUE;
+	else
+		psa->confidentiality = FALSE;
+
+	psa->an = an;
+	psa->pkey = key;
+	psa->next_pn = next_PN;
+	psa->sc = psc;
+
+	os_get_time(&psa->created_time);
+	psa->in_use = FALSE;
+
+	dl_list_add(&psc->sa_list, &psa->list);
+	wpa_printf(MSG_DEBUG,
+		   "KaY: Create transmit SA(an: %d, next_PN: %u) of SC(channel: %d)",
+		   (int) an, next_PN, psc->channel);
+
+	return psa;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_transmit_sa -
+ */
+static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
+{
+	psa->pkey = NULL;
+	wpa_printf(MSG_DEBUG,
+		   "KaY: Delete transmit SA(an: %d) of SC(channel: %d)",
+		   psa->an, psa->sc->channel);
+	dl_list_del(&psa->list);
+	os_free(psa);
+}
+
+
+/**
+ * init_transmit_sc -
+ */
+static struct transmit_sc *
+ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
+				int channel)
+{
+	struct transmit_sc *psc;
+
+	psc = os_zalloc(sizeof(*psc));
+	if (!psc) {
+		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+		return NULL;
+	}
+	os_memcpy(&psc->sci, sci, sizeof(psc->sci));
+	psc->channel = channel;
+
+	os_get_time(&psc->created_time);
+	psc->transmitting = FALSE;
+	psc->encoding_sa = FALSE;
+	psc->enciphering_sa = FALSE;
+
+	dl_list_init(&psc->sa_list);
+	wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel);
+	wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci));
+
+	return psc;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_transmit_sc -
+ */
+static void
+ieee802_1x_kay_deinit_transmit_sc(
+	struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc)
+{
+	struct transmit_sa *psa, *tmp;
+
+	wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)",
+		   psc->channel);
+	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa,
+			      list) {
+		secy_disable_transmit_sa(participant->kay, psa);
+		ieee802_1x_kay_deinit_transmit_sa(psa);
+	}
+
+	os_free(psc);
+}
+
+
+/****************** Interface between CP and KAY *********************/
+/**
+ * ieee802_1x_kay_set_latest_sa_attr -
+ */
+int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
+				      struct ieee802_1x_mka_ki *lki, u8 lan,
+				      Boolean ltx, Boolean lrx)
+{
+	struct ieee802_1x_mka_participant *principal;
+
+	principal = ieee802_1x_kay_get_principal_participant(kay);
+	if (!principal)
+		return -1;
+
+	if (!lki)
+		os_memset(&principal->lki, 0, sizeof(principal->lki));
+	else
+		os_memcpy(&principal->lki, lki, sizeof(principal->lki));
+
+	principal->lan = lan;
+	principal->ltx = ltx;
+	principal->lrx = lrx;
+	if (!lki) {
+		kay->ltx_kn = 0;
+		kay->lrx_kn = 0;
+	} else {
+		kay->ltx_kn = lki->kn;
+		kay->lrx_kn = lki->kn;
+	}
+	kay->ltx_an = lan;
+	kay->lrx_an = lan;
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_set_old_sa_attr -
+ */
+int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
+				   struct ieee802_1x_mka_ki *oki,
+				   u8 oan, Boolean otx, Boolean orx)
+{
+	struct ieee802_1x_mka_participant *principal;
+
+	principal = ieee802_1x_kay_get_principal_participant(kay);
+	if (!principal)
+		return -1;
+
+	if (!oki)
+		os_memset(&principal->oki, 0, sizeof(principal->oki));
+	else
+		os_memcpy(&principal->oki, oki, sizeof(principal->oki));
+
+	principal->oan = oan;
+	principal->otx = otx;
+	principal->orx = orx;
+
+	if (!oki) {
+		kay->otx_kn = 0;
+		kay->orx_kn = 0;
+	} else {
+		kay->otx_kn = oki->kn;
+		kay->orx_kn = oki->kn;
+	}
+	kay->otx_an = oan;
+	kay->orx_an = oan;
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_create_sas -
+ */
+int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
+			      struct ieee802_1x_mka_ki *lki)
+{
+	struct data_key *sa_key, *latest_sak;
+	struct ieee802_1x_mka_participant *principal;
+	struct receive_sc *rxsc;
+	struct receive_sa *rxsa;
+	struct transmit_sa *txsa;
+
+	principal = ieee802_1x_kay_get_principal_participant(kay);
+	if (!principal)
+		return -1;
+
+	latest_sak = NULL;
+	dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) {
+		if (is_ki_equal(&sa_key->key_identifier, lki)) {
+			sa_key->rx_latest = TRUE;
+			sa_key->tx_latest = TRUE;
+			latest_sak = sa_key;
+			principal->to_use_sak = TRUE;
+		} else {
+			sa_key->rx_latest = FALSE;
+			sa_key->tx_latest = FALSE;
+		}
+	}
+	if (!latest_sak) {
+		wpa_printf(MSG_ERROR, "lki related sak not found");
+		return -1;
+	}
+
+	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+		rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
+						      latest_sak);
+		if (!rxsa)
+			return -1;
+
+		secy_create_receive_sa(kay, rxsa);
+	}
+
+	txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
+					       1, latest_sak);
+	if (!txsa)
+		return -1;
+
+	secy_create_transmit_sa(kay, txsa);
+
+
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_delete_sas -
+ */
+int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
+			      struct ieee802_1x_mka_ki *ki)
+{
+	struct data_key *sa_key, *pre_key;
+	struct transmit_sa *txsa, *pre_txsa;
+	struct receive_sa *rxsa, *pre_rxsa;
+	struct receive_sc *rxsc;
+	struct ieee802_1x_mka_participant *principal;
+
+	wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__);
+	principal = ieee802_1x_kay_get_principal_participant(kay);
+	if (!principal)
+		return -1;
+
+	/* remove the transmit sa */
+	dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
+			      struct transmit_sa, list) {
+		if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
+			secy_disable_transmit_sa(kay, txsa);
+			ieee802_1x_kay_deinit_transmit_sa(txsa);
+		}
+	}
+
+	/* remove the receive sa */
+	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+		dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
+				      struct receive_sa, list) {
+			if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
+				secy_disable_receive_sa(kay, rxsa);
+				ieee802_1x_kay_deinit_receive_sa(rxsa);
+			}
+		}
+	}
+
+	/* remove the sak */
+	dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
+			      struct data_key, list) {
+		if (is_ki_equal(&sa_key->key_identifier, ki)) {
+			ieee802_1x_kay_deinit_data_key(sa_key);
+			break;
+		}
+		if (principal->new_key == sa_key)
+			principal->new_key = NULL;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_tx_sas -
+ */
+int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
+				 struct ieee802_1x_mka_ki *lki)
+{
+	struct ieee802_1x_mka_participant *principal;
+	struct transmit_sa *txsa;
+
+	principal = ieee802_1x_kay_get_principal_participant(kay);
+	if (!principal)
+		return -1;
+
+	dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa,
+			 list) {
+		if (is_ki_equal(&txsa->pkey->key_identifier, lki)) {
+			txsa->in_use = TRUE;
+			secy_enable_transmit_sa(kay, txsa);
+			ieee802_1x_cp_set_usingtransmitas(
+				principal->kay->cp, TRUE);
+			ieee802_1x_cp_sm_step(principal->kay->cp);
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_rx_sas -
+ */
+int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
+				 struct ieee802_1x_mka_ki *lki)
+{
+	struct ieee802_1x_mka_participant *principal;
+	struct receive_sa *rxsa;
+	struct receive_sc *rxsc;
+
+	principal = ieee802_1x_kay_get_principal_participant(kay);
+	if (!principal)
+		return -1;
+
+	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+		dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
+		{
+			if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) {
+				rxsa->in_use = TRUE;
+				secy_enable_receive_sa(kay, rxsa);
+				ieee802_1x_cp_set_usingreceivesas(
+					principal->kay->cp, TRUE);
+				ieee802_1x_cp_sm_step(principal->kay->cp);
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_new_info -
+ */
+int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
+{
+	struct ieee802_1x_mka_participant *principal;
+
+	principal = ieee802_1x_kay_get_principal_participant(kay);
+	if (!principal)
+		return -1;
+
+	if (principal->retry_count < MAX_RETRY_CNT) {
+		ieee802_1x_participant_send_mkpdu(principal);
+		principal->retry_count++;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_cp_conf -
+ */
+int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
+			   struct ieee802_1x_cp_conf *pconf)
+{
+	pconf->protect = kay->macsec_protect;
+	pconf->replay_protect = kay->macsec_replay_protect;
+	pconf->validate = kay->macsec_validate;
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_alloc_cp_sm -
+ */
+static struct ieee802_1x_cp_sm *
+ieee802_1x_kay_alloc_cp_sm(struct ieee802_1x_kay *kay)
+{
+	struct ieee802_1x_cp_conf conf;
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.protect = kay->macsec_protect;
+	conf.replay_protect = kay->macsec_replay_protect;
+	conf.validate = kay->macsec_validate;
+	conf.replay_window = kay->macsec_replay_window;
+
+	return ieee802_1x_cp_sm_init(kay, &conf);
+}
+
+
+/**
+ * ieee802_1x_kay_mkpdu_sanity_check -
+ *     sanity check specified in clause 11.11.2 of IEEE802.1X-2010
+ */
+static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
+					     const u8 *buf, size_t len)
+{
+	struct ieee8023_hdr *eth_hdr;
+	struct ieee802_1x_hdr *eapol_hdr;
+	struct ieee802_1x_mka_hdr *mka_hdr;
+	struct ieee802_1x_mka_basic_body *body;
+	size_t mka_msg_len;
+	struct ieee802_1x_mka_participant *participant;
+	size_t body_len;
+	u8 icv[MAX_ICV_LEN];
+	u8 *msg_icv;
+
+	eth_hdr = (struct ieee8023_hdr *) buf;
+	eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
+	mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
+
+	/* destination address should be not individual address */
+	if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_MSGDUMP,
+			   "KaY: ethernet destination address is not PAE group address");
+		return -1;
+	}
+
+	/* MKPDU should not less than 32 octets */
+	mka_msg_len = be_to_host16(eapol_hdr->length);
+	if (mka_msg_len < 32) {
+		wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets");
+		return -1;
+	}
+	/* MKPDU should multiple 4 octets */
+	if ((mka_msg_len % 4) != 0) {
+		wpa_printf(MSG_MSGDUMP,
+			   "KaY: MKPDU is not multiple of 4 octets");
+		return -1;
+	}
+
+	body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
+	ieee802_1x_mka_dump_basic_body(body);
+	body_len = get_mka_param_body_len(body);
+	/* EAPOL-MKA body should comprise basic parameter set and ICV */
+	if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: Received EAPOL-MKA Packet Body Length (%d bytes) is less than the Basic Parameter Set Header Length (%d bytes) + the Basic Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+			   (int) mka_msg_len, (int) MKA_HDR_LEN,
+			   (int) body_len, DEFAULT_ICV_LEN);
+		return -1;
+	}
+
+	/* CKN should be owned by I */
+	participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+	if (!participant) {
+		wpa_printf(MSG_DEBUG, "CKN is not included in my CA");
+		return -1;
+	}
+
+	/* algorithm agility check */
+	if (os_memcmp(body->algo_agility, mka_algo_agility,
+		      sizeof(body->algo_agility)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: peer's algorithm agility not supported for me");
+		return -1;
+	}
+
+	/* ICV check */
+	/*
+	 * The ICV will comprise the final octets of the packet body, whatever
+	 * its size, not the fixed length 16 octets, indicated by the EAPOL
+	 * packet body length.
+	 */
+	if (mka_alg_tbl[kay->mka_algindex].icv_hash(
+		    participant->ick.key,
+		    buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
+		wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed");
+		return -1;
+	}
+	msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr,
+						 mka_msg_len);
+
+	if (msg_icv) {
+		if (os_memcmp_const(msg_icv, icv,
+				    mka_alg_tbl[kay->mka_algindex].icv_len) !=
+		    0) {
+			wpa_printf(MSG_ERROR,
+				   "KaY: Computed ICV is not equal to Received ICV");
+		return -1;
+		}
+	} else {
+		wpa_printf(MSG_ERROR, "KaY: No ICV");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_decode_mkpdu -
+ */
+static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
+				       const u8 *buf, size_t len)
+{
+	struct ieee802_1x_mka_participant *participant;
+	struct ieee802_1x_mka_hdr *hdr;
+	size_t body_len;
+	size_t left_len;
+	int body_type;
+	int i;
+	const u8 *pos;
+	Boolean my_included;
+	Boolean handled[256];
+
+	if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
+		return -1;
+
+	/* handle basic parameter set */
+	pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr);
+	left_len = len - sizeof(struct ieee8023_hdr) -
+		sizeof(struct ieee802_1x_hdr);
+	participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len);
+	if (!participant)
+		return -1;
+
+	/* to skip basic parameter set */
+	hdr = (struct ieee802_1x_mka_hdr *) pos;
+	body_len = get_mka_param_body_len(hdr);
+	pos += body_len + MKA_HDR_LEN;
+	left_len -= body_len + MKA_HDR_LEN;
+
+	/* check i am in the peer's peer list */
+	my_included = ieee802_1x_mka_i_in_peerlist(participant, pos, left_len);
+	if (my_included) {
+		/* accept the peer as live peer */
+		if (!ieee802_1x_kay_is_in_peer(
+			    participant,
+			    participant->current_peer_id.mi)) {
+			if (!ieee802_1x_kay_create_live_peer(
+				    participant,
+				    participant->current_peer_id.mi,
+				    participant->current_peer_id.mn))
+				return -1;
+			ieee802_1x_kay_elect_key_server(participant);
+			ieee802_1x_kay_decide_macsec_use(participant);
+		}
+		if (ieee802_1x_kay_is_in_potential_peer(
+			    participant, participant->current_peer_id.mi)) {
+			ieee802_1x_kay_move_live_peer(
+				participant, participant->current_peer_id.mi,
+				participant->current_peer_id.mn);
+			ieee802_1x_kay_elect_key_server(participant);
+			ieee802_1x_kay_decide_macsec_use(participant);
+		}
+	}
+
+	/*
+	 * Handle other parameter set than basic parameter set.
+	 * Each parameter set should be present only once.
+	 */
+	for (i = 0; i < 256; i++)
+		handled[i] = FALSE;
+
+	handled[0] = TRUE;
+	while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
+		hdr = (struct ieee802_1x_mka_hdr *) pos;
+		body_len = get_mka_param_body_len(hdr);
+		body_type = get_mka_param_body_type(hdr);
+
+		if (body_type == MKA_ICV_INDICATOR)
+			return 0;
+
+		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+			wpa_printf(MSG_ERROR,
+				   "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+				   (int) left_len, (int) MKA_HDR_LEN,
+				   (int) body_len, DEFAULT_ICV_LEN);
+			goto next_para_set;
+		}
+
+		if (handled[body_type])
+			goto next_para_set;
+
+		handled[body_type] = TRUE;
+		if (mak_body_handler[body_type].body_rx) {
+			mak_body_handler[body_type].body_rx
+				(participant, pos, left_len);
+		} else {
+			wpa_printf(MSG_ERROR,
+				   "The type %d not supported in this MKA version %d",
+				   body_type, MKA_VERSION_ID);
+		}
+
+next_para_set:
+		pos += body_len + MKA_HDR_LEN;
+		left_len -= body_len + MKA_HDR_LEN;
+	}
+
+	kay->active = TRUE;
+	participant->retry_count = 0;
+	participant->active = TRUE;
+
+	return 0;
+}
+
+
+
+static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+			   size_t len)
+{
+	struct ieee802_1x_kay *kay = ctx;
+	struct ieee8023_hdr *eth_hdr;
+	struct ieee802_1x_hdr *eapol_hdr;
+
+	/* must contain at least ieee8023_hdr + ieee802_1x_hdr */
+	if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
+		wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	eth_hdr = (struct ieee8023_hdr *) buf;
+	eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
+	if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
+	    ntohs(eapol_hdr->length)) {
+		wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)",
+			   (unsigned long) len,
+			   (unsigned long) ntohs(eapol_hdr->length));
+		return;
+	}
+
+	if (eapol_hdr->version < EAPOL_VERSION) {
+		wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
+			   eapol_hdr->version);
+		return;
+	}
+	if (ntohs(eth_hdr->ethertype) != ETH_P_PAE ||
+	    eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
+		return;
+
+	wpa_hexdump(MSG_DEBUG, "RX EAPOL-MKA: ", buf, len);
+	if (dl_list_empty(&kay->participant_list)) {
+		wpa_printf(MSG_ERROR, "KaY: no MKA participant instance");
+		return;
+	}
+
+	ieee802_1x_kay_decode_mkpdu(kay, buf, len);
+}
+
+
+/**
+ * ieee802_1x_kay_init -
+ */
+struct ieee802_1x_kay *
+ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+		    const char *ifname, const u8 *addr)
+{
+	struct ieee802_1x_kay *kay;
+
+	kay = os_zalloc(sizeof(*kay));
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+		return NULL;
+	}
+
+	kay->ctx = ctx;
+
+	kay->enable = TRUE;
+	kay->active = FALSE;
+
+	kay->authenticated = FALSE;
+	kay->secured = FALSE;
+	kay->failed = FALSE;
+	kay->policy = policy;
+
+	os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
+	os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
+	kay->actor_sci.port = 0x0001;
+	kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+
+	/* While actor acts as a key server, shall distribute sakey */
+	kay->dist_kn = 1;
+	kay->dist_an = 0;
+	kay->dist_time = 0;
+
+	kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
+	kay->macsec_csindex = DEFAULT_CS_INDEX;
+	kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
+	kay->mka_version = MKA_VERSION_ID;
+
+	os_memcpy(kay->algo_agility, mka_algo_agility,
+		  sizeof(kay->algo_agility));
+
+	dl_list_init(&kay->participant_list);
+
+	if (policy == DO_NOT_SECURE) {
+		kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
+		kay->macsec_desired = FALSE;
+		kay->macsec_protect = FALSE;
+		kay->macsec_validate = FALSE;
+		kay->macsec_replay_protect = FALSE;
+		kay->macsec_replay_window = 0;
+		kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
+	} else {
+		kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+		kay->macsec_desired = TRUE;
+		kay->macsec_protect = TRUE;
+		kay->macsec_validate = TRUE;
+		kay->macsec_replay_protect = FALSE;
+		kay->macsec_replay_window = 0;
+		kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+	}
+
+	wpa_printf(MSG_DEBUG, "KaY: state machine created");
+
+	/* Initialize the SecY must be prio to CP, as CP will control SecY */
+	secy_init_macsec(kay);
+	secy_get_available_transmit_sc(kay, &kay->sc_ch);
+
+	wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
+
+	/* init CP */
+	kay->cp = ieee802_1x_kay_alloc_cp_sm(kay);
+	if (kay->cp == NULL) {
+		ieee802_1x_kay_deinit(kay);
+		return NULL;
+	}
+
+	if (policy == DO_NOT_SECURE) {
+		ieee802_1x_cp_connect_authenticated(kay->cp);
+		ieee802_1x_cp_sm_step(kay->cp);
+	} else {
+		kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE,
+					     kay_l2_receive, kay, 1);
+		if (kay->l2_mka == NULL) {
+			wpa_printf(MSG_WARNING,
+				   "KaY: Failed to initialize L2 packet processing for MKA packet");
+			ieee802_1x_kay_deinit(kay);
+			return NULL;
+		}
+	}
+
+	return kay;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit -
+ */
+void
+ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
+{
+	struct ieee802_1x_mka_participant *participant;
+
+	if (!kay)
+		return;
+
+	wpa_printf(MSG_DEBUG, "KaY: state machine removed");
+
+	while (!dl_list_empty(&kay->participant_list)) {
+		participant = dl_list_entry(kay->participant_list.next,
+					    struct ieee802_1x_mka_participant,
+					    list);
+		ieee802_1x_kay_delete_mka(kay, &participant->ckn);
+	}
+
+	ieee802_1x_cp_sm_deinit(kay->cp);
+	secy_deinit_macsec(kay);
+
+	if (kay->l2_mka) {
+		l2_packet_deinit(kay->l2_mka);
+		kay->l2_mka = NULL;
+	}
+
+	os_free(kay->ctx);
+	os_free(kay);
+}
+
+
+/**
+ * ieee802_1x_kay_create_mka -
+ */
+struct ieee802_1x_mka_participant *
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
+			  struct mka_key *cak, u32 life,
+			  enum mka_created_mode mode, Boolean is_authenticator)
+{
+	struct ieee802_1x_mka_participant *participant;
+	unsigned int usecs;
+
+	if (!kay || !ckn || !cak) {
+		wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
+		return NULL;
+	}
+
+	if (cak->len != mka_alg_tbl[kay->mka_algindex].cak_len) {
+		wpa_printf(MSG_ERROR, "KaY: CAK length not follow key schema");
+		return NULL;
+	}
+	if (ckn->len > MAX_CKN_LEN) {
+		wpa_printf(MSG_ERROR, "KaY: CKN is out of range(<=32 bytes)");
+		return NULL;
+	}
+	if (!kay->enable) {
+		wpa_printf(MSG_ERROR, "KaY: Now is at disable state");
+		return NULL;
+	}
+
+	participant = os_zalloc(sizeof(*participant));
+	if (!participant) {
+		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+		return NULL;
+	}
+
+	participant->ckn.len = ckn->len;
+	os_memcpy(participant->ckn.name, ckn->name, ckn->len);
+	participant->cak.len = cak->len;
+	os_memcpy(participant->cak.key, cak->key, cak->len);
+	if (life)
+		participant->cak_life = life + time(NULL);
+
+	switch (mode) {
+	case EAP_EXCHANGE:
+		if (is_authenticator) {
+			participant->is_obliged_key_server = TRUE;
+			participant->can_be_key_server = TRUE;
+			participant->is_key_server = TRUE;
+			participant->principal = TRUE;
+
+			os_memcpy(&kay->key_server_sci, &kay->actor_sci,
+				  sizeof(kay->key_server_sci));
+			kay->key_server_priority = kay->actor_priority;
+			participant->is_elected = TRUE;
+		} else {
+			participant->is_obliged_key_server = FALSE;
+			participant->can_be_key_server = FALSE;
+			participant->is_key_server = FALSE;
+			participant->is_elected = TRUE;
+		}
+		break;
+
+	default:
+		participant->is_obliged_key_server = FALSE;
+		participant->can_be_key_server = TRUE;
+		participant->is_key_server = FALSE;
+		participant->is_elected = FALSE;
+		break;
+	}
+
+	participant->cached = FALSE;
+
+	participant->active = FALSE;
+	participant->participant = FALSE;
+	participant->retain = FALSE;
+	participant->activate = DEFAULT;
+
+	if (participant->is_key_server)
+		participant->principal = TRUE;
+
+	dl_list_init(&participant->live_peers);
+	dl_list_init(&participant->potential_peers);
+
+	participant->retry_count = 0;
+	participant->kay = kay;
+
+	os_get_random(participant->mi, sizeof(participant->mi));
+	participant->mn = 0;
+
+	participant->lrx = FALSE;
+	participant->ltx = FALSE;
+	participant->orx = FALSE;
+	participant->otx = FALSE;
+	participant->to_dist_sak = FALSE;
+	participant->to_use_sak = FALSE;
+	participant->new_sak = FALSE;
+	dl_list_init(&participant->sak_list);
+	participant->new_key = NULL;
+	dl_list_init(&participant->rxsc_list);
+	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci,
+							    kay->sc_ch);
+	secy_create_transmit_sc(kay, participant->txsc);
+
+	/* to derive KEK from CAK and CKN */
+	participant->kek.len = mka_alg_tbl[kay->mka_algindex].kek_len;
+	if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
+						    participant->ckn.name,
+						    participant->ckn.len,
+						    participant->kek.key)) {
+		wpa_printf(MSG_ERROR, "KaY: Derived KEK failed");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
+			participant->kek.key, participant->kek.len);
+
+	/* to derive ICK from CAK and CKN */
+	participant->ick.len = mka_alg_tbl[kay->mka_algindex].ick_len;
+	if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
+						    participant->ckn.name,
+						    participant->ckn.len,
+						    participant->ick.key)) {
+		wpa_printf(MSG_ERROR, "KaY: Derived ICK failed");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
+			participant->ick.key, participant->ick.len);
+
+	dl_list_add(&kay->participant_list, &participant->list);
+	wpa_hexdump(MSG_DEBUG, "KaY: Participant created:",
+		    ckn->name, ckn->len);
+
+	usecs = os_random() % (MKA_HELLO_TIME * 1000);
+	eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
+			       participant, NULL);
+	participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
+		usecs / 1000000;
+
+	return participant;
+
+fail:
+	os_free(participant);
+	return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_delete_mka -
+ */
+void
+ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
+{
+	struct ieee802_1x_mka_participant *participant;
+	struct ieee802_1x_kay_peer *peer;
+	struct data_key *sak;
+	struct receive_sc *rxsc;
+
+	if (!kay || !ckn)
+		return;
+
+	wpa_printf(MSG_DEBUG, "KaY: participant removed");
+
+	/* get the participant */
+	participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+	if (!participant) {
+		wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
+			    ckn->name, ckn->len);
+		return;
+	}
+
+	dl_list_del(&participant->list);
+
+	/* remove live peer */
+	while (!dl_list_empty(&participant->live_peers)) {
+		peer = dl_list_entry(participant->live_peers.next,
+				     struct ieee802_1x_kay_peer, list);
+		dl_list_del(&peer->list);
+		os_free(peer);
+	}
+
+	/* remove potential peer */
+	while (!dl_list_empty(&participant->potential_peers)) {
+		peer = dl_list_entry(participant->potential_peers.next,
+				     struct ieee802_1x_kay_peer, list);
+		dl_list_del(&peer->list);
+		os_free(peer);
+	}
+
+	/* remove sak */
+	while (!dl_list_empty(&participant->sak_list)) {
+		sak = dl_list_entry(participant->sak_list.next,
+				    struct data_key, list);
+		dl_list_del(&sak->list);
+		os_free(sak->key);
+		os_free(sak);
+	}
+	while (!dl_list_empty(&participant->rxsc_list)) {
+		rxsc = dl_list_entry(participant->rxsc_list.next,
+				     struct receive_sc, list);
+		secy_delete_receive_sc(kay, rxsc);
+		ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
+	}
+	secy_delete_transmit_sc(kay, participant->txsc);
+	ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
+
+	os_memset(&participant->cak, 0, sizeof(participant->cak));
+	os_memset(&participant->kek, 0, sizeof(participant->kek));
+	os_memset(&participant->ick, 0, sizeof(participant->ick));
+	os_free(participant);
+}
+
+
+/**
+ * ieee802_1x_kay_mka_participate -
+ */
+void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
+				    struct mka_key_name *ckn,
+				    Boolean status)
+{
+	struct ieee802_1x_mka_participant *participant;
+
+	if (!kay || !ckn)
+		return;
+
+	participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+	if (!participant)
+		return;
+
+	participant->active = status;
+}
+
+
+/**
+ * ieee802_1x_kay_new_sak -
+ */
+int
+ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay)
+{
+	struct ieee802_1x_mka_participant *participant;
+
+	if (!kay)
+		return -1;
+
+	participant = ieee802_1x_kay_get_principal_participant(kay);
+	if (!participant)
+		return -1;
+
+	participant->new_sak = TRUE;
+	wpa_printf(MSG_DEBUG, "KaY: new SAK signal");
+
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_change_cipher_suite -
+ */
+int
+ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, int cs_index)
+{
+	struct ieee802_1x_mka_participant *participant;
+
+	if (!kay)
+		return -1;
+
+	if ((unsigned int) cs_index >= CS_TABLE_SIZE) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: Configured cipher suite index is out of range");
+		return -1;
+	}
+	if (kay->macsec_csindex == cs_index)
+		return -2;
+
+	if (cs_index == 0)
+		kay->macsec_desired = FALSE;
+
+	kay->macsec_csindex = cs_index;
+	kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
+
+	participant = ieee802_1x_kay_get_principal_participant(kay);
+	if (participant) {
+		wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
+		participant->new_sak = TRUE;
+	}
+
+	return 0;
+}
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
new file mode 100644
index 0000000..064417e
--- /dev/null
+++ b/src/pae/ieee802_1x_kay.h
@@ -0,0 +1,194 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KAY_H
+#define IEEE802_1X_KAY_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct macsec_init_params;
+struct ieee802_1x_cp_conf;
+
+#define MI_LEN			12
+#define MAX_KEY_LEN		32  /* 32 bytes, 256 bits */
+#define MAX_CKN_LEN		32  /* 32 bytes, 256 bits */
+
+/* MKA timer, unit: millisecond */
+#define MKA_HELLO_TIME		2000
+#define MKA_LIFE_TIME		6000
+#define MKA_SAK_RETIRE_TIME	3000
+
+struct ieee802_1x_mka_ki {
+	u8 mi[MI_LEN];
+	u32 kn;
+};
+
+struct ieee802_1x_mka_sci {
+	u8 addr[ETH_ALEN];
+	u16 port;
+};
+
+struct mka_key {
+	u8 key[MAX_KEY_LEN];
+	size_t len;
+};
+
+struct mka_key_name {
+	u8 name[MAX_CKN_LEN];
+	size_t len;
+};
+
+enum mka_created_mode {
+	PSK,
+	EAP_EXCHANGE,
+	DISTRIBUTED,
+	CACHED,
+};
+
+struct ieee802_1x_kay_ctx {
+	/* pointer to arbitrary upper level context */
+	void *ctx;
+
+	/* abstract wpa driver interface */
+	int (*macsec_init)(void *ctx, struct macsec_init_params *params);
+	int (*macsec_deinit)(void *ctx);
+	int (*enable_protect_frames)(void *ctx, Boolean enabled);
+	int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window);
+	int (*set_current_cipher_suite)(void *ctx, const u8 *cs, size_t cs_len);
+	int (*enable_controlled_port)(void *ctx, Boolean enabled);
+	int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an,
+				     u32 *lowest_pn);
+	int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an,
+				    u32 *next_pn);
+	int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn);
+	int (*get_available_receive_sc)(void *ctx, u32 *channel);
+	int (*create_receive_sc)(void *ctx, u32 channel,
+				 struct ieee802_1x_mka_sci *sci,
+				 enum validate_frames vf,
+				 enum confidentiality_offset co);
+	int (*delete_receive_sc)(void *ctx, u32 channel);
+	int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn,
+				 const u8 *sak);
+	int (*enable_receive_sa)(void *ctx, u32 channel, u8 an);
+	int (*disable_receive_sa)(void *ctx, u32 channel, u8 an);
+	int (*get_available_transmit_sc)(void *ctx, u32 *channel);
+	int (*create_transmit_sc)(void *ctx, u32 channel,
+				  const struct ieee802_1x_mka_sci *sci,
+				  enum confidentiality_offset co);
+	int (*delete_transmit_sc)(void *ctx, u32 channel);
+	int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn,
+				  Boolean confidentiality, const u8 *sak);
+	int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an);
+	int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an);
+};
+
+struct ieee802_1x_kay {
+	Boolean enable;
+	Boolean active;
+
+	Boolean authenticated;
+	Boolean secured;
+	Boolean failed;
+
+	struct ieee802_1x_mka_sci actor_sci;
+	u8 actor_priority;
+	struct ieee802_1x_mka_sci key_server_sci;
+	u8 key_server_priority;
+
+	enum macsec_cap macsec_capable;
+	Boolean macsec_desired;
+	Boolean macsec_protect;
+	Boolean macsec_replay_protect;
+	u32 macsec_replay_window;
+	enum validate_frames macsec_validate;
+	enum confidentiality_offset macsec_confidentiality;
+
+	u32 ltx_kn;
+	u8 ltx_an;
+	u32 lrx_kn;
+	u8 lrx_an;
+
+	u32 otx_kn;
+	u8 otx_an;
+	u32 orx_kn;
+	u8 orx_an;
+
+	/* not defined in IEEE802.1X */
+	struct ieee802_1x_kay_ctx *ctx;
+	Boolean is_key_server;
+	Boolean is_obliged_key_server;
+	char if_name[IFNAMSIZ];
+
+	int macsec_csindex;  /*  MACsec cipher suite table index */
+	int mka_algindex;  /* MKA alg table index */
+
+	u32 dist_kn;
+	u8 dist_an;
+	time_t dist_time;
+
+	u8 mka_version;
+	u8 algo_agility[4];
+	u32 sc_ch;
+
+	u32 pn_exhaustion;
+	Boolean port_enable;
+	Boolean rx_enable;
+	Boolean tx_enable;
+
+	struct dl_list participant_list;
+	enum macsec_policy policy;
+
+	struct ieee802_1x_cp_sm *cp;
+
+	struct l2_packet_data *l2_mka;
+
+	enum validate_frames vf;
+	enum confidentiality_offset co;
+};
+
+
+struct ieee802_1x_kay *
+ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+		    const char *ifname, const u8 *addr);
+void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
+
+struct ieee802_1x_mka_participant *
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
+			  struct mka_key_name *ckn, struct mka_key *cak,
+			  u32 life, enum mka_created_mode mode,
+			  Boolean is_authenticator);
+void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay,
+			       struct mka_key_name *ckn);
+void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
+				    struct mka_key_name *ckn,
+				    Boolean status);
+int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay);
+int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
+				       int cs_index);
+
+int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
+				      struct ieee802_1x_mka_ki *lki, u8 lan,
+				      Boolean ltx, Boolean lrx);
+int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
+				   struct ieee802_1x_mka_ki *oki,
+				   u8 oan, Boolean otx, Boolean orx);
+int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
+			      struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
+			      struct ieee802_1x_mka_ki *ki);
+int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
+				 struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
+				 struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay);
+int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
+			   struct ieee802_1x_cp_conf *pconf);
+
+#endif /* IEEE802_1X_KAY_H */
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
new file mode 100644
index 0000000..bdad3a5
--- /dev/null
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -0,0 +1,419 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KAY_I_H
+#define IEEE802_1X_KAY_I_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+#define MKA_VERSION_ID              1
+
+/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 */
+enum mka_packet_type {
+	MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID,
+	MKA_LIVE_PEER_LIST = 1,
+	MKA_POTENTIAL_PEER_LIST = 2,
+	MKA_SAK_USE = 3,
+	MKA_DISTRIBUTED_SAK = 4,
+	MKA_DISTRIBUTED_CAK = 5,
+	MKA_KMD = 6,
+	MKA_ANNOUNCEMENT = 7,
+	MKA_ICV_INDICATOR = 255
+};
+
+#define ICV_LEN                         16  /* 16 bytes */
+#define SAK_WRAPPED_LEN                 24
+/* KN + Wrapper SAK */
+#define DEFAULT_DIS_SAK_BODY_LENGTH     (SAK_WRAPPED_LEN + 4)
+#define MAX_RETRY_CNT                   5
+
+struct ieee802_1x_kay;
+
+struct ieee802_1x_mka_peer_id {
+	u8 mi[MI_LEN];
+	u32 mn;
+};
+
+struct ieee802_1x_kay_peer {
+	struct ieee802_1x_mka_sci sci;
+	u8 mi[MI_LEN];
+	u32 mn;
+	time_t expire;
+	Boolean is_key_server;
+	u8 key_server_priority;
+	Boolean macsec_desired;
+	enum macsec_cap macsec_capbility;
+	Boolean sak_used;
+	struct dl_list list;
+};
+
+struct key_conf {
+	u8 *key;
+	struct ieee802_1x_mka_ki ki;
+	enum confidentiality_offset offset;
+	u8 an;
+	Boolean tx;
+	Boolean rx;
+	int key_len; /* unit: byte */
+};
+
+struct data_key {
+	u8 *key;
+	int key_len;
+	struct ieee802_1x_mka_ki key_identifier;
+	enum confidentiality_offset confidentiality_offset;
+	u8 an;
+	Boolean transmits;
+	Boolean receives;
+	struct os_time created_time;
+	u32 next_pn;
+
+	/* not defined data */
+	Boolean rx_latest;
+	Boolean tx_latest;
+
+	int user;  /* FIXME: to indicate if it can be delete safely */
+
+	struct dl_list list;
+};
+
+/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sc {
+	struct ieee802_1x_mka_sci sci; /* const SCI sci */
+	Boolean transmitting; /* bool transmitting (read only) */
+
+	struct os_time created_time; /* Time createdTime */
+
+	u8 encoding_sa; /* AN encodingSA (read only) */
+	u8 enciphering_sa; /* AN encipheringSA (read only) */
+
+	/* not defined data */
+	unsigned int channel;
+
+	struct dl_list list;
+	struct dl_list sa_list;
+};
+
+/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sa {
+	Boolean in_use; /* bool inUse (read only) */
+	u32 next_pn; /* PN nextPN (read only) */
+	struct os_time created_time; /* Time createdTime */
+
+	Boolean enable_transmit; /* bool EnableTransmit */
+
+	u8 an;
+	Boolean confidentiality;
+	struct data_key *pkey;
+
+	struct transmit_sc *sc;
+	struct dl_list list; /* list entry in struct transmit_sc::sa_list */
+};
+
+/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sc {
+	struct ieee802_1x_mka_sci sci; /* const SCI sci */
+	Boolean receiving; /* bool receiving (read only) */
+
+	struct os_time created_time; /* Time createdTime */
+
+	unsigned int channel;
+
+	struct dl_list list;
+	struct dl_list sa_list;
+};
+
+/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sa {
+	Boolean enable_receive; /* bool enableReceive */
+	Boolean in_use; /* bool inUse (read only) */
+
+	u32 next_pn; /* PN nextPN (read only) */
+	u32 lowest_pn; /* PN lowestPN (read only) */
+	u8 an;
+	struct os_time created_time;
+
+	struct data_key *pkey;
+	struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
+
+	struct dl_list list;
+};
+
+struct macsec_ciphersuite {
+	u8 id[CS_ID_LEN];
+	char name[32];
+	enum macsec_cap capable;
+	int sak_len; /* unit: byte */
+
+	u32 index;
+};
+
+struct mka_alg {
+	u8 parameter[4];
+	size_t cak_len;
+	size_t kek_len;
+	size_t ick_len;
+	size_t icv_len;
+
+	int (*cak_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, u8 *cak);
+	int (*ckn_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2,
+			const u8 *sid, size_t sid_len, u8 *ckn);
+	int (*kek_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *kek);
+	int (*ick_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *ick);
+	int (*icv_hash)(const u8 *ick, const u8 *msg, size_t msg_len, u8 *icv);
+
+	int index; /* index for configuring */
+};
+
+#define DEFAULT_MKA_ALG_INDEX 0
+
+/* See IEEE Std 802.1X-2010, 9.16 MKA management */
+struct ieee802_1x_mka_participant {
+	/* used for active and potential participant */
+	struct mka_key_name ckn;
+	struct mka_key cak;
+	Boolean cached;
+
+	/* used by management to monitor and control activation */
+	Boolean active;
+	Boolean participant;
+	Boolean retain;
+
+	enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
+
+	/* used for active participant */
+	Boolean principal;
+	struct dl_list live_peers;
+	struct dl_list potential_peers;
+
+	/* not defined in IEEE 802.1X */
+	struct dl_list list;
+
+	struct mka_key kek;
+	struct mka_key ick;
+
+	struct ieee802_1x_mka_ki lki;
+	u8 lan;
+	Boolean ltx;
+	Boolean lrx;
+
+	struct ieee802_1x_mka_ki oki;
+	u8 oan;
+	Boolean otx;
+	Boolean orx;
+
+	Boolean is_key_server;
+	Boolean is_obliged_key_server;
+	Boolean can_be_key_server;
+	Boolean is_elected;
+
+	struct dl_list sak_list;
+	struct dl_list rxsc_list;
+
+	struct transmit_sc *txsc;
+
+	u8 mi[MI_LEN];
+	u32 mn;
+
+	struct ieee802_1x_mka_peer_id current_peer_id;
+	struct ieee802_1x_mka_sci current_peer_sci;
+	time_t cak_life;
+	time_t mka_life;
+	Boolean to_dist_sak;
+	Boolean to_use_sak;
+	Boolean new_sak;
+
+	Boolean advised_desired;
+	enum macsec_cap advised_capability;
+
+	struct data_key *new_key;
+	u32 retry_count;
+
+	struct ieee802_1x_kay *kay;
+};
+
+struct ieee802_1x_mka_hdr {
+	/* octet 1 */
+	u32 type:8;
+	/* octet 2 */
+	u32 reserve:8;
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 length:4;
+	u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 reserve1:4;
+	u32 length:4;
+#else
+#error "Please fix <bits/endian.h>"
+#endif
+	/* octet 4 */
+	u32 length1:8;
+};
+
+#define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
+
+struct ieee802_1x_mka_basic_body {
+	/* octet 1 */
+	u32 version:8;
+	/* octet 2 */
+	u32 priority:8;
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 length:4;
+	u32 macsec_capbility:2;
+	u32 macsec_desired:1;
+	u32 key_server:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 key_server:1;
+	u32 macsec_desired:1;
+	u32 macsec_capbility:2;
+	u32 length:4;
+#endif
+	/* octet 4 */
+	u32 length1:8;
+
+	struct ieee802_1x_mka_sci actor_sci;
+	u8 actor_mi[MI_LEN];
+	u32 actor_mn;
+	u8 algo_agility[4];
+
+	/* followed by CAK Name*/
+	u8 ckn[0];
+};
+
+struct ieee802_1x_mka_peer_body {
+	/* octet 1 */
+	u32 type:8;
+	/* octet 2 */
+	u32 reserve:8;
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 length:4;
+	u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 reserve1:4;
+	u32 length:4;
+#endif
+	/* octet 4 */
+	u32 length1:8;
+
+	u8 peer[0];
+	/* followed by Peers */
+};
+
+struct ieee802_1x_mka_sak_use_body {
+	/* octet 1 */
+	u32 type:8;
+	/* octet 2 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 orx:1;
+	u32 otx:1;
+	u32 oan:2;
+	u32 lrx:1;
+	u32 ltx:1;
+	u32 lan:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 lan:2;
+	u32 ltx:1;
+	u32 lrx:1;
+	u32 oan:2;
+	u32 otx:1;
+	u32 orx:1;
+#endif
+
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 length:4;
+	u32 delay_protect:1;
+	u32 reserve:1;
+	u32 prx:1;
+	u32 ptx:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 ptx:1;
+	u32 prx:1;
+	u32 reserve:1;
+	u32 delay_protect:1;
+	u32 length:4;
+#endif
+
+	/* octet 4 */
+	u32 length1:8;
+
+	/* octet 5 - 16 */
+	u8 lsrv_mi[MI_LEN];
+	/* octet 17 - 20 */
+	u32 lkn;
+	/* octet 21 - 24 */
+	u32 llpn;
+
+	/* octet 25 - 36 */
+	u8 osrv_mi[MI_LEN];
+	/* octet 37 - 40 */
+	u32 okn;
+	/* octet 41 - 44 */
+	u32 olpn;
+};
+
+
+struct ieee802_1x_mka_dist_sak_body {
+	/* octet 1 */
+	u32 type:8;
+	/* octet 2 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 reserve:4;
+	u32 confid_offset:2;
+	u32 dan:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 dan:2;
+	u32 confid_offset:2;
+	u32 reserve:4;
+#endif
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 length:4;
+	u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 reserve1:4;
+	u32 length:4;
+#endif
+	/* octet 4 */
+	u32 length1:8;
+	/* octet 5 - 8 */
+	u32 kn;
+
+	/* for GCM-AES-128: octet 9-32: SAK
+	 * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK
+	 */
+	u8 sak[0];
+};
+
+
+struct ieee802_1x_mka_icv_body {
+	/* octet 1 */
+	u32 type:8;
+	/* octet 2 */
+	u32 reserve:8;
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u32 length:4;
+	u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u32 reserve1:4;
+	u32 length:4;
+#endif
+	/* octet 4 */
+	u32 length1:8;
+
+	/* octet 5 - */
+	u8 icv[0];
+};
+
+#endif /* IEEE802_1X_KAY_I_H */
diff --git a/src/pae/ieee802_1x_key.c b/src/pae/ieee802_1x_key.c
new file mode 100644
index 0000000..9a8d923
--- /dev/null
+++ b/src/pae/ieee802_1x_key.c
@@ -0,0 +1,189 @@
+/*
+ * IEEE 802.1X-2010 Key Hierarchy
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * SAK derivation specified in IEEE Std 802.1X-2010, Clause 6.2
+*/
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "ieee802_1x_key.h"
+
+
+static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out)
+{
+	if (os_memcmp(mac1, mac2, ETH_ALEN) < 0) {
+		os_memcpy(out, mac1, ETH_ALEN);
+		os_memcpy(out + ETH_ALEN, mac2, ETH_ALEN);
+	} else {
+		os_memcpy(out, mac2, ETH_ALEN);
+		os_memcpy(out + ETH_ALEN, mac1, ETH_ALEN);
+	}
+}
+
+
+/* IEEE Std 802.1X-2010, 6.2.1 KDF */
+static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context,
+		       int ctx_bits, int ret_bits, u8 *ret)
+{
+	const int h = 128;
+	const int r = 8;
+	int i, n;
+	int lab_len, ctx_len, ret_len, buf_len;
+	u8 *buf;
+
+	lab_len = os_strlen(label);
+	ctx_len = (ctx_bits + 7) / 8;
+	ret_len = ((ret_bits & 0xffff) + 7) / 8;
+	buf_len = lab_len + ctx_len + 4;
+
+	os_memset(ret, 0, ret_len);
+
+	n = (ret_bits + h - 1) / h;
+	if (n > ((0x1 << r) - 1))
+		return -1;
+
+	buf = os_zalloc(buf_len);
+	if (buf == NULL)
+		return -1;
+
+	os_memcpy(buf + 1, label, lab_len);
+	os_memcpy(buf + lab_len + 2, context, ctx_len);
+	WPA_PUT_BE16(&buf[buf_len - 2], ret_bits);
+
+	for (i = 0; i < n; i++) {
+		buf[0] = (u8) (i + 1);
+		if (omac1_aes_128(kdk, buf, buf_len, ret)) {
+			os_free(buf);
+			return -1;
+		}
+		ret = ret + h / 8;
+	}
+	os_free(buf);
+	return 0;
+}
+
+
+/********** AES-CMAC-128 **********/
+/**
+ * ieee802_1x_cak_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 6.2.2
+ * CAK = KDF(Key, Label, mac1 | mac2, CAKlength)
+ */
+int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+				    const u8 *mac2, u8 *cak)
+{
+	u8 context[2 * ETH_ALEN];
+
+	joint_two_mac(mac1, mac2, context);
+	return aes_kdf_128(msk, "IEEE8021 EAP CAK",
+			   context, sizeof(context) * 8, 128, cak);
+}
+
+
+/**
+ * ieee802_1x_ckn_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 6.2.2
+ * CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength)
+ */
+int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+				    const u8 *mac2, const u8 *sid,
+				    size_t sid_bytes, u8 *ckn)
+{
+	int res;
+	u8 *context;
+	size_t ctx_len = sid_bytes + ETH_ALEN * 2;
+
+	context = os_zalloc(ctx_len);
+	if (!context) {
+		wpa_printf(MSG_ERROR, "MKA-%s: out of memory", __func__);
+		return -1;
+	}
+	os_memcpy(context, sid, sid_bytes);
+	joint_two_mac(mac1, mac2, context + sid_bytes);
+
+	res = aes_kdf_128(msk, "IEEE8021 EAP CKN", context, ctx_len * 8,
+			  128, ckn);
+	os_free(context);
+	return res;
+}
+
+
+/**
+ * ieee802_1x_kek_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.3.3
+ * KEK = KDF(Key, Label, Keyid, KEKLength)
+ */
+int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+				    size_t ckn_bytes, u8 *kek)
+{
+	u8 context[16];
+
+	/* First 16 octets of CKN, with null octets appended to pad if needed */
+	os_memset(context, 0, sizeof(context));
+	os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
+
+	return aes_kdf_128(cak, "IEEE8021 KEK", context, sizeof(context) * 8,
+			   128, kek);
+}
+
+
+/**
+ * ieee802_1x_ick_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.3.3
+ * ICK = KDF(Key, Label, Keyid, ICKLength)
+ */
+int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+				    size_t ckn_bytes, u8 *ick)
+{
+	u8 context[16];
+
+	/* First 16 octets of CKN, with null octets appended to pad if needed */
+	os_memset(context, 0, sizeof(context));
+	os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
+
+	return aes_kdf_128(cak, "IEEE8021 ICK", context, sizeof(context) * 8,
+			   128, ick);
+}
+
+
+/**
+ * ieee802_1x_icv_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.4.1
+ * ICV = AES-CMAC(ICK, M, 128)
+ */
+int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
+				    size_t msg_bytes, u8 *icv)
+{
+	if (omac1_aes_128(ick, msg, msg_bytes, icv)) {
+		wpa_printf(MSG_ERROR, "MKA: omac1_aes_128 failed");
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * ieee802_1x_sak_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.8.1
+ * SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength)
+ */
+int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
+				    size_t ctx_bytes, u8 *sak)
+{
+	return aes_kdf_128(cak, "IEEE8021 SAK", ctx, ctx_bytes * 8, 128, sak);
+}
diff --git a/src/pae/ieee802_1x_key.h b/src/pae/ieee802_1x_key.h
new file mode 100644
index 0000000..ea318ea
--- /dev/null
+++ b/src/pae/ieee802_1x_key.h
@@ -0,0 +1,26 @@
+/*
+ * IEEE 802.1X-2010 Key Hierarchy
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KEY_H
+#define IEEE802_1X_KEY_H
+
+int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+				    const u8 *mac2, u8 *cak);
+int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+				    const u8 *mac2, const u8 *sid,
+				    size_t sid_bytes, u8 *ckn);
+int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+				    size_t ckn_bytes, u8 *kek);
+int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+				    size_t ckn_bytes, u8 *ick);
+int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
+				    size_t msg_bytes, u8 *icv);
+int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
+				    size_t ctx_bytes, u8 *sak);
+
+#endif /* IEEE802_1X_KEY_H */
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
new file mode 100644
index 0000000..fbe05dc
--- /dev/null
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -0,0 +1,492 @@
+ /*
+ * SecY Operations
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "drivers/driver.h"
+#include "pae/ieee802_1x_kay.h"
+#include "pae/ieee802_1x_kay_i.h"
+#include "pae/ieee802_1x_secy_ops.h"
+
+
+int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
+				    enum validate_frames vf)
+{
+	kay->vf = vf;
+	return 0;
+}
+
+
+int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->enable_protect_frames) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy enable_protect_frames operation not supported");
+		return -1;
+	}
+
+	return ops->enable_protect_frames(ops->ctx, enabled);
+}
+
+
+int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->set_replay_protect) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy set_replay_protect operation not supported");
+		return -1;
+	}
+
+	return ops->set_replay_protect(ops->ctx, enabled, win);
+}
+
+
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
+					 const u8 *cs, size_t cs_len)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->set_current_cipher_suite) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy set_current_cipher_suite operation not supported");
+		return -1;
+	}
+
+	return ops->set_current_cipher_suite(ops->ctx, cs, cs_len);
+}
+
+
+int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
+					   enum confidentiality_offset co)
+{
+	kay->co = co;
+	return 0;
+}
+
+
+int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->enable_controlled_port) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy enable_controlled_port operation not supported");
+		return -1;
+	}
+
+	return ops->enable_controlled_port(ops->ctx, enabled);
+}
+
+
+int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
+			       struct receive_sa *rxsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->get_receive_lowest_pn) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy get_receive_lowest_pn operation not supported");
+		return -1;
+	}
+
+	return ops->get_receive_lowest_pn(ops->ctx,
+					rxsa->sc->channel,
+					rxsa->an,
+					&rxsa->lowest_pn);
+}
+
+
+int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
+			      struct transmit_sa *txsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->get_transmit_next_pn) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy get_receive_lowest_pn operation not supported");
+		return -1;
+	}
+
+	return ops->get_transmit_next_pn(ops->ctx,
+					txsa->sc->channel,
+					txsa->an,
+					&txsa->next_pn);
+}
+
+
+int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+			      struct transmit_sa *txsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->set_transmit_next_pn) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy get_receive_lowest_pn operation not supported");
+		return -1;
+	}
+
+	return ops->set_transmit_next_pn(ops->ctx,
+					txsa->sc->channel,
+					txsa->an,
+					txsa->next_pn);
+}
+
+
+int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->get_available_receive_sc) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy get_available_receive_sc operation not supported");
+		return -1;
+	}
+
+	return ops->get_available_receive_sc(ops->ctx, channel);
+}
+
+
+int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsc) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->create_receive_sc) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy create_receive_sc operation not supported");
+		return -1;
+	}
+
+	return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci,
+				      kay->vf, kay->co);
+}
+
+
+int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsc) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->delete_receive_sc) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy delete_receive_sc operation not supported");
+		return -1;
+	}
+
+	return ops->delete_receive_sc(ops->ctx, rxsc->channel);
+}
+
+
+int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->create_receive_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy create_receive_sa operation not supported");
+		return -1;
+	}
+
+	return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an,
+				      rxsa->lowest_pn, rxsa->pkey->key);
+}
+
+
+int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->enable_receive_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy enable_receive_sa operation not supported");
+		return -1;
+	}
+
+	rxsa->enable_receive = TRUE;
+
+	return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+}
+
+
+int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->disable_receive_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy disable_receive_sa operation not supported");
+		return -1;
+	}
+
+	rxsa->enable_receive = FALSE;
+
+	return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+}
+
+
+int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->get_available_transmit_sc) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy get_available_transmit_sc operation not supported");
+		return -1;
+	}
+
+	return ops->get_available_transmit_sc(ops->ctx, channel);
+}
+
+
+int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
+			    struct transmit_sc *txsc)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsc) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->create_transmit_sc) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy create_transmit_sc operation not supported");
+		return -1;
+	}
+
+	return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci,
+				       kay->co);
+}
+
+
+int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
+			    struct transmit_sc *txsc)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsc) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->delete_transmit_sc) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy delete_transmit_sc operation not supported");
+		return -1;
+	}
+
+	return ops->delete_transmit_sc(ops->ctx, txsc->channel);
+}
+
+
+int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->create_transmit_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy create_transmit_sa operation not supported");
+		return -1;
+	}
+
+	return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an,
+					txsa->next_pn, txsa->confidentiality,
+					txsa->pkey->key);
+}
+
+
+int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->enable_transmit_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy enable_transmit_sa operation not supported");
+		return -1;
+	}
+
+	txsa->enable_transmit = TRUE;
+
+	return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+}
+
+
+int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
+			     struct transmit_sa *txsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->disable_transmit_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy disable_transmit_sa operation not supported");
+		return -1;
+	}
+
+	txsa->enable_transmit = FALSE;
+
+	return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+}
+
+
+int secy_init_macsec(struct ieee802_1x_kay *kay)
+{
+	int ret;
+	struct ieee802_1x_kay_ctx *ops;
+	struct macsec_init_params params;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->macsec_init) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy macsec_init operation not supported");
+		return -1;
+	}
+
+	params.use_es = FALSE;
+	params.use_scb = FALSE;
+	params.always_include_sci = TRUE;
+
+	ret = ops->macsec_init(ops->ctx, &params);
+
+	return ret;
+}
+
+
+int secy_deinit_macsec(struct ieee802_1x_kay *kay)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->macsec_deinit) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy macsec_deinit operation not supported");
+		return -1;
+	}
+
+	return ops->macsec_deinit(ops->ctx);
+}
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
new file mode 100644
index 0000000..295b823
--- /dev/null
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -0,0 +1,62 @@
+ /*
+ * SecY Operations
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_SECY_OPS_H
+#define IEEE802_1X_SECY_OPS_H
+
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct ieee802_1x_kay_conf;
+struct receive_sa;
+struct transmit_sa;
+struct receive_sc;
+struct transmit_sc;
+
+int secy_init_macsec(struct ieee802_1x_kay *kay);
+int secy_deinit_macsec(struct ieee802_1x_kay *kay);
+
+/****** CP -> SecY ******/
+int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
+				    enum validate_frames vf);
+int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag);
+int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win);
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
+					 const u8 *cs, size_t cs_len);
+int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
+					   enum confidentiality_offset co);
+int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag);
+
+/****** KaY -> SecY *******/
+int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
+			       struct receive_sa *rxsa);
+int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
+			      struct transmit_sa *txsa);
+int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+			      struct transmit_sa *txsa);
+int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel);
+int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
+int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
+int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_disable_receive_sa(struct ieee802_1x_kay *kay,
+			    struct receive_sa *rxsa);
+
+int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel);
+int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
+			    struct transmit_sc *txsc);
+int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
+			    struct transmit_sc *txsc);
+int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa);
+int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa);
+int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
+			     struct transmit_sa *txsa);
+
+#endif /* IEEE802_1X_SECY_OPS_H */
diff --git a/src/radius/Makefile b/src/radius/Makefile
index b199be8..b5d063d 100644
--- a/src/radius/Makefile
+++ b/src/radius/Makefile
@@ -1,7 +1,7 @@
 all: libradius.a
 
 clean:
-	rm -f *~ *.o *.d libradius.a
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a
 
 install:
 	@echo Nothing to be made.
diff --git a/src/radius/radius.c b/src/radius/radius.c
index d1feec9..f3b645d 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -231,9 +231,21 @@
 	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
 	  RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
-	{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
+	{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
+	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
+	  RADIUS_ATTR_HEXDUMP },
 };
-#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
 
 
 static struct radius_attr_type *radius_get_attr_type(u8 type)
@@ -249,25 +261,17 @@
 }
 
 
-static void print_char(char c)
-{
-	if (c >= 32 && c < 127)
-		printf("%c", c);
-	else
-		printf("<%02x>", c);
-}
-
-
 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 {
 	struct radius_attr_type *attr;
-	int i, len;
+	int len;
 	unsigned char *pos;
+	char buf[1000];
 
 	attr = radius_get_attr_type(hdr->type);
 
-	printf("   Attribute %d (%s) length=%d\n",
-	       hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+	wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
+		   hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
 
 	if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
 		return;
@@ -277,47 +281,50 @@
 
 	switch (attr->data_type) {
 	case RADIUS_ATTR_TEXT:
-		printf("      Value: '");
-		for (i = 0; i < len; i++)
-			print_char(pos[i]);
-		printf("'\n");
+		printf_encode(buf, sizeof(buf), pos, len);
+		wpa_printf(MSG_INFO, "      Value: '%s'", buf);
 		break;
 
 	case RADIUS_ATTR_IP:
 		if (len == 4) {
 			struct in_addr addr;
 			os_memcpy(&addr, pos, 4);
-			printf("      Value: %s\n", inet_ntoa(addr));
-		} else
-			printf("      Invalid IP address length %d\n", len);
+			wpa_printf(MSG_INFO, "      Value: %s",
+				   inet_ntoa(addr));
+		} else {
+			wpa_printf(MSG_INFO, "      Invalid IP address length %d",
+				   len);
+		}
 		break;
 
 #ifdef CONFIG_IPV6
 	case RADIUS_ATTR_IPV6:
 		if (len == 16) {
-			char buf[128];
 			const char *atxt;
 			struct in6_addr *addr = (struct in6_addr *) pos;
 			atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
-			printf("      Value: %s\n", atxt ? atxt : "?");
-		} else
-			printf("      Invalid IPv6 address length %d\n", len);
+			wpa_printf(MSG_INFO, "      Value: %s",
+				   atxt ? atxt : "?");
+		} else {
+			wpa_printf(MSG_INFO, "      Invalid IPv6 address length %d",
+				   len);
+		}
 		break;
 #endif /* CONFIG_IPV6 */
 
 	case RADIUS_ATTR_HEXDUMP:
 	case RADIUS_ATTR_UNDIST:
-		printf("      Value:");
-		for (i = 0; i < len; i++)
-			printf(" %02x", pos[i]);
-		printf("\n");
+		wpa_snprintf_hex(buf, sizeof(buf), pos, len);
+		wpa_printf(MSG_INFO, "      Value: %s", buf);
 		break;
 
 	case RADIUS_ATTR_INT32:
 		if (len == 4)
-			printf("      Value: %u\n", WPA_GET_BE32(pos));
+			wpa_printf(MSG_INFO, "      Value: %u",
+				   WPA_GET_BE32(pos));
 		else
-			printf("      Invalid INT32 length %d\n", len);
+			wpa_printf(MSG_INFO, "      Invalid INT32 length %d",
+				   len);
 		break;
 
 	default:
@@ -330,9 +337,9 @@
 {
 	size_t i;
 
-	printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
-	       msg->hdr->code, radius_code_string(msg->hdr->code),
-	       msg->hdr->identifier, be_to_host16(msg->hdr->length));
+	wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d",
+		   msg->hdr->code, radius_code_string(msg->hdr->code),
+		   msg->hdr->identifier, be_to_host16(msg->hdr->length));
 
 	for (i = 0; i < msg->attr_used; i++) {
 		struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
@@ -384,7 +391,7 @@
 	attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
 				   auth, MD5_MAC_LEN);
 	if (attr == NULL) {
-		printf("WARNING: Could not add Message-Authenticator\n");
+		wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
 		return -1;
 	}
 	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
@@ -473,6 +480,27 @@
 }
 
 
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+				 size_t secret_len, const u8 *req_authenticator)
+{
+	const u8 *addr[2];
+	size_t len[2];
+
+	msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+	os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN);
+	addr[0] = wpabuf_head(msg->buf);
+	len[0] = wpabuf_len(msg->buf);
+	addr[1] = secret;
+	len[1] = secret_len;
+	md5_vector(2, addr, len, msg->hdr->authenticator);
+
+	if (wpabuf_len(msg->buf) > 0xffff) {
+		wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
+			   (unsigned long) wpabuf_len(msg->buf));
+	}
+}
+
+
 int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
 			       size_t secret_len)
 {
@@ -491,7 +519,7 @@
 	addr[3] = secret;
 	len[3] = secret_len;
 	md5_vector(4, addr, len, hash);
-	return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+	return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
 }
 
 
@@ -518,7 +546,7 @@
 	addr[3] = secret;
 	len[3] = secret_len;
 	md5_vector(4, addr, len, hash);
-	if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+	if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
 		return 1;
 
 	for (i = 0; i < msg->attr_used; i++) {
@@ -551,7 +579,7 @@
 	os_memcpy(msg->hdr->authenticator, orig_authenticator,
 		  sizeof(orig_authenticator));
 
-	return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
+	return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0;
 }
 
 
@@ -585,7 +613,7 @@
 	struct radius_attr_hdr *attr;
 
 	if (data_len > RADIUS_MAX_ATTR_LEN) {
-		printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+		wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
 		       (unsigned long) data_len);
 		return NULL;
 	}
@@ -756,8 +784,7 @@
 		tmp = radius_get_attr_hdr(msg, i);
 		if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
 			if (attr != NULL) {
-				printf("Multiple Message-Authenticator "
-				       "attributes in RADIUS message\n");
+				wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message");
 				return 1;
 			}
 			attr = tmp;
@@ -765,7 +792,7 @@
 	}
 
 	if (attr == NULL) {
-		printf("No Message-Authenticator attribute found\n");
+		wpa_printf(MSG_INFO, "No Message-Authenticator attribute found");
 		return 1;
 	}
 
@@ -785,8 +812,8 @@
 			  sizeof(orig_authenticator));
 	}
 
-	if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
-		printf("Invalid Message-Authenticator!\n");
+	if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "Invalid Message-Authenticator!");
 		return 1;
 	}
 
@@ -802,7 +829,7 @@
 	u8 hash[MD5_MAC_LEN];
 
 	if (sent_msg == NULL) {
-		printf("No matching Access-Request message found\n");
+		wpa_printf(MSG_INFO, "No matching Access-Request message found");
 		return 1;
 	}
 
@@ -822,8 +849,8 @@
 	addr[3] = secret;
 	len[3] = secret_len;
 	md5_vector(4, addr, len, hash);
-	if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
-		printf("Response Authenticator invalid!\n");
+	if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "Response Authenticator invalid!");
 		return 1;
 	}
 
@@ -962,7 +989,8 @@
 	pos = key + 2;
 	left = len - 2;
 	if (left % 16) {
-		printf("Invalid ms key len %lu\n", (unsigned long) left);
+		wpa_printf(MSG_INFO, "Invalid ms key len %lu",
+			   (unsigned long) left);
 		return NULL;
 	}
 
@@ -996,7 +1024,7 @@
 	}
 
 	if (plain[0] == 0 || plain[0] > plen - 1) {
-		printf("Failed to decrypt MPPE key\n");
+		wpa_printf(MSG_INFO, "Failed to decrypt MPPE key");
 		os_free(plain);
 		return NULL;
 	}
@@ -1204,30 +1232,55 @@
 }
 
 
-/* Add User-Password attribute to a RADIUS message and encrypt it as specified
- * in RFC 2865, Chap. 5.2 */
-struct radius_attr_hdr *
-radius_msg_add_attr_user_password(struct radius_msg *msg,
-				  const u8 *data, size_t data_len,
-				  const u8 *secret, size_t secret_len)
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+		       size_t len)
 {
-	u8 buf[128];
-	size_t padlen, i, buf_len, pos;
+	struct radius_attr_hdr *attr;
+	u8 *buf, *pos;
+	size_t alen;
+
+	alen = 4 + 2 + len;
+	buf = os_malloc(alen);
+	if (buf == NULL)
+		return 0;
+	pos = buf;
+	WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA);
+	pos += 4;
+	*pos++ = subtype;
+	*pos++ = 2 + len;
+	os_memcpy(pos, data, len);
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+				   buf, alen);
+	os_free(buf);
+	if (attr == NULL)
+		return 0;
+
+	return 1;
+}
+
+
+int radius_user_password_hide(struct radius_msg *msg,
+			      const u8 *data, size_t data_len,
+			      const u8 *secret, size_t secret_len,
+			      u8 *buf, size_t buf_len)
+{
+	size_t padlen, i, pos;
 	const u8 *addr[2];
 	size_t len[2];
 	u8 hash[16];
 
-	if (data_len > 128)
-		return NULL;
+	if (data_len + 16 > buf_len)
+		return -1;
 
 	os_memcpy(buf, data, data_len);
-	buf_len = data_len;
 
 	padlen = data_len % 16;
-	if (padlen && data_len < sizeof(buf)) {
+	if (padlen && data_len < buf_len) {
 		padlen = 16 - padlen;
 		os_memset(buf + data_len, 0, padlen);
-		buf_len += padlen;
+		buf_len = data_len + padlen;
+	} else {
+		buf_len = data_len;
 	}
 
 	addr[0] = secret;
@@ -1253,8 +1306,27 @@
 		pos += 16;
 	}
 
+	return buf_len;
+}
+
+
+/* Add User-Password attribute to a RADIUS message and encrypt it as specified
+ * in RFC 2865, Chap. 5.2 */
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+				  const u8 *data, size_t data_len,
+				  const u8 *secret, size_t secret_len)
+{
+	u8 buf[128];
+	int res;
+
+	res = radius_user_password_hide(msg, data, data_len,
+					secret, secret_len, buf, sizeof(buf));
+	if (res < 0)
+		return NULL;
+
 	return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
-				   buf, buf_len);
+				   buf, res);
 }
 
 
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 2031054..62faae1 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -90,7 +90,14 @@
        RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
        RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
        RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
-       RADIUS_ATTR_ERROR_CAUSE = 101
+       RADIUS_ATTR_ERROR_CAUSE = 101,
+       RADIUS_ATTR_EAP_KEY_NAME = 102,
+       RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177,
+       RADIUS_ATTR_WLAN_HESSID = 181,
+       RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186,
+       RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
+       RADIUS_ATTR_WLAN_AKM_SUITE = 188,
+       RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189,
 };
 
 
@@ -163,6 +170,18 @@
        RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
 };
 
+
+/* Hotspot 2.0 - WFA Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_WFA 40808
+
+enum {
+	RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1,
+	RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2,
+	RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
+	RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
+	RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5,
+};
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -204,6 +223,9 @@
 			       const struct radius_hdr *req_hdr);
 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 			    size_t secret_len);
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+				 size_t secret_len,
+				 const u8 *req_authenticator);
 int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
 			       size_t secret_len);
 int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
@@ -234,6 +256,12 @@
 			     const u8 *secret, size_t secret_len,
 			     const u8 *send_key, size_t send_key_len,
 			     const u8 *recv_key, size_t recv_key_len);
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+		       size_t len);
+int radius_user_password_hide(struct radius_msg *msg,
+			      const u8 *data, size_t data_len,
+			      const u8 *secret, size_t secret_len,
+			      u8 *buf, size_t buf_len);
 struct radius_attr_hdr *
 radius_msg_add_attr_user_password(struct radius_msg *msg,
 				  const u8 *data, size_t data_len,
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 425ad93..10056a6 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -122,7 +122,7 @@
 	/**
 	 * last_attempt - Time of the last transmission attempt
 	 */
-	struct os_time last_attempt;
+	struct os_reltime last_attempt;
 
 	/**
 	 * shared_secret - Shared secret with the target RADIUS server
@@ -295,26 +295,34 @@
 }
 
 
-static void radius_client_handle_send_error(struct radius_client_data *radius,
-					    int s, RadiusType msg_type)
+/*
+ * Returns >0 if message queue was flushed (i.e., the message that triggered
+ * the error is not available anymore)
+ */
+static int radius_client_handle_send_error(struct radius_client_data *radius,
+					   int s, RadiusType msg_type)
 {
 #ifndef CONFIG_NATIVE_WINDOWS
 	int _errno = errno;
-	perror("send[RADIUS]");
+	wpa_printf(MSG_INFO, "send[RADIUS]: %s", strerror(errno));
 	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
-	    _errno == EBADF) {
+	    _errno == EBADF || _errno == ENETUNREACH) {
 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 			       HOSTAPD_LEVEL_INFO,
 			       "Send failed - maybe interface status changed -"
 			       " try to connect again");
-		eloop_unregister_read_sock(s);
-		close(s);
-		if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
+		if (msg_type == RADIUS_ACCT ||
+		    msg_type == RADIUS_ACCT_INTERIM) {
 			radius_client_init_acct(radius);
-		else
+			return 0;
+		} else {
 			radius_client_init_auth(radius);
+			return 1;
+		}
 	}
 #endif /* CONFIG_NATIVE_WINDOWS */
+
+	return 0;
 }
 
 
@@ -351,18 +359,20 @@
 		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
 		       radius_msg_get_hdr(entry->msg)->identifier);
 
-	os_get_time(&entry->last_attempt);
+	os_get_reltime(&entry->last_attempt);
 	buf = radius_msg_get_buf(entry->msg);
-	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
-		radius_client_handle_send_error(radius, s, entry->msg_type);
+	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+		if (radius_client_handle_send_error(radius, s, entry->msg_type)
+		    > 0)
+			return 0;
+	}
 
 	entry->next_try = now + entry->next_wait;
 	entry->next_wait *= 2;
 	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
 		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
 	if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
-		printf("Removing un-ACKed RADIUS message due to too many "
-		       "failed retransmit attempts\n");
+		wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
 		return 1;
 	}
 
@@ -374,21 +384,24 @@
 {
 	struct radius_client_data *radius = eloop_ctx;
 	struct hostapd_radius_servers *conf = radius->conf;
-	struct os_time now;
+	struct os_reltime now;
 	os_time_t first;
 	struct radius_msg_list *entry, *prev, *tmp;
 	int auth_failover = 0, acct_failover = 0;
 	char abuf[50];
+	size_t prev_num_msgs;
+	int s;
 
 	entry = radius->msgs;
 	if (!entry)
 		return;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	first = 0;
 
 	prev = NULL;
 	while (entry) {
+		prev_num_msgs = radius->num_msgs;
 		if (now.sec >= entry->next_try &&
 		    radius_client_retransmit(radius, entry, now.sec)) {
 			if (prev)
@@ -403,7 +416,18 @@
 			continue;
 		}
 
-		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
+		if (prev_num_msgs != radius->num_msgs) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS: Message removed from queue - restart from beginning");
+			entry = radius->msgs;
+			prev = NULL;
+			continue;
+		}
+
+		s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
+			radius->acct_sock;
+		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
+		    (s < 0 && entry->attempts > 0)) {
 			if (entry->msg_type == RADIUS_ACCT ||
 			    entry->msg_type == RADIUS_ACCT_INTERIM)
 				acct_failover++;
@@ -482,7 +506,7 @@
 
 static void radius_client_update_timeout(struct radius_client_data *radius)
 {
-	struct os_time now;
+	struct os_reltime now;
 	os_time_t first;
 	struct radius_msg_list *entry;
 
@@ -498,7 +522,7 @@
 			first = entry->next_try;
 	}
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	if (first < now.sec)
 		first = now.sec;
 	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
@@ -526,7 +550,7 @@
 
 	entry = os_zalloc(sizeof(*entry));
 	if (entry == NULL) {
-		printf("Failed to add RADIUS packet into retransmit list\n");
+		wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
 		radius_msg_free(msg);
 		return;
 	}
@@ -537,7 +561,7 @@
 	entry->msg_type = msg_type;
 	entry->shared_secret = shared_secret;
 	entry->shared_secret_len = shared_secret_len;
-	os_get_time(&entry->last_attempt);
+	os_get_reltime(&entry->last_attempt);
 	entry->first_try = entry->last_attempt.sec;
 	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
 	entry->attempts = 1;
@@ -547,8 +571,7 @@
 	radius_client_update_timeout(radius);
 
 	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
-		printf("Removing the oldest un-ACKed RADIUS packet due to "
-		       "retransmit list limits.\n");
+		wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
 		prev = NULL;
 		while (entry->next) {
 			prev = entry;
@@ -635,7 +658,7 @@
 	}
 
 	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
-		if (conf->acct_server == NULL) {
+		if (conf->acct_server == NULL || radius->acct_sock < 0) {
 			hostapd_logger(radius->ctx, NULL,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
@@ -649,7 +672,7 @@
 		s = radius->acct_sock;
 		conf->acct_server->requests++;
 	} else {
-		if (conf->auth_server == NULL) {
+		if (conf->auth_server == NULL || radius->auth_sock < 0) {
 			hostapd_logger(radius->ctx, NULL,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
@@ -694,7 +717,7 @@
 	struct radius_rx_handler *handlers;
 	size_t num_handlers, i;
 	struct radius_msg_list *req, *prev_req;
-	struct os_time now;
+	struct os_reltime now;
 	struct hostapd_radius_server *rconf;
 	int invalid_authenticator = 0;
 
@@ -710,21 +733,20 @@
 
 	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
 	if (len < 0) {
-		perror("recv[RADIUS]");
+		wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno));
 		return;
 	}
 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
 		       "server", len);
 	if (len == sizeof(buf)) {
-		printf("Possibly too long UDP frame for our buffer - "
-		       "dropping it\n");
+		wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
 		return;
 	}
 
 	msg = radius_msg_parse(buf, len);
 	if (msg == NULL) {
-		printf("Parsing incoming RADIUS frame failed\n");
+		wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
 		rconf->malformed_responses++;
 		return;
 	}
@@ -775,7 +797,7 @@
 		goto fail;
 	}
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	roundtrip = (now.sec - req->last_attempt.sec) * 100 +
 		(now.usec - req->last_attempt.usec) / 10000;
 	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
@@ -954,9 +976,10 @@
 		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
 		       nserv->port);
 
-	if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
-	    os_memcmp(nserv->shared_secret, oserv->shared_secret,
-		      nserv->shared_secret_len) != 0) {
+	if (oserv && oserv != nserv &&
+	    (nserv->shared_secret_len != oserv->shared_secret_len ||
+	     os_memcmp(nserv->shared_secret, oserv->shared_secret,
+		       nserv->shared_secret_len) != 0)) {
 		/* Pending RADIUS packets used different shared secret, so
 		 * they need to be modified. Update accounting message
 		 * authenticators here. Authentication messages are removed
@@ -974,7 +997,8 @@
 	}
 
 	/* Reset retry counters for the new server */
-	for (entry = radius->msgs; entry; entry = entry->next) {
+	for (entry = radius->msgs; oserv && oserv != nserv && entry;
+	     entry = entry->next) {
 		if ((auth && entry->msg_type != RADIUS_AUTH) ||
 		    (!auth && entry->msg_type != RADIUS_ACCT))
 			continue;
@@ -1041,13 +1065,14 @@
 		}
 
 		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
-			perror("bind[radius]");
+			wpa_printf(MSG_INFO, "bind[radius]: %s",
+				   strerror(errno));
 			return -1;
 		}
 	}
 
 	if (connect(sel_sock, addr, addrlen) < 0) {
-		perror("connect[radius]");
+		wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
 		return -1;
 	}
 
@@ -1123,21 +1148,62 @@
 	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
 		       sizeof(action));
 	if (r == -1)
-		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
-			   "%s", strerror(errno));
+		wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
+			   strerror(errno));
 #endif
 	return r;
 }
 
 
+static void radius_close_auth_sockets(struct radius_client_data *radius)
+{
+	radius->auth_sock = -1;
+
+	if (radius->auth_serv_sock >= 0) {
+		eloop_unregister_read_sock(radius->auth_serv_sock);
+		close(radius->auth_serv_sock);
+		radius->auth_serv_sock = -1;
+	}
+#ifdef CONFIG_IPV6
+	if (radius->auth_serv_sock6 >= 0) {
+		eloop_unregister_read_sock(radius->auth_serv_sock6);
+		close(radius->auth_serv_sock6);
+		radius->auth_serv_sock6 = -1;
+	}
+#endif /* CONFIG_IPV6 */
+}
+
+
+static void radius_close_acct_sockets(struct radius_client_data *radius)
+{
+	radius->acct_sock = -1;
+
+	if (radius->acct_serv_sock >= 0) {
+		eloop_unregister_read_sock(radius->acct_serv_sock);
+		close(radius->acct_serv_sock);
+		radius->acct_serv_sock = -1;
+	}
+#ifdef CONFIG_IPV6
+	if (radius->acct_serv_sock6 >= 0) {
+		eloop_unregister_read_sock(radius->acct_serv_sock6);
+		close(radius->acct_serv_sock6);
+		radius->acct_serv_sock6 = -1;
+	}
+#endif /* CONFIG_IPV6 */
+}
+
+
 static int radius_client_init_auth(struct radius_client_data *radius)
 {
 	struct hostapd_radius_servers *conf = radius->conf;
 	int ok = 0;
 
+	radius_close_auth_sockets(radius);
+
 	radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (radius->auth_serv_sock < 0)
-		perror("socket[PF_INET,SOCK_DGRAM]");
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
 	else {
 		radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
 		ok++;
@@ -1146,7 +1212,8 @@
 #ifdef CONFIG_IPV6
 	radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
 	if (radius->auth_serv_sock6 < 0)
-		perror("socket[PF_INET6,SOCK_DGRAM]");
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+			   strerror(errno));
 	else
 		ok++;
 #endif /* CONFIG_IPV6 */
@@ -1162,8 +1229,8 @@
 	    eloop_register_read_sock(radius->auth_serv_sock,
 				     radius_client_receive, radius,
 				     (void *) RADIUS_AUTH)) {
-		printf("Could not register read socket for authentication "
-		       "server\n");
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+		radius_close_auth_sockets(radius);
 		return -1;
 	}
 
@@ -1172,8 +1239,8 @@
 	    eloop_register_read_sock(radius->auth_serv_sock6,
 				     radius_client_receive, radius,
 				     (void *) RADIUS_AUTH)) {
-		printf("Could not register read socket for authentication "
-		       "server\n");
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+		radius_close_auth_sockets(radius);
 		return -1;
 	}
 #endif /* CONFIG_IPV6 */
@@ -1187,9 +1254,12 @@
 	struct hostapd_radius_servers *conf = radius->conf;
 	int ok = 0;
 
+	radius_close_acct_sockets(radius);
+
 	radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (radius->acct_serv_sock < 0)
-		perror("socket[PF_INET,SOCK_DGRAM]");
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
 	else {
 		radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
 		ok++;
@@ -1198,7 +1268,8 @@
 #ifdef CONFIG_IPV6
 	radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
 	if (radius->acct_serv_sock6 < 0)
-		perror("socket[PF_INET6,SOCK_DGRAM]");
+		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+			   strerror(errno));
 	else
 		ok++;
 #endif /* CONFIG_IPV6 */
@@ -1214,8 +1285,8 @@
 	    eloop_register_read_sock(radius->acct_serv_sock,
 				     radius_client_receive, radius,
 				     (void *) RADIUS_ACCT)) {
-		printf("Could not register read socket for accounting "
-		       "server\n");
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+		radius_close_acct_sockets(radius);
 		return -1;
 	}
 
@@ -1224,8 +1295,8 @@
 	    eloop_register_read_sock(radius->acct_serv_sock6,
 				     radius_client_receive, radius,
 				     (void *) RADIUS_ACCT)) {
-		printf("Could not register read socket for accounting "
-		       "server\n");
+		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+		radius_close_acct_sockets(radius);
 		return -1;
 	}
 #endif /* CONFIG_IPV6 */
@@ -1287,16 +1358,8 @@
 	if (!radius)
 		return;
 
-	if (radius->auth_serv_sock >= 0)
-		eloop_unregister_read_sock(radius->auth_serv_sock);
-	if (radius->acct_serv_sock >= 0)
-		eloop_unregister_read_sock(radius->acct_serv_sock);
-#ifdef CONFIG_IPV6
-	if (radius->auth_serv_sock6 >= 0)
-		eloop_unregister_read_sock(radius->auth_serv_sock6);
-	if (radius->acct_serv_sock6 >= 0)
-		eloop_unregister_read_sock(radius->acct_serv_sock6);
-#endif /* CONFIG_IPV6 */
+	radius_close_auth_sockets(radius);
+	radius_close_acct_sockets(radius);
 
 	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
 
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index bded965..9655f4c 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
- * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,9 +16,6 @@
 #include "radius_das.h"
 
 
-extern int wpa_debug_level;
-
-
 struct radius_das_data {
 	int sock;
 	u8 *shared_secret;
@@ -41,11 +38,16 @@
 	struct radius_msg *reply;
 	u8 allowed[] = {
 		RADIUS_ATTR_USER_NAME,
+		RADIUS_ATTR_NAS_IP_ADDRESS,
 		RADIUS_ATTR_CALLING_STATION_ID,
+		RADIUS_ATTR_NAS_IDENTIFIER,
 		RADIUS_ATTR_ACCT_SESSION_ID,
 		RADIUS_ATTR_EVENT_TIMESTAMP,
 		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
 		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+#ifdef CONFIG_IPV6
+		RADIUS_ATTR_NAS_IPV6_ADDRESS,
+#endif /* CONFIG_IPV6 */
 		0
 	};
 	int error = 405;
@@ -70,6 +72,36 @@
 
 	os_memset(&attrs, 0, sizeof(attrs));
 
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				    &buf, &len, NULL) == 0) {
+		if (len != 4) {
+			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
+				   abuf, from_port);
+			error = 407;
+			goto fail;
+		}
+		attrs.nas_ip_addr = buf;
+	}
+
+#ifdef CONFIG_IPV6
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+				    &buf, &len, NULL) == 0) {
+		if (len != 16) {
+			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
+				   abuf, from_port);
+			error = 407;
+			goto fail;
+		}
+		attrs.nas_ipv6_addr = buf;
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+				    &buf, &len, NULL) == 0) {
+		attrs.nas_identifier = buf;
+		attrs.nas_identifier_len = len;
+	}
+
 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
 				    &buf, &len, NULL) == 0) {
 		if (len >= sizeof(tmp))
@@ -200,7 +232,8 @@
 				  (u8 *) &val, 4);
 	if (res == 4) {
 		u32 timestamp = ntohl(val);
-		if (abs(now.sec - timestamp) > das->time_window) {
+		if ((unsigned int) abs(now.sec - timestamp) >
+		    das->time_window) {
 			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
 				   "Event-Timestamp (%u; local time %u) in "
 				   "packet from %s:%d - drop",
@@ -284,7 +317,7 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
 		return -1;
 	}
 
@@ -292,7 +325,7 @@
 	addr.sin_family = AF_INET;
 	addr.sin_port = htons(port);
 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind");
+		wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
 		close(s);
 		return -1;
 	}
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
index 738b18b..e3ed540 100644
--- a/src/radius/radius_das.h
+++ b/src/radius/radius_das.h
@@ -18,6 +18,13 @@
 };
 
 struct radius_das_attrs {
+	/* NAS identification attributes */
+	const u8 *nas_ip_addr;
+	const u8 *nas_identifier;
+	size_t nas_identifier_len;
+	const u8 *nas_ipv6_addr;
+
+	/* Session identification attributes */
 	const u8 *sta_addr;
 	const u8 *user_name;
 	size_t user_name_len;
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index 0144c9f..24348a3 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS authentication server
- * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -8,11 +8,16 @@
 
 #include "includes.h"
 #include <net/if.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "common.h"
 #include "radius.h"
 #include "eloop.h"
 #include "eap_server/eap.h"
+#include "ap/ap_config.h"
+#include "crypto/tls.h"
 #include "radius_server.h"
 
 /**
@@ -49,6 +54,13 @@
 	u32 bad_authenticators;
 	u32 packets_dropped;
 	u32 unknown_types;
+
+	u32 acct_requests;
+	u32 invalid_acct_requests;
+	u32 acct_responses;
+	u32 malformed_acct_requests;
+	u32 acct_bad_authenticators;
+	u32 unknown_acct_types;
 };
 
 /**
@@ -61,6 +73,8 @@
 	unsigned int sess_id;
 	struct eap_sm *eap;
 	struct eap_eapol_interface *eap_if;
+	char *username; /* from User-Name attribute */
+	char *nas_ip;
 
 	struct radius_msg *last_msg;
 	char *last_from_addr;
@@ -70,6 +84,11 @@
 	u8 last_identifier;
 	struct radius_msg *last_reply;
 	u8 last_authenticator[16];
+
+	unsigned int remediation:1;
+	unsigned int macacl:1;
+
+	struct hostapd_radius_attr *accept_attr;
 };
 
 /**
@@ -99,6 +118,11 @@
 	int auth_sock;
 
 	/**
+	 * acct_sock - Socket for RADIUS accounting messages
+	 */
+	int acct_sock;
+
+	/**
 	 * clients - List of authorized RADIUS clients
 	 */
 	struct radius_client *clients;
@@ -244,7 +268,7 @@
 	/**
 	 * start_time - Timestamp of server start
 	 */
-	struct os_time start_time;
+	struct os_reltime start_time;
 
 	/**
 	 * counters - Statistics counters for server operations
@@ -295,11 +319,16 @@
 #ifdef CONFIG_RADIUS_TEST
 	char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
+
+	char *subscr_remediation_url;
+	u8 subscr_remediation_method;
+
+#ifdef CONFIG_SQLITE
+	sqlite3 *db;
+#endif /* CONFIG_SQLITE */
 };
 
 
-extern int wpa_debug_level;
-
 #define RADIUS_DEBUG(args...) \
 wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
 #define RADIUS_ERROR(args...) \
@@ -314,6 +343,52 @@
 static void radius_server_session_remove_timeout(void *eloop_ctx,
 						 void *timeout_ctx);
 
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	va_start(ap, fmt);
+	vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+
+	RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf);
+
+#ifdef CONFIG_SQLITE
+	if (sess->server->db) {
+		char *sql;
+		sql = sqlite3_mprintf("INSERT INTO authlog"
+				      "(timestamp,session,nas_ip,username,note)"
+				      " VALUES ("
+				      "strftime('%%Y-%%m-%%d %%H:%%M:%%f',"
+				      "'now'),%u,%Q,%Q,%Q)",
+				      sess->sess_id, sess->nas_ip,
+				      sess->username, buf);
+		if (sql) {
+			if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
+					 NULL) != SQLITE_OK) {
+				RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s",
+					     sqlite3_errmsg(sess->server->db));
+			}
+			sqlite3_free(sql);
+		}
+	}
+#endif /* CONFIG_SQLITE */
+
+	os_free(buf);
+}
+
 
 static struct radius_client *
 radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
@@ -379,6 +454,8 @@
 	radius_msg_free(sess->last_msg);
 	os_free(sess->last_from_addr);
 	radius_msg_free(sess->last_reply);
+	os_free(sess->username);
+	os_free(sess->nas_ip);
 	os_free(sess);
 	data->num_sess--;
 }
@@ -458,47 +535,125 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+static void radius_server_testing_options_tls(struct radius_session *sess,
+					      const char *tls,
+					      struct eap_config *eap_conf)
+{
+	int test = atoi(tls);
+
+	switch (test) {
+	case 1:
+		srv_log(sess, "TLS test - break VerifyData");
+		eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA;
+		break;
+	case 2:
+		srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash");
+		eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH;
+		break;
+	case 3:
+		srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature");
+		eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE;
+		break;
+	case 4:
+		srv_log(sess, "TLS test - RSA-DHE using a short 511-bit prime");
+		eap_conf->tls_test_flags = TLS_DHE_PRIME_511B;
+		break;
+	case 5:
+		srv_log(sess, "TLS test - RSA-DHE using a short 767-bit prime");
+		eap_conf->tls_test_flags = TLS_DHE_PRIME_767B;
+		break;
+	case 6:
+		srv_log(sess, "TLS test - RSA-DHE using a bogus 15 \"prime\"");
+		eap_conf->tls_test_flags = TLS_DHE_PRIME_15;
+		break;
+	case 7:
+		srv_log(sess, "TLS test - RSA-DHE using a short 58-bit prime in long container");
+		eap_conf->tls_test_flags = TLS_DHE_PRIME_58B;
+		break;
+	case 8:
+		srv_log(sess, "TLS test - RSA-DHE using a non-prime");
+		eap_conf->tls_test_flags = TLS_DHE_NON_PRIME;
+		break;
+	default:
+		srv_log(sess, "Unrecognized TLS test");
+		break;
+	}
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+static void radius_server_testing_options(struct radius_session *sess,
+					  struct eap_config *eap_conf)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	const char *pos;
+
+	pos = os_strstr(sess->username, "@test-");
+	if (pos == NULL)
+		return;
+	pos += 6;
+	if (os_strncmp(pos, "tls-", 4) == 0)
+		radius_server_testing_options_tls(sess, pos + 4, eap_conf);
+	else
+		srv_log(sess, "Unrecognized test: %s", pos);
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
 static struct radius_session *
 radius_server_get_new_session(struct radius_server_data *data,
 			      struct radius_client *client,
-			      struct radius_msg *msg)
+			      struct radius_msg *msg, const char *from_addr)
 {
 	u8 *user;
 	size_t user_len;
 	int res;
 	struct radius_session *sess;
 	struct eap_config eap_conf;
+	struct eap_user tmp;
 
 	RADIUS_DEBUG("Creating a new session");
 
-	user = os_malloc(256);
-	if (user == NULL) {
-		return NULL;
-	}
-	res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256);
-	if (res < 0 || res > 256) {
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
+				    &user_len, NULL) < 0) {
 		RADIUS_DEBUG("Could not get User-Name");
-		os_free(user);
 		return NULL;
 	}
-	user_len = res;
 	RADIUS_DUMP_ASCII("User-Name", user, user_len);
 
-	res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL);
-	os_free(user);
+	os_memset(&tmp, 0, sizeof(tmp));
+	res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp);
+	bin_clear_free(tmp.password, tmp.password_len);
 
-	if (res == 0) {
-		RADIUS_DEBUG("Matching user entry found");
-		sess = radius_server_new_session(data, client);
-		if (sess == NULL) {
-			RADIUS_DEBUG("Failed to create a new session");
-			return NULL;
-		}
-	} else {
+	if (res != 0) {
 		RADIUS_DEBUG("User-Name not found from user database");
 		return NULL;
 	}
 
+	RADIUS_DEBUG("Matching user entry found");
+	sess = radius_server_new_session(data, client);
+	if (sess == NULL) {
+		RADIUS_DEBUG("Failed to create a new session");
+		return NULL;
+	}
+	sess->accept_attr = tmp.accept_attr;
+	sess->macacl = tmp.macacl;
+
+	sess->username = os_malloc(user_len * 4 + 1);
+	if (sess->username == NULL) {
+		radius_server_session_free(data, sess);
+		return NULL;
+	}
+	printf_encode(sess->username, user_len * 4 + 1, user, user_len);
+
+	sess->nas_ip = os_strdup(from_addr);
+	if (sess->nas_ip == NULL) {
+		radius_server_session_free(data, sess);
+		return NULL;
+	}
+
+	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;
@@ -518,6 +673,7 @@
 	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);
+	radius_server_testing_options(sess, &eap_conf);
 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
 				       &eap_conf);
 	if (sess->eap == NULL) {
@@ -612,12 +768,134 @@
 		}
 	}
 
+#ifdef CONFIG_HS20
+	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
+	    data->subscr_remediation_url) {
+		u8 *buf;
+		size_t url_len = os_strlen(data->subscr_remediation_url);
+		buf = os_malloc(1 + url_len);
+		if (buf == NULL) {
+			radius_msg_free(msg);
+			return NULL;
+		}
+		buf[0] = data->subscr_remediation_method;
+		os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
+		if (!radius_msg_add_wfa(
+			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
+			    buf, 1 + url_len)) {
+			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
+		}
+		os_free(buf);
+	} else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
+		u8 buf[1];
+		if (!radius_msg_add_wfa(
+			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
+			    buf, 0)) {
+			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
+		}
+	}
+#endif /* CONFIG_HS20 */
+
 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
 		radius_msg_free(msg);
 		return NULL;
 	}
 
+	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+		struct hostapd_radius_attr *attr;
+		for (attr = sess->accept_attr; attr; attr = attr->next) {
+			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");
+				radius_msg_free(msg);
+				return NULL;
+			}
+		}
+	}
+
+	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+				  client->shared_secret_len,
+				  hdr->authenticator) < 0) {
+		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+	}
+
+	return msg;
+}
+
+
+static struct radius_msg *
+radius_server_macacl(struct radius_server_data *data,
+		     struct radius_client *client,
+		     struct radius_session *sess,
+		     struct radius_msg *request)
+{
+	struct radius_msg *msg;
+	int code;
+	struct radius_hdr *hdr = radius_msg_get_hdr(request);
+	u8 *pw;
+	size_t pw_len;
+
+	code = RADIUS_CODE_ACCESS_ACCEPT;
+
+	if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw,
+				    &pw_len, NULL) < 0) {
+		RADIUS_DEBUG("Could not get User-Password");
+		code = RADIUS_CODE_ACCESS_REJECT;
+	} else {
+		int res;
+		struct eap_user tmp;
+
+		os_memset(&tmp, 0, sizeof(tmp));
+		res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username,
+					 os_strlen(sess->username), 0, &tmp);
+		if (res || !tmp.macacl || tmp.password == NULL) {
+			RADIUS_DEBUG("No MAC ACL user entry");
+			bin_clear_free(tmp.password, tmp.password_len);
+			code = RADIUS_CODE_ACCESS_REJECT;
+		} else {
+			u8 buf[128];
+			res = radius_user_password_hide(
+				request, tmp.password, tmp.password_len,
+				(u8 *) client->shared_secret,
+				client->shared_secret_len,
+				buf, sizeof(buf));
+			bin_clear_free(tmp.password, tmp.password_len);
+
+			if (res < 0 || pw_len != (size_t) res ||
+			    os_memcmp_const(pw, buf, res) != 0) {
+				RADIUS_DEBUG("Incorrect User-Password");
+				code = RADIUS_CODE_ACCESS_REJECT;
+			}
+		}
+	}
+
+	msg = radius_msg_new(code, hdr->identifier);
+	if (msg == NULL) {
+		RADIUS_DEBUG("Failed to allocate reply message");
+		return NULL;
+	}
+
+	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
+		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
+		radius_msg_free(msg);
+		return NULL;
+	}
+
+	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+		struct hostapd_radius_attr *attr;
+		for (attr = sess->accept_attr; attr; attr = attr->next) {
+			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");
+				radius_msg_free(msg);
+				return NULL;
+			}
+		}
+	}
+
 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
 				  client->shared_secret_len,
 				  hdr->authenticator) < 0) {
@@ -679,7 +957,7 @@
 	buf = radius_msg_get_buf(msg);
 	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
 		   (struct sockaddr *) from, sizeof(*from)) < 0) {
-		perror("sendto[RADIUS SRV]");
+		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
 		ret = -1;
 	}
 
@@ -726,7 +1004,8 @@
 				     from_addr, from_port);
 		return -1;
 	} else {
-		sess = radius_server_get_new_session(data, client, msg);
+		sess = radius_server_get_new_session(data, client, msg,
+						     from_addr);
 		if (sess == NULL) {
 			RADIUS_DEBUG("Could not create a new session");
 			radius_server_reject(data, client, msg, from, fromlen,
@@ -750,7 +1029,8 @@
 				     wpabuf_len(buf), 0,
 				     (struct sockaddr *) from, fromlen);
 			if (res < 0) {
-				perror("sendto[RADIUS SRV]");
+				wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+					   strerror(errno));
 			}
 			return 0;
 		}
@@ -761,6 +1041,12 @@
 	}
 		      
 	eap = radius_msg_get_eap(msg);
+	if (eap == NULL && sess->macacl) {
+		reply = radius_server_macacl(data, client, sess, msg);
+		if (reply == NULL)
+			return -1;
+		goto send_reply;
+	}
 	if (eap == NULL) {
 		RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
 			     from_addr);
@@ -811,9 +1097,14 @@
 
 	if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
 		is_complete = 1;
+	if (sess->eap_if->eapFail)
+		srv_log(sess, "EAP authentication failed");
+	else if (sess->eap_if->eapSuccess)
+		srv_log(sess, "EAP authentication succeeded");
 
 	reply = radius_server_encapsulate_eap(data, client, sess, msg);
 
+send_reply:
 	if (reply) {
 		struct wpabuf *buf;
 		struct radius_hdr *hdr;
@@ -825,10 +1116,12 @@
 
 		switch (radius_msg_get_hdr(reply)->code) {
 		case RADIUS_CODE_ACCESS_ACCEPT:
+			srv_log(sess, "Sending Access-Accept");
 			data->counters.access_accepts++;
 			client->counters.access_accepts++;
 			break;
 		case RADIUS_CODE_ACCESS_REJECT:
+			srv_log(sess, "Sending Access-Reject");
 			data->counters.access_rejects++;
 			client->counters.access_rejects++;
 			break;
@@ -842,7 +1135,8 @@
 			     wpabuf_len(buf), 0,
 			     (struct sockaddr *) from, fromlen);
 		if (res < 0) {
-			perror("sendto[RADIUS SRV]");
+			wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+				   strerror(errno));
 		}
 		radius_msg_free(sess->last_reply);
 		sess->last_reply = reply;
@@ -897,7 +1191,8 @@
 	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
 		       (struct sockaddr *) &from.ss, &fromlen);
 	if (len < 0) {
-		perror("recvfrom[radius_server]");
+		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
+			   strerror(errno));
 		goto fail;
 	}
 
@@ -978,6 +1273,140 @@
 }
 
 
+static void radius_server_receive_acct(int sock, void *eloop_ctx,
+				       void *sock_ctx)
+{
+	struct radius_server_data *data = eloop_ctx;
+	u8 *buf = NULL;
+	union {
+		struct sockaddr_storage ss;
+		struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+		struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+	} from;
+	socklen_t fromlen;
+	int len, res;
+	struct radius_client *client = NULL;
+	struct radius_msg *msg = NULL, *resp = NULL;
+	char abuf[50];
+	int from_port = 0;
+	struct radius_hdr *hdr;
+	struct wpabuf *rbuf;
+
+	buf = os_malloc(RADIUS_MAX_MSG_LEN);
+	if (buf == NULL) {
+		goto fail;
+	}
+
+	fromlen = sizeof(from);
+	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
+		       (struct sockaddr *) &from.ss, &fromlen);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+#ifdef CONFIG_IPV6
+	if (data->ipv6) {
+		if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
+			      sizeof(abuf)) == NULL)
+			abuf[0] = '\0';
+		from_port = ntohs(from.sin6.sin6_port);
+		RADIUS_DEBUG("Received %d bytes from %s:%d",
+			     len, abuf, from_port);
+
+		client = radius_server_get_client(data,
+						  (struct in_addr *)
+						  &from.sin6.sin6_addr, 1);
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (!data->ipv6) {
+		os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+		from_port = ntohs(from.sin.sin_port);
+		RADIUS_DEBUG("Received %d bytes from %s:%d",
+			     len, abuf, from_port);
+
+		client = radius_server_get_client(data, &from.sin.sin_addr, 0);
+	}
+
+	RADIUS_DUMP("Received data", buf, len);
+
+	if (client == NULL) {
+		RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
+		data->counters.invalid_acct_requests++;
+		goto fail;
+	}
+
+	msg = radius_msg_parse(buf, len);
+	if (msg == NULL) {
+		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
+		data->counters.malformed_acct_requests++;
+		client->counters.malformed_acct_requests++;
+		goto fail;
+	}
+
+	os_free(buf);
+	buf = NULL;
+
+	if (wpa_debug_level <= MSG_MSGDUMP) {
+		radius_msg_dump(msg);
+	}
+
+	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) {
+		RADIUS_DEBUG("Unexpected RADIUS code %d",
+			     radius_msg_get_hdr(msg)->code);
+		data->counters.unknown_acct_types++;
+		client->counters.unknown_acct_types++;
+		goto fail;
+	}
+
+	data->counters.acct_requests++;
+	client->counters.acct_requests++;
+
+	if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret,
+				       client->shared_secret_len)) {
+		RADIUS_DEBUG("Invalid Authenticator from %s", abuf);
+		data->counters.acct_bad_authenticators++;
+		client->counters.acct_bad_authenticators++;
+		goto fail;
+	}
+
+	/* TODO: Write accounting information to a file or database */
+
+	hdr = radius_msg_get_hdr(msg);
+
+	resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier);
+	if (resp == NULL)
+		goto fail;
+
+	radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
+				    client->shared_secret_len,
+				    hdr->authenticator);
+
+	RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
+	if (wpa_debug_level <= MSG_MSGDUMP) {
+		radius_msg_dump(resp);
+	}
+	rbuf = radius_msg_get_buf(resp);
+	data->counters.acct_responses++;
+	client->counters.acct_responses++;
+	res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0,
+		     (struct sockaddr *) &from.ss, fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+			   strerror(errno));
+	}
+
+fail:
+	radius_msg_free(resp);
+	radius_msg_free(msg);
+	os_free(buf);
+}
+
+
 static int radius_server_disable_pmtu_discovery(int s)
 {
 	int r = -1;
@@ -1001,7 +1430,7 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
 		return -1;
 	}
 
@@ -1011,7 +1440,7 @@
 	addr.sin_family = AF_INET;
 	addr.sin_port = htons(port);
 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind");
+		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -1028,7 +1457,8 @@
 
 	s = socket(PF_INET6, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket[IPv6]");
+		wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -1037,7 +1467,7 @@
 	os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
 	addr.sin6_port = htons(port);
 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind");
+		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -1190,8 +1620,8 @@
 			break;
 		}
 		entry->shared_secret_len = os_strlen(entry->shared_secret);
-		entry->addr.s_addr = addr.s_addr;
 		if (!ipv6) {
+			entry->addr.s_addr = addr.s_addr;
 			val = 0;
 			for (i = 0; i < mask; i++)
 				val |= 1 << (31 - i);
@@ -1248,8 +1678,7 @@
 
 #ifndef CONFIG_IPV6
 	if (conf->ipv6) {
-		fprintf(stderr, "RADIUS server compiled without IPv6 "
-			"support.\n");
+		wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
 		return NULL;
 	}
 #endif /* CONFIG_IPV6 */
@@ -1258,7 +1687,7 @@
 	if (data == NULL)
 		return NULL;
 
-	os_get_time(&data->start_time);
+	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;
@@ -1297,6 +1726,23 @@
 		}
 	}
 
+	if (conf->subscr_remediation_url) {
+		data->subscr_remediation_url =
+			os_strdup(conf->subscr_remediation_url);
+	}
+	data->subscr_remediation_method = conf->subscr_remediation_method;
+
+#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;
+		}
+	}
+#endif /* CONFIG_SQLITE */
+
 #ifdef CONFIG_RADIUS_TEST
 	if (conf->dump_msk_file)
 		data->dump_msk_file = os_strdup(conf->dump_msk_file);
@@ -1305,7 +1751,7 @@
 	data->clients = radius_server_read_clients(conf->client_file,
 						   conf->ipv6);
 	if (data->clients == NULL) {
-		printf("No RADIUS clients configured.\n");
+		wpa_printf(MSG_ERROR, "No RADIUS clients configured");
 		radius_server_deinit(data);
 		return NULL;
 	}
@@ -1317,8 +1763,7 @@
 #endif /* CONFIG_IPV6 */
 	data->auth_sock = radius_server_open_socket(conf->auth_port);
 	if (data->auth_sock < 0) {
-		printf("Failed to open UDP socket for RADIUS authentication "
-		       "server\n");
+		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
 		radius_server_deinit(data);
 		return NULL;
 	}
@@ -1329,6 +1774,29 @@
 		return NULL;
 	}
 
+	if (conf->acct_port) {
+#ifdef CONFIG_IPV6
+		if (conf->ipv6)
+			data->acct_sock = radius_server_open_socket6(
+				conf->acct_port);
+		else
+#endif /* CONFIG_IPV6 */
+		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;
+		}
+		if (eloop_register_read_sock(data->acct_sock,
+					     radius_server_receive_acct,
+					     data, NULL)) {
+			radius_server_deinit(data);
+			return NULL;
+		}
+	} else {
+		data->acct_sock = -1;
+	}
+
 	return data;
 }
 
@@ -1347,6 +1815,11 @@
 		close(data->auth_sock);
 	}
 
+	if (data->acct_sock >= 0) {
+		eloop_unregister_read_sock(data->acct_sock);
+		close(data->acct_sock);
+	}
+
 	radius_server_free_clients(data, data->clients);
 
 	os_free(data->pac_opaque_encr_key);
@@ -1356,6 +1829,13 @@
 #ifdef CONFIG_RADIUS_TEST
 	os_free(data->dump_msk_file);
 #endif /* CONFIG_RADIUS_TEST */
+	os_free(data->subscr_remediation_url);
+
+#ifdef CONFIG_SQLITE
+	if (data->db)
+		sqlite3_close(data->db);
+#endif /* CONFIG_SQLITE */
+
 	os_free(data);
 }
 
@@ -1373,7 +1853,7 @@
 	int ret, uptime;
 	unsigned int idx;
 	char *end, *pos;
-	struct os_time now;
+	struct os_reltime now;
 	struct radius_client *cli;
 
 	/* RFC 2619 - RADIUS Authentication Server MIB */
@@ -1384,7 +1864,7 @@
 	pos = buf;
 	end = buf + buflen;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	uptime = (now.sec - data->start_time.sec) * 100 +
 		((now.usec - data->start_time.usec) / 10000) % 100;
 	ret = os_snprintf(pos, end - pos,
@@ -1410,7 +1890,13 @@
 			  "radiusAuthServTotalMalformedAccessRequests=%u\n"
 			  "radiusAuthServTotalBadAuthenticators=%u\n"
 			  "radiusAuthServTotalPacketsDropped=%u\n"
-			  "radiusAuthServTotalUnknownTypes=%u\n",
+			  "radiusAuthServTotalUnknownTypes=%u\n"
+			  "radiusAccServTotalRequests=%u\n"
+			  "radiusAccServTotalInvalidRequests=%u\n"
+			  "radiusAccServTotalResponses=%u\n"
+			  "radiusAccServTotalMalformedRequests=%u\n"
+			  "radiusAccServTotalBadAuthenticators=%u\n"
+			  "radiusAccServTotalUnknownTypes=%u\n",
 			  data->counters.access_requests,
 			  data->counters.invalid_requests,
 			  data->counters.dup_access_requests,
@@ -1420,7 +1906,13 @@
 			  data->counters.malformed_access_requests,
 			  data->counters.bad_authenticators,
 			  data->counters.packets_dropped,
-			  data->counters.unknown_types);
+			  data->counters.unknown_types,
+			  data->counters.acct_requests,
+			  data->counters.invalid_acct_requests,
+			  data->counters.acct_responses,
+			  data->counters.malformed_acct_requests,
+			  data->counters.acct_bad_authenticators,
+			  data->counters.unknown_acct_types);
 	if (ret < 0 || ret >= end - pos) {
 		*pos = '\0';
 		return pos - buf;
@@ -1455,7 +1947,13 @@
 				  "radiusAuthServMalformedAccessRequests=%u\n"
 				  "radiusAuthServBadAuthenticators=%u\n"
 				  "radiusAuthServPacketsDropped=%u\n"
-				  "radiusAuthServUnknownTypes=%u\n",
+				  "radiusAuthServUnknownTypes=%u\n"
+				  "radiusAccServTotalRequests=%u\n"
+				  "radiusAccServTotalInvalidRequests=%u\n"
+				  "radiusAccServTotalResponses=%u\n"
+				  "radiusAccServTotalMalformedRequests=%u\n"
+				  "radiusAccServTotalBadAuthenticators=%u\n"
+				  "radiusAccServTotalUnknownTypes=%u\n",
 				  idx,
 				  abuf, mbuf,
 				  cli->counters.access_requests,
@@ -1466,7 +1964,13 @@
 				  cli->counters.malformed_access_requests,
 				  cli->counters.bad_authenticators,
 				  cli->counters.packets_dropped,
-				  cli->counters.unknown_types);
+				  cli->counters.unknown_types,
+				  cli->counters.acct_requests,
+				  cli->counters.invalid_acct_requests,
+				  cli->counters.acct_responses,
+				  cli->counters.malformed_acct_requests,
+				  cli->counters.acct_bad_authenticators,
+				  cli->counters.unknown_acct_types);
 		if (ret < 0 || ret >= end - pos) {
 			*pos = '\0';
 			return pos - buf;
@@ -1484,9 +1988,16 @@
 {
 	struct radius_session *sess = ctx;
 	struct radius_server_data *data = sess->server;
+	int ret;
 
-	return data->get_eap_user(data->conf_ctx, identity, identity_len,
-				  phase2, user);
+	ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+				 phase2, user);
+	if (ret == 0 && user) {
+		sess->accept_attr = user->accept_attr;
+		sess->remediation = user->remediation;
+		sess->macacl = user->macacl;
+	}
+	return ret;
 }
 
 
@@ -1499,10 +2010,18 @@
 }
 
 
+static void radius_server_log_msg(void *ctx, const char *msg)
+{
+	struct radius_session *sess = ctx;
+	srv_log(sess, "EAP: %s", msg);
+}
+
+
 static struct eapol_callbacks radius_server_eapol_cb =
 {
 	.get_eap_user = radius_server_get_eap_user,
 	.get_eap_req_id_text = radius_server_get_eap_req_id_text,
+	.log_msg = radius_server_log_msg,
 };
 
 
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 284bd59..46ac312 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -22,6 +22,11 @@
 	int auth_port;
 
 	/**
+	 * acct_port - UDP port to listen to as an accounting server
+	 */
+	int acct_port;
+
+	/**
 	 * client_file - RADIUS client configuration file
 	 *
 	 * This file contains the RADIUS clients and the shared secret to be
@@ -35,6 +40,11 @@
 	char *client_file;
 
 	/**
+	 * sqlite_file - SQLite database for storing debug log information
+	 */
+	const char *sqlite_file;
+
+	/**
 	 * conf_ctx - Context pointer for callbacks
 	 *
 	 * This is used as the ctx argument in get_eap_user() calls.
@@ -204,6 +214,9 @@
 #ifdef CONFIG_RADIUS_TEST
 	const char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
+
+	char *subscr_remediation_url;
+	u8 subscr_remediation_method;
 };
 
 
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index 9c41962..adfd3df 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index 789ac25..aab8b7e 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -516,7 +516,6 @@
 	struct wpa_peerkey *peerkey;
 	struct wpa_eapol_ie_parse kde;
 	u32 lifetime;
-	struct os_time now;
 
 	if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
 		wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
@@ -568,10 +567,8 @@
 	lifetime = WPA_GET_BE32(kde.lifetime);
 	wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
 	if (lifetime > 1000000000)
-		lifetime = 1000000000; /* avoid overflowing expiration time */
+		lifetime = 1000000000; /* avoid overflowing eloop time */
 	peerkey->lifetime = lifetime;
-	os_get_time(&now);
-	peerkey->expiration = now.sec + lifetime;
 	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
 			       sm, peerkey);
 
@@ -656,11 +653,11 @@
 static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
 					      struct wpa_peerkey *peerkey,
 					      const struct wpa_eapol_key *key,
-					      u16 ver)
+					      u16 ver, const u8 *key_data,
+					      size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse ie;
-	const u8 *kde;
-	size_t len, kde_buf_len;
+	size_t kde_buf_len;
 	struct wpa_ptk *stk;
 	u8 buf[8], *kde_buf, *pos;
 	be32 lifetime;
@@ -671,14 +668,13 @@
 	os_memset(&ie, 0, sizeof(ie));
 
 	/* RSN: msg 1/4 should contain SMKID for the selected SMK */
-	kde = (const u8 *) (key + 1);
-	len = WPA_GET_BE16(key->key_data_length);
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len);
-	if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) {
+	wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 ||
+	    ie.pmkid == NULL) {
 		wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
 		return;
 	}
-	if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+	if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
 		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
 			    ie.pmkid, PMKID_LEN);
 		return;
@@ -736,7 +732,6 @@
 					       struct wpa_eapol_ie_parse *kde)
 {
 	u32 lifetime;
-	struct os_time now;
 
 	if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
 		return;
@@ -755,8 +750,6 @@
 		   lifetime, peerkey->lifetime);
 	peerkey->lifetime = lifetime;
 
-	os_get_time(&now);
-	peerkey->expiration = now.sec + lifetime;
 	eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
 	eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
 			       sm, peerkey);
@@ -766,11 +759,10 @@
 static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
 					      struct wpa_peerkey *peerkey,
 					      const struct wpa_eapol_key *key,
-					      u16 ver)
+					      u16 ver, const u8 *key_data,
+					      size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse kde;
-	const u8 *keydata;
-	size_t len;
 
 	wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
 		   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
@@ -779,16 +771,14 @@
 
 	/* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
 	 * from the peer. It may also include Lifetime KDE. */
-	keydata = (const u8 *) (key + 1);
-	len = WPA_GET_BE16(key->key_data_length);
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len);
-	if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 ||
+	wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 ||
 	    kde.pmkid == NULL || kde.rsn_ie == NULL) {
 		wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
 		return;
 	}
 
-	if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+	if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
 		wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
 			    kde.pmkid, PMKID_LEN);
 		return;
@@ -815,11 +805,11 @@
 static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
 					      struct wpa_peerkey *peerkey,
 					      const struct wpa_eapol_key *key,
-					      u16 ver)
+					      u16 ver, const u8 *key_data,
+					      size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse kde;
-	const u8 *keydata;
-	size_t len, key_len;
+	size_t key_len;
 	const u8 *_key;
 	u8 key_buf[32], rsc[6];
 
@@ -830,10 +820,8 @@
 
 	/* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
 	 * Lifetime KDE. */
-	keydata = (const u8 *) (key + 1);
-	len = WPA_GET_BE16(key->key_data_length);
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len);
-	if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) {
+	wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
 		wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
 			   "STK 3/4");
 		return;
@@ -864,7 +852,7 @@
 
 	if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
 				       WPA_GET_BE16(key->key_info),
-				       NULL, 0, &peerkey->stk))
+				       &peerkey->stk))
 		return;
 
 	_key = (u8 *) peerkey->stk.tk1;
@@ -941,7 +929,7 @@
 		os_memset(key->key_mic, 0, 16);
 		wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len,
 				  key->key_mic);
-		if (os_memcmp(mic, key->key_mic, 16) != 0) {
+		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "when using TSTK - ignoring TSTK");
 		} else {
@@ -957,7 +945,7 @@
 		os_memset(key->key_mic, 0, 16);
 		wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len,
 				  key->key_mic);
-		if (os_memcmp(mic, key->key_mic, 16) != 0) {
+		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "- dropping packet");
 			return -1;
@@ -1116,28 +1104,32 @@
 	while (peerkey) {
 		prev = peerkey;
 		peerkey = peerkey->next;
-		os_free(prev);
+		wpa_supplicant_peerkey_free(sm, prev);
 	}
 	sm->peerkey = NULL;
 }
 
 
 void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-			   struct wpa_eapol_key *key, u16 key_info, u16 ver)
+			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
+			   const u8 *key_data, size_t key_data_len)
 {
 	if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
 	    (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
 		/* 3/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver);
+		wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver,
+						  key_data, key_data_len);
 	} else if (key_info & WPA_KEY_INFO_ACK) {
 		/* 1/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver);
+		wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver,
+						  key_data, key_data_len);
 	} else if (key_info & WPA_KEY_INFO_SECURE) {
 		/* 4/4 STK 4-Way Handshake */
 		wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
 	} else {
 		/* 2/4 STK 4-Way Handshake */
-		wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver);
+		wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver,
+						  key_data, key_data_len);
 	}
 }
 
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
index b8845f7..4c17eae 100644
--- a/src/rsn_supp/peerkey.h
+++ b/src/rsn_supp/peerkey.h
@@ -24,7 +24,6 @@
 	int smk_complete;
 	u8 smkid[PMKID_LEN];
 	u32 lifetime;
-	os_time_t expiration;
 	int cipher; /* Selected cipher (WPA_CIPHER_*) */
 	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int replay_counter_set;
@@ -42,7 +41,8 @@
 				 struct wpa_eapol_key *key, u16 ver,
 				 const u8 *buf, size_t len);
 void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-			   struct wpa_eapol_key *key, u16 key_info, u16 ver);
+			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
+			   const u8 *key_data, size_t key_data_len);
 void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
 			  struct wpa_eapol_key *key, size_t extra_len,
 			  u16 key_info, u16 ver);
@@ -61,7 +61,8 @@
 
 static inline void
 peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-		      struct wpa_eapol_key *key, u16 key_info, u16 ver)
+		      struct wpa_eapol_key *key, u16 key_info, u16 ver,
+		      const u8 *key_data, size_t key_data_len)
 {
 }
 
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 33fa1a2..b5a87fc 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -53,9 +53,9 @@
 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
 		struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 		pmksa->pmksa = entry->next;
@@ -80,13 +80,13 @@
 {
 	int sec;
 	struct rsn_pmksa_cache_entry *entry;
-	struct os_time now;
+	struct os_reltime now;
 
 	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
 	eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
 	if (pmksa->pmksa == NULL)
 		return;
-	os_get_time(&now);
+	os_get_reltime(&now);
 	sec = pmksa->pmksa->expiration - now.sec;
 	if (sec < 0)
 		sec = 0;
@@ -125,7 +125,7 @@
 		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
 {
 	struct rsn_pmksa_cache_entry *entry, *pos, *prev;
-	struct os_time now;
+	struct os_reltime now;
 
 	if (pmk_len > PMK_LEN)
 		return NULL;
@@ -137,7 +137,7 @@
 	entry->pmk_len = pmk_len;
 	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
 		  wpa_key_mgmt_sha256(akmp));
-	os_get_time(&now);
+	os_get_reltime(&now);
 	entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
 	entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
 		pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
@@ -152,9 +152,9 @@
 	while (pos) {
 		if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
 			if (pos->pmk_len == pmk_len &&
-			    os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
-			    os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
-			    0) {
+			    os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
+			    os_memcmp_const(pos->pmkid, entry->pmkid,
+					    PMKID_LEN) == 0) {
 				wpa_printf(MSG_DEBUG, "WPA: reusing previous "
 					   "PMKSA entry");
 				os_free(entry);
@@ -466,9 +466,9 @@
 	int i, ret;
 	char *pos = buf;
 	struct rsn_pmksa_cache_entry *entry;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	ret = os_snprintf(pos, buf + len - pos,
 			  "Index / AA / PMKID / expiration (in seconds) / "
 			  "opportunistic\n");
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index c51620e..915f85e 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -70,13 +70,14 @@
 }
 
 
-static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
+static void rsn_preauth_eapol_cb(struct eapol_sm *eapol,
+				 enum eapol_supp_result result,
 				 void *ctx)
 {
 	struct wpa_sm *sm = ctx;
 	u8 pmk[PMK_LEN];
 
-	if (success) {
+	if (result == EAPOL_SUPP_RESULT_SUCCESS) {
 		int res, pmk_len;
 		pmk_len = PMK_LEN;
 		res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
@@ -100,13 +101,14 @@
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"RSN: failed to get master session key from "
 				"pre-auth EAPOL state machines");
-			success = 0;
+			result = EAPOL_SUPP_RESULT_FAILURE;
 		}
 	}
 
 	wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
 		MACSTR " %s", MAC2STR(sm->preauth_bssid),
-		success ? "completed successfully" : "failed");
+		result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" :
+		"failed");
 
 	rsn_preauth_deinit(sm);
 	rsn_preauth_candidate_process(sm);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index e911ad0..cd34223 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -33,6 +33,7 @@
 #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
 #define TDLS_TESTING_DECLINE_RESP BIT(9)
 #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
+#define TDLS_TESTING_WRONG_MIC BIT(11)
 unsigned int tdls_testing = 0;
 #endif /* CONFIG_TDLS_TESTING */
 
@@ -83,6 +84,8 @@
 static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
 static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
 				       struct wpa_tdls_peer *peer);
+static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
+				  u16 reason_code);
 
 
 #define TDLS_MAX_IE_LEN 80
@@ -118,6 +121,7 @@
 		u8 action_code; /* TDLS frame type */
 		u8 dialog_token;
 		u16 status_code;
+		u32 peer_capab;
 		int buf_len;    /* length of TPK message for retransmission */
 		u8 *buf;        /* buffer for TPK message */
 	} sm_tmr;
@@ -136,6 +140,14 @@
 
 	u8 *ext_capab;
 	size_t ext_capab_len;
+
+	u8 *supp_channels;
+	size_t supp_channels_len;
+
+	u8 *supp_oper_classes;
+	size_t supp_oper_classes_len;
+
+	u8 wmm_capable;
 };
 
 
@@ -205,26 +217,27 @@
 
 static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
 				 u8 action_code, u8 dialog_token,
-				 u16 status_code, const u8 *buf, size_t len)
+				 u16 status_code, u32 peer_capab,
+				 const u8 *buf, size_t len)
 {
 	return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
-				     status_code, buf, len);
+				     status_code, peer_capab, buf, len);
 }
 
 
 static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
-			     u8 dialog_token, u16 status_code,
+			     u8 dialog_token, u16 status_code, u32 peer_capab,
 			     const u8 *msg, size_t msg_len)
 {
 	struct wpa_tdls_peer *peer;
 
 	wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
-		   "dialog_token=%u status_code=%u msg_len=%u",
+		   "dialog_token=%u status_code=%u peer_capab=%u msg_len=%u",
 		   MAC2STR(dest), action_code, dialog_token, status_code,
-		   (unsigned int) msg_len);
+		   peer_capab, (unsigned int) msg_len);
 
 	if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
-				  status_code, msg, msg_len)) {
+				  status_code, peer_capab, msg, msg_len)) {
 		wpa_printf(MSG_INFO, "TDLS: Failed to send message "
 			   "(action_code=%u)", action_code);
 		return -1;
@@ -262,6 +275,7 @@
 	peer->sm_tmr.action_code = action_code;
 	peer->sm_tmr.dialog_token = dialog_token;
 	peer->sm_tmr.status_code = status_code;
+	peer->sm_tmr.peer_capab = peer_capab;
 	peer->sm_tmr.buf_len = msg_len;
 	os_free(peer->sm_tmr.buf);
 	peer->sm_tmr.buf = os_malloc(msg_len);
@@ -318,6 +332,7 @@
 					  peer->sm_tmr.action_code,
 					  peer->sm_tmr.dialog_token,
 					  peer->sm_tmr.status_code,
+					  peer->sm_tmr.peer_capab,
 					  peer->sm_tmr.buf,
 					  peer->sm_tmr.buf_len)) {
 			wpa_printf(MSG_INFO, "TDLS: Failed to retry "
@@ -549,7 +564,7 @@
 		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
 				  peer->rsnie_p, timeoutie, (u8 *) ftie,
 				  mic);
-		if (os_memcmp(mic, ftie->mic, 16) != 0) {
+		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
 				   "dropping packet");
 			wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
@@ -576,7 +591,7 @@
 	if (peer->tpk_set) {
 		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
 					  dtoken, lnkid, (u8 *) ftie, mic);
-		if (os_memcmp(mic, ftie->mic, 16) != 0) {
+		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
 				   "dropping packet");
 			return -1;
@@ -616,7 +631,33 @@
 }
 
 
-static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
+					   struct wpa_tdls_peer *peer)
+{
+	struct wpa_tdls_peer *cur, *prev;
+
+	cur = sm->tdls;
+	prev = NULL;
+	while (cur && cur != peer) {
+		prev = cur;
+		cur = cur->next;
+	}
+
+	if (cur != peer) {
+		wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
+			   " to remove it from the list",
+			   MAC2STR(peer->addr));
+		return;
+	}
+
+	if (prev)
+		prev->next = peer->next;
+	else
+		sm->tdls = peer->next;
+}
+
+
+static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 {
 	wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
 		   MAC2STR(peer->addr));
@@ -633,8 +674,14 @@
 	peer->vht_capabilities = NULL;
 	os_free(peer->ext_capab);
 	peer->ext_capab = NULL;
+	os_free(peer->supp_channels);
+	peer->supp_channels = NULL;
+	os_free(peer->supp_oper_classes);
+	peer->supp_oper_classes = NULL;
 	peer->rsnie_i_len = peer->rsnie_p_len = 0;
 	peer->cipher = 0;
+	peer->qos_info = 0;
+	peer->wmm_capable = 0;
 	peer->tpk_set = peer->tpk_success = 0;
 	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
 	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
@@ -642,6 +689,14 @@
 }
 
 
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+	wpa_tdls_peer_clear(sm, peer);
+	wpa_tdls_peer_remove_from_list(sm, peer);
+	os_free(peer);
+}
+
+
 static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
 			    struct wpa_tdls_lnkid *lnkid)
 {
@@ -658,7 +713,8 @@
 }
 
 
-int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
+				  u16 reason_code)
 {
 	struct wpa_tdls_peer *peer;
 	struct wpa_tdls_ftie *ftie;
@@ -737,12 +793,9 @@
 
 	/* request driver to send Teardown using this FTIE */
 	wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
-			  reason_code, rbuf, pos - rbuf);
+			  reason_code, 0, rbuf, pos - rbuf);
 	os_free(rbuf);
 
-	/* clear the Peerkey statemachine */
-	wpa_tdls_peer_free(sm, peer);
-
 	return 0;
 }
 
@@ -783,7 +836,7 @@
 }
 
 
-void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
 {
 	struct wpa_tdls_peer *peer;
 
@@ -792,8 +845,47 @@
 			break;
 	}
 
-	if (peer)
+	if (!peer || !peer->tpk_success) {
+		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+			   " not connected - cannot teardown unreachable link",
+			   MAC2STR(addr));
+		return;
+	}
+
+	if (wpa_tdls_is_external_setup(sm)) {
+		/*
+		 * Disable the link, send a teardown packet through the
+		 * AP, and then reset link data.
+		 */
+		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+		wpa_tdls_send_teardown(sm, addr,
+				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
+		wpa_tdls_peer_free(sm, peer);
+	} else {
 		wpa_tdls_disable_peer_link(sm, peer);
+	}
+}
+
+
+const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return "disabled";
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL)
+		return "peer does not exist";
+
+	if (!peer->tpk_success)
+		return "peer not connected";
+
+	return "connected";
 }
 
 
@@ -886,7 +978,7 @@
 		   " (action=%u status=%u)",
 		   MAC2STR(dst), tdls_action, status);
 	return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
-				 NULL, 0);
+				 0, NULL, 0);
 }
 
 
@@ -1092,7 +1184,7 @@
 		   MAC2STR(peer->addr));
 
 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
-				   1, 0, rbuf, pos - rbuf);
+				   1, 0, 0, rbuf, pos - rbuf);
 	os_free(rbuf);
 
 	return status;
@@ -1173,10 +1265,16 @@
 	/* compute MIC before sending */
 	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+		ftie->mic[0] ^= 0x01;
+	}
+#endif /* CONFIG_TDLS_TESTING */
 
 skip_ies:
 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
-				   dtoken, 0, rbuf, pos - rbuf);
+				   dtoken, 0, 0, rbuf, pos - rbuf);
 	os_free(rbuf);
 
 	return status;
@@ -1194,6 +1292,7 @@
 	struct wpa_tdls_timeoutie timeoutie;
 	u32 lifetime;
 	int status;
+	u32 peer_capab = 0;
 
 	buf_len = 0;
 	if (wpa_tdls_get_privacy(sm)) {
@@ -1255,10 +1354,24 @@
 	/* compute MIC before sending */
 	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+		ftie->mic[0] ^= 0x01;
+	}
+#endif /* CONFIG_TDLS_TESTING */
 
 skip_ies:
+
+	if (peer->vht_capabilities)
+		peer_capab |= TDLS_PEER_VHT;
+	if (peer->ht_capabilities)
+		peer_capab |= TDLS_PEER_HT;
+	if (peer->wmm_capable)
+		peer_capab |= TDLS_PEER_WMM;
+
 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
-				   dtoken, 0, rbuf, pos - rbuf);
+				   dtoken, 0, peer_capab, rbuf, pos - rbuf);
 	os_free(rbuf);
 
 	return status;
@@ -1273,7 +1386,7 @@
 		   "(peer " MACSTR ")", MAC2STR(peer->addr));
 
 	return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
-				 dialog_token, 0, NULL, 0);
+				 dialog_token, 0, 0, NULL, 0);
 }
 
 
@@ -1299,10 +1412,17 @@
 
 	dialog_token = buf[sizeof(struct wpa_tdls_frame)];
 
+	/*
+	 * Some APs will tack on a weird IE to the end of a TDLS
+	 * discovery request packet. This needn't fail the response,
+	 * since the required IE are verified separately.
+	 */
 	if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
 				     len - (sizeof(struct wpa_tdls_frame) + 1),
-				     &kde) < 0)
-		return -1;
+				     &kde) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
+	}
 
 	if (!kde.lnkid) {
 		wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
@@ -1334,7 +1454,7 @@
 	wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
 		   MACSTR, MAC2STR(addr));
 	return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
-				 1, 0, NULL, 0);
+				 1, 0, 0, NULL, 0);
 }
 
 
@@ -1434,6 +1554,100 @@
 }
 
 
+static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
+			       struct wpa_tdls_peer *peer)
+{
+	struct wmm_information_element *wmm;
+
+	if (!kde->wmm) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
+		return 0;
+	}
+
+	if (kde->wmm_len < sizeof(struct wmm_information_element)) {
+		wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
+		return -1;
+	}
+
+	wmm = (struct wmm_information_element *) kde->wmm;
+	peer->qos_info = wmm->qos_info;
+
+	peer->wmm_capable = 1;
+
+	wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
+	return 0;
+}
+
+
+static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
+				   struct wpa_tdls_peer *peer)
+{
+	if (!kde->supp_channels) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
+		return 0;
+	}
+
+	if (!peer->supp_channels ||
+	    peer->supp_channels_len < kde->supp_channels_len) {
+		os_free(peer->supp_channels);
+		peer->supp_channels = os_zalloc(kde->supp_channels_len);
+		if (peer->supp_channels == NULL)
+			return -1;
+	}
+
+	peer->supp_channels_len = kde->supp_channels_len;
+
+	os_memcpy(peer->supp_channels, kde->supp_channels,
+		  peer->supp_channels_len);
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
+		    (u8 *) peer->supp_channels, peer->supp_channels_len);
+	return 0;
+}
+
+
+static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
+				       struct wpa_tdls_peer *peer)
+{
+	if (!kde->supp_oper_classes) {
+		wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
+		return 0;
+	}
+
+	if (!peer->supp_oper_classes ||
+	    peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
+		os_free(peer->supp_oper_classes);
+		peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
+		if (peer->supp_oper_classes == NULL)
+			return -1;
+	}
+
+	peer->supp_oper_classes_len = kde->supp_oper_classes_len;
+	os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
+		  peer->supp_oper_classes_len);
+	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
+		    (u8 *) peer->supp_oper_classes,
+		    peer->supp_oper_classes_len);
+	return 0;
+}
+
+
+static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+				int add)
+{
+	return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
+				       peer->capability,
+				       peer->supp_rates, peer->supp_rates_len,
+				       peer->ht_capabilities,
+				       peer->vht_capabilities,
+				       peer->qos_info, peer->ext_capab,
+				       peer->ext_capab_len,
+				       peer->supp_channels,
+				       peer->supp_channels_len,
+				       peer->supp_oper_classes,
+				       peer->supp_oper_classes_len);
+}
+
+
 static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
 				   const u8 *buf, size_t len)
 {
@@ -1481,16 +1695,16 @@
 			wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
 				   "direct link is enabled - tear down the "
 				   "old link first");
-			wpa_tdls_disable_peer_link(sm, peer);
-		}
-
-		/*
-		 * An entry is already present, so check if we already sent a
-		 * TDLS Setup Request. If so, compare MAC addresses and let the
-		 * STA with the lower MAC address continue as the initiator.
-		 * The other negotiation is terminated.
-		 */
-		if (peer->initiator) {
+			wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+			wpa_tdls_peer_clear(sm, peer);
+		} else if (peer->initiator) {
+			/*
+			 * An entry is already present, so check if we already
+			 * sent a TDLS Setup Request. If so, compare MAC
+			 * addresses and let the STA with the lower MAC address
+			 * continue as the initiator. The other negotiation is
+			 * terminated.
+			 */
 			if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
 				wpa_printf(MSG_DEBUG, "TDLS: Discard request "
 					   "from peer with higher address "
@@ -1502,7 +1716,9 @@
 					   MACSTR " (terminate previously "
 					   "initiated negotiation",
 					   MAC2STR(src_addr));
-				wpa_tdls_disable_peer_link(sm, peer);
+				wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+						 peer->addr);
+				wpa_tdls_peer_clear(sm, peer);
 			}
 		}
 	}
@@ -1546,8 +1762,18 @@
 	if (copy_peer_ext_capab(&kde, peer) < 0)
 		goto error;
 
+	if (copy_peer_supp_channels(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
+		goto error;
+
 	peer->qos_info = kde.qosinfo;
 
+	/* Overwrite with the qos_info obtained in WMM IE */
+	if (copy_peer_wmm_capab(&kde, peer) < 0)
+		goto error;
+
 	peer->aid = kde.aid;
 
 #ifdef CONFIG_TDLS_TESTING
@@ -1682,7 +1908,6 @@
 		if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
 			wpa_msg(sm->ctx->ctx, MSG_WARNING,
 				"TDLS: Failed to get random data for responder nonce");
-			wpa_tdls_peer_free(sm, peer);
 			goto error;
 		}
 	}
@@ -1737,14 +1962,15 @@
 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
 
 skip_rsn_check:
-	/* add the peer to the driver as a "setup in progress" peer */
-	wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0,
-				NULL, 0);
+	/* add supported rates, capabilities, and qos_info to the TDLS peer */
+	if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
+		goto error;
+
 	peer->tpk_in_progress = 1;
 
 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
 	if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
-		wpa_tdls_disable_peer_link(sm, peer);
+		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
 		goto error;
 	}
 
@@ -1753,6 +1979,8 @@
 error:
 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
 			    status);
+	if (peer)
+		wpa_tdls_peer_free(sm, peer);
 	return -1;
 }
 
@@ -1781,16 +2009,6 @@
 #endif /* CONFIG_TDLS_TESTING */
 	}
 
-	/* add supported rates, capabilities, and qos_info to the TDLS peer */
-	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid,
-				    peer->capability,
-				    peer->supp_rates, peer->supp_rates_len,
-				    peer->ht_capabilities,
-				    peer->vht_capabilities,
-				    peer->qos_info, peer->ext_capab,
-				    peer->ext_capab_len) < 0)
-		return -1;
-
 	if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
 		wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
 			   "driver");
@@ -1817,7 +2035,7 @@
 	int ielen;
 	u16 status;
 	const u8 *pos;
-	int ret;
+	int ret = 0;
 
 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
@@ -1916,8 +2134,18 @@
 	if (copy_peer_ext_capab(&kde, peer) < 0)
 		goto error;
 
+	if (copy_peer_supp_channels(&kde, peer) < 0)
+		goto error;
+
+	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
+		goto error;
+
 	peer->qos_info = kde.qosinfo;
 
+	/* Overwrite with the qos_info obtained in WMM IE */
+	if (copy_peer_wmm_capab(&kde, peer) < 0)
+		goto error;
+
 	peer->aid = kde.aid;
 
 	if (!wpa_tdls_get_privacy(sm)) {
@@ -1935,6 +2163,13 @@
 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
 		    kde.rsn_ie, kde.rsn_ie_len);
 
+	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+		wpa_printf(MSG_INFO,
+			   "TDLS: Too long Responder RSN IE in TPK M2");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto error;
+	}
+
 	/*
 	 * FIX: bitwise comparison of RSN IE is not the correct way of
 	 * validation this. It can be different, but certain fields must
@@ -2027,18 +2262,28 @@
 skip_rsn:
 	peer->dtoken = dtoken;
 
+	/* add supported rates, capabilities, and qos_info to the TDLS peer */
+	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+		goto error;
+
 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
 		   "TPK Handshake Message 3");
-	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) {
-		wpa_tdls_disable_peer_link(sm, peer);
-		return -1;
-	}
+	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
+		goto error;
 
-	ret = wpa_tdls_enable_link(sm, peer);
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
-		wpa_tdls_do_teardown(sm, peer,
-				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	if (!peer->tpk_success) {
+		/*
+		 * Enable Link only when tpk_success is 0, signifying that this
+		 * processing of TPK M2 frame is not because of a retransmission
+		 * during TDLS setup handshake.
+		 */
+		ret = wpa_tdls_enable_link(sm, peer);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
+			wpa_tdls_do_teardown(
+				sm, peer,
+				WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+		}
 	}
 	return ret;
 
@@ -2062,7 +2307,7 @@
 	u16 status;
 	const u8 *pos;
 	u32 lifetime;
-	int ret;
+	int ret = 0;
 
 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
@@ -2092,9 +2337,16 @@
 	pos += 2 /* status code */ + 1 /* dialog token */;
 
 	ielen = len - (pos - buf); /* start of IE in buf */
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs piggy-back broken IEs to the end
+	 * of a TDLS Confirm packet, which will fail the link if we don't ignore
+	 * this error.
+	 */
 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
-		wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
-		goto error;
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
 	}
 
 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
@@ -2179,15 +2431,25 @@
 	}
 
 skip_rsn:
-	ret = wpa_tdls_enable_link(sm, peer);
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
-		wpa_tdls_do_teardown(sm, peer,
-				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	/* add supported rates, capabilities, and qos_info to the TDLS peer */
+	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+		goto error;
+
+	if (!peer->tpk_success) {
+		/*
+		 * Enable Link only when tpk_success is 0, signifying that this
+		 * processing of TPK M3 frame is not because of a retransmission
+		 * during TDLS setup handshake.
+		 */
+		ret = wpa_tdls_enable_link(sm, peer);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
+			goto error;
+		}
 	}
 	return ret;
 error:
-	wpa_tdls_disable_peer_link(sm, peer);
+	wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
 	return -1;
 }
 
@@ -2250,8 +2512,11 @@
 	peer->initiator = 1;
 
 	/* add the peer to the driver as a "setup in progress" peer */
-	wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0,
-				NULL, 0);
+	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+				    NULL, 0, NULL, 0, NULL, 0, NULL, 0)) {
+		wpa_tdls_disable_peer_link(sm, peer);
+		return -1;
+	}
 
 	peer->tpk_in_progress = 1;
 
@@ -2284,7 +2549,8 @@
 		 * Disable previous link to allow renegotiation to be completed
 		 * on AP path.
 		 */
-		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
 	}
 }
 
@@ -2400,22 +2666,23 @@
 
 void wpa_tdls_teardown_peers(struct wpa_sm *sm)
 {
-	struct wpa_tdls_peer *peer;
+	struct wpa_tdls_peer *peer, *tmp;
 
 	peer = sm->tdls;
 
 	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
 
 	while (peer) {
+		tmp = peer->next;
 		wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
 			   MAC2STR(peer->addr));
 		if (sm->tdls_external_setup)
-			wpa_tdls_send_teardown(sm, peer->addr,
-					       WLAN_REASON_DEAUTH_LEAVING);
+			wpa_tdls_do_teardown(sm, peer,
+					     WLAN_REASON_DEAUTH_LEAVING);
 		else
 			wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
 
-		peer = peer->next;
+		peer = tmp;
 	}
 }
 
@@ -2425,7 +2692,6 @@
 	struct wpa_tdls_peer *peer, *tmp;
 
 	peer = sm->tdls;
-	sm->tdls = NULL;
 
 	while (peer) {
 		int res;
@@ -2434,7 +2700,6 @@
 		wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
 			   MAC2STR(peer->addr), res);
 		wpa_tdls_peer_free(sm, peer);
-		os_free(peer);
 		peer = tmp;
 	}
 }
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 292255c..d6fb6a1 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -89,7 +89,10 @@
 	int key_info, ver;
 	u8 bssid[ETH_ALEN], *rbuf;
 
-	if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt))
+	if (sm->key_mgmt == WPA_KEY_MGMT_OSEN)
+		ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+	else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
+		 wpa_key_mgmt_sha256(sm->key_mgmt))
 		ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
 	else if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
@@ -107,7 +110,8 @@
 	if (rbuf == NULL)
 		return;
 
-	reply->type = sm->proto == WPA_PROTO_RSN ?
+	reply->type = (sm->proto == WPA_PROTO_RSN ||
+		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info = WPA_KEY_INFO_REQUEST | ver;
 	if (sm->ptk_set)
@@ -158,7 +162,7 @@
 	}
 
 	if (pmkid && sm->cur_pmksa &&
-	    os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+	    os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
 		wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
 		wpa_sm_set_pmk_from_pmksa(sm);
 		wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
@@ -231,7 +235,8 @@
 	}
 
 	if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
-	    !wpa_key_mgmt_ft(sm->key_mgmt)) {
+	    !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
+	{
 		/* Send EAPOL-Start to trigger full EAP authentication. */
 		u8 *buf;
 		size_t buflen;
@@ -325,11 +330,12 @@
 		return -1;
 	}
 
-	reply->type = sm->proto == WPA_PROTO_RSN ?
+	reply->type = (sm->proto == WPA_PROTO_RSN ||
+		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	WPA_PUT_BE16(reply->key_info,
 		     ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
-	if (sm->proto == WPA_PROTO_RSN)
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
 		os_memcpy(reply->key_length, key->key_length, 2);
@@ -356,7 +362,7 @@
 			  const struct wpa_eapol_key *key,
 			  struct wpa_ptk *ptk)
 {
-	size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
+	size_t ptk_len = wpa_cipher_key_len(sm->pairwise_cipher) + 32;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt))
 		return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
@@ -373,12 +379,14 @@
 static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 					  const unsigned char *src_addr,
 					  const struct wpa_eapol_key *key,
-					  u16 ver)
+					  u16 ver, const u8 *key_data,
+					  size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse ie;
 	struct wpa_ptk *ptk;
-	u8 buf[8];
 	int res;
+	u8 *kde, *kde_buf = NULL;
+	size_t kde_len;
 
 	if (wpa_sm_get_network_ctx(sm) == NULL) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
@@ -392,12 +400,11 @@
 
 	os_memset(&ie, 0, sizeof(ie));
 
-	if (sm->proto == WPA_PROTO_RSN) {
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
 		/* RSN: msg 1/4 should contain PMKID for the selected PMK */
-		const u8 *_buf = (const u8 *) (key + 1);
-		size_t len = WPA_GET_BE16(key->key_data_length);
-		wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len);
-		if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0)
+		wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+			    key_data, key_data_len);
+		if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
 			goto failed;
 		if (ie.pmkid) {
 			wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
@@ -429,21 +436,49 @@
 	 * been verified when processing message 3/4. */
 	ptk = &sm->tptk;
 	wpa_derive_ptk(sm, src_addr, key, ptk);
-	/* Supplicant: swap tx/rx Mic keys */
-	os_memcpy(buf, ptk->u.auth.tx_mic_key, 8);
-	os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
-	os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+	if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
+		u8 buf[8];
+		/* Supplicant: swap tx/rx Mic keys */
+		os_memcpy(buf, ptk->u.auth.tx_mic_key, 8);
+		os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
+		os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+		os_memset(buf, 0, sizeof(buf));
+	}
 	sm->tptk_set = 1;
 
+	kde = sm->assoc_wpa_ie;
+	kde_len = sm->assoc_wpa_ie_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;
+		}
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
-				       sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
-				       ptk))
+				       kde, kde_len, ptk))
 		goto failed;
 
+	os_free(kde_buf);
 	os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
 	return;
 
 failed:
+	os_free(kde_buf);
 	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
@@ -535,7 +570,7 @@
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 
-	if (sm->proto == WPA_PROTO_RSN) {
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
 		key_rsc = null_rsc;
 	} else {
 		key_rsc = key->key_rsc;
@@ -623,6 +658,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));
 			return -1;
 		}
 	} else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
@@ -632,8 +668,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));
 		return -1;
 	}
+	os_memset(gtk_buf, 0, sizeof(gtk_buf));
 
 	return 0;
 }
@@ -688,14 +726,17 @@
 	os_memcpy(gd.gtk, gtk, gtk_len);
 	gd.gtk_len = gtk_len;
 
-	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
-					      gtk_len, gtk_len,
-					      &gd.key_rsc_len, &gd.alg) ||
-	    wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
+	if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&
+	    (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					       gtk_len, gtk_len,
+					       &gd.key_rsc_len, &gd.alg) ||
+	     wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"RSN: Failed to install GTK");
+		os_memset(&gd, 0, sizeof(gd));
 		return -1;
 	}
+	os_memset(&gd, 0, sizeof(gd));
 
 	wpa_supplicant_key_neg_complete(sm, sm->bssid,
 					key_info & WPA_KEY_INFO_SECURE);
@@ -707,13 +748,15 @@
 			       struct wpa_eapol_ie_parse *ie)
 {
 #ifdef CONFIG_IEEE80211W
-	if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+	if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
 		return 0;
 
 	if (ie->igtk) {
+		size_t len;
 		const struct wpa_igtk_kde *igtk;
 		u16 keyidx;
-		if (ie->igtk_len != sizeof(*igtk))
+		len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+		if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
 			return -1;
 		igtk = (const struct wpa_igtk_kde *) ie->igtk;
 		keyidx = WPA_GET_LE16(igtk->keyid);
@@ -721,15 +764,16 @@
 			"pn %02x%02x%02x%02x%02x%02x",
 			keyidx, MAC2STR(igtk->pn));
 		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
-				igtk->igtk, WPA_IGTK_LEN);
+				igtk->igtk, len);
 		if (keyidx > 4095) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid IGTK KeyID %d", keyidx);
 			return -1;
 		}
-		if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
+		if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+				   broadcast_ether_addr,
 				   keyidx, 0, igtk->pn, sizeof(igtk->pn),
-				   igtk->igtk, WPA_IGTK_LEN) < 0) {
+				   igtk->igtk, len) < 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Failed to configure IGTK to the driver");
 			return -1;
@@ -862,7 +906,8 @@
 		return -1;
 	}
 
-	if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) {
+	if (os_memcmp_const(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
+	{
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"FT: PMKR1Name mismatch in "
 			"FT 4-way handshake message 3/4");
@@ -982,45 +1027,37 @@
  * @key: Pointer to the EAPOL-Key frame header
  * @ver: Version bits from EAPOL-Key Key Info
  * @key_info: Key Info
- * @kde: KDEs to include the EAPOL-Key frame
- * @kde_len: Length of KDEs
  * @ptk: PTK to use for keyed hash and encryption
  * Returns: 0 on success, -1 on failure
  */
 int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
 			       const struct wpa_eapol_key *key,
 			       u16 ver, u16 key_info,
-			       const u8 *kde, size_t kde_len,
 			       struct wpa_ptk *ptk)
 {
 	size_t rlen;
 	struct wpa_eapol_key *reply;
 	u8 *rbuf;
 
-	if (kde)
-		wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len);
-
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*reply) + kde_len,
-				  &rlen, (void *) &reply);
+				  sizeof(*reply), &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
 
-	reply->type = sm->proto == WPA_PROTO_RSN ?
+	reply->type = (sm->proto == WPA_PROTO_RSN ||
+		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_SECURE;
 	key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
 	WPA_PUT_BE16(reply->key_info, key_info);
-	if (sm->proto == WPA_PROTO_RSN)
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
 		os_memcpy(reply->key_length, key->key_length, 2);
 	os_memcpy(reply->replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(reply->key_data_length, kde_len);
-	if (kde)
-		os_memcpy(reply + 1, kde, kde_len);
+	WPA_PUT_BE16(reply->key_data_length, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
 	wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
@@ -1032,10 +1069,10 @@
 
 static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 					  const struct wpa_eapol_key *key,
-					  u16 ver)
+					  u16 ver, const u8 *key_data,
+					  size_t key_data_len)
 {
-	u16 key_info, keylen, len;
-	const u8 *pos;
+	u16 key_info, keylen;
 	struct wpa_eapol_ie_parse ie;
 
 	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
@@ -1044,10 +1081,8 @@
 
 	key_info = WPA_GET_BE16(key->key_info);
 
-	pos = (const u8 *) (key + 1);
-	len = WPA_GET_BE16(key->key_data_length);
-	wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
-	if (wpa_supplicant_parse_ies(pos, len, &ie) < 0)
+	wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
 		goto failed;
 	if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1061,7 +1096,10 @@
 		goto failed;
 	}
 
-	if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
+	if (ie.igtk &&
+	    wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
+	    ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN +
+	    (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Invalid IGTK KDE length %lu",
 			(unsigned long) ie.igtk_len);
@@ -1089,8 +1127,16 @@
 		goto failed;
 	}
 
+#ifdef CONFIG_P2P
+	if (ie.ip_addr_alloc) {
+		os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4);
+		wpa_hexdump(MSG_DEBUG, "P2P: IP address info",
+			    sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr));
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
-				       NULL, 0, &sm->ptk)) {
+				       &sm->ptk)) {
 		goto failed;
 	}
 
@@ -1112,7 +1158,10 @@
 	}
 	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
 
-	if (ie.gtk &&
+	if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
+		wpa_supplicant_key_neg_complete(sm, sm->bssid,
+						key_info & WPA_KEY_INFO_SECURE);
+	} else if (ie.gtk &&
 	    wpa_supplicant_pairwise_gtk(sm, key,
 					ie.gtk, ie.gtk_len, key_info) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1126,7 +1175,8 @@
 		goto failed;
 	}
 
-	wpa_sm_set_rekey_offload(sm);
+	if (ie.gtk)
+		wpa_sm_set_rekey_offload(sm);
 
 	return;
 
@@ -1187,22 +1237,14 @@
 
 static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
 					     const struct wpa_eapol_key *key,
-					     size_t keydatalen, int key_info,
-					     size_t extra_len, u16 ver,
-					     struct wpa_gtk_data *gd)
+					     const u8 *key_data,
+					     size_t key_data_len, u16 key_info,
+					     u16 ver, struct wpa_gtk_data *gd)
 {
 	size_t maxkeylen;
-	u8 ek[32];
 
 	gd->gtk_len = WPA_GET_BE16(key->key_length);
-	maxkeylen = keydatalen;
-	if (keydatalen > extra_len) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: Truncated EAPOL-Key packet: "
-			"key_data_length=%lu > extra_len=%lu",
-			(unsigned long) keydatalen, (unsigned long) extra_len);
-		return -1;
-	}
+	maxkeylen = key_data_len;
 	if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		if (maxkeylen < 8) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1221,37 +1263,39 @@
 	gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
 		WPA_KEY_INFO_KEY_INDEX_SHIFT;
 	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
-		os_memcpy(ek, key->key_iv, 16);
-		os_memcpy(ek + 16, sm->ptk.kek, 16);
-		if (keydatalen > sizeof(gd->gtk)) {
+		u8 ek[32];
+		if (key_data_len > sizeof(gd->gtk)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: RC4 key data too long (%lu)",
-				(unsigned long) keydatalen);
+				(unsigned long) key_data_len);
 			return -1;
 		}
-		os_memcpy(gd->gtk, key + 1, keydatalen);
-		if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) {
+		os_memcpy(ek, key->key_iv, 16);
+		os_memcpy(ek + 16, sm->ptk.kek, 16);
+		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));
 			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
 				"WPA: RC4 failed");
 			return -1;
 		}
+		os_memset(ek, 0, sizeof(ek));
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
-		if (keydatalen % 8) {
+		if (maxkeylen % 8) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Unsupported AES-WRAP len %lu",
-				(unsigned long) keydatalen);
+				(unsigned long) maxkeylen);
 			return -1;
 		}
 		if (maxkeylen > sizeof(gd->gtk)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: AES-WRAP key data "
 				"too long (keydatalen=%lu maxkeylen=%lu)",
-				(unsigned long) keydatalen,
+				(unsigned long) key_data_len,
 				(unsigned long) maxkeylen);
 			return -1;
 		}
-		if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
-			       (const u8 *) (key + 1), gd->gtk)) {
+		if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, key_data, gd->gtk)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: AES unwrap failed - could not decrypt "
 				"GTK");
@@ -1281,12 +1325,13 @@
 	if (rbuf == NULL)
 		return -1;
 
-	reply->type = sm->proto == WPA_PROTO_RSN ?
+	reply->type = (sm->proto == WPA_PROTO_RSN ||
+		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
 	key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
 	WPA_PUT_BE16(reply->key_info, key_info);
-	if (sm->proto == WPA_PROTO_RSN)
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
 		os_memcpy(reply->key_length, key->key_length, 2);
@@ -1306,9 +1351,10 @@
 static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
 					  const unsigned char *src_addr,
 					  const struct wpa_eapol_key *key,
-					  int extra_len, u16 ver)
+					  const u8 *key_data,
+					  size_t key_data_len, u16 ver)
 {
-	u16 key_info, keydatalen;
+	u16 key_info;
 	int rekey, ret;
 	struct wpa_gtk_data gd;
 
@@ -1319,17 +1365,15 @@
 		"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
 
 	key_info = WPA_GET_BE16(key->key_info);
-	keydatalen = WPA_GET_BE16(key->key_data_length);
 
-	if (sm->proto == WPA_PROTO_RSN) {
-		ret = wpa_supplicant_process_1_of_2_rsn(sm,
-							(const u8 *) (key + 1),
-							keydatalen, key_info,
+	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+		ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
+							key_data_len, key_info,
 							&gd);
 	} else {
-		ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen,
-							key_info, extra_len,
-							ver, &gd);
+		ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
+							key_data_len,
+							key_info, ver, &gd);
 	}
 
 	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
@@ -1347,13 +1391,14 @@
 			MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
 		wpa_sm_cancel_auth_timeout(sm);
 		wpa_sm_set_state(sm, WPA_COMPLETED);
-
-		wpa_sm_set_rekey_offload(sm);
 	} else {
 		wpa_supplicant_key_neg_complete(sm, sm->bssid,
 						key_info &
 						WPA_KEY_INFO_SECURE);
 	}
+
+	wpa_sm_set_rekey_offload(sm);
+
 	return;
 
 failed:
@@ -1374,7 +1419,7 @@
 		os_memset(key->key_mic, 0, 16);
 		wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
 				  key->key_mic);
-		if (os_memcmp(mic, key->key_mic, 16) != 0) {
+		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC "
 				"when using TPTK - ignoring TPTK");
@@ -1383,6 +1428,7 @@
 			sm->tptk_set = 0;
 			sm->ptk_set = 1;
 			os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+			os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 		}
 	}
 
@@ -1390,7 +1436,7 @@
 		os_memset(key->key_mic, 0, 16);
 		wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
 				  key->key_mic);
-		if (os_memcmp(mic, key->key_mic, 16) != 0) {
+		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC - "
 				"dropping packet");
@@ -1415,12 +1461,11 @@
 
 /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
 static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
-					   struct wpa_eapol_key *key, u16 ver)
+					   struct wpa_eapol_key *key, u16 ver,
+					   u8 *key_data, size_t *key_data_len)
 {
-	u16 keydatalen = WPA_GET_BE16(key->key_data_length);
-
 	wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
-		    (u8 *) (key + 1), keydatalen);
+		    key_data, *key_data_len);
 	if (!sm->ptk_set) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: PTK not available, cannot decrypt EAPOL-Key Key "
@@ -1434,45 +1479,48 @@
 		u8 ek[32];
 		os_memcpy(ek, key->key_iv, 16);
 		os_memcpy(ek + 16, sm->ptk.kek, 16);
-		if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) {
+		if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
+			os_memset(ek, 0, sizeof(ek));
 			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
 				"WPA: RC4 failed");
 			return -1;
 		}
+		os_memset(ek, 0, sizeof(ek));
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
-		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+		   sm->key_mgmt == WPA_KEY_MGMT_OSEN) {
 		u8 *buf;
-		if (keydatalen % 8) {
+		if (*key_data_len < 8 || *key_data_len % 8) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Unsupported AES-WRAP len %d",
-				keydatalen);
+				"WPA: Unsupported AES-WRAP len %u",
+				(unsigned int) *key_data_len);
 			return -1;
 		}
-		keydatalen -= 8; /* AES-WRAP adds 8 bytes */
-		buf = os_malloc(keydatalen);
+		*key_data_len -= 8; /* AES-WRAP adds 8 bytes */
+		buf = os_malloc(*key_data_len);
 		if (buf == NULL) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: No memory for AES-UNWRAP buffer");
 			return -1;
 		}
-		if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
-			       (u8 *) (key + 1), buf)) {
+		if (aes_unwrap(sm->ptk.kek, *key_data_len / 8,
+			       key_data, buf)) {
 			os_free(buf);
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: AES unwrap failed - "
 				"could not decrypt EAPOL-Key key data");
 			return -1;
 		}
-		os_memcpy(key + 1, buf, keydatalen);
+		os_memcpy(key_data, buf, *key_data_len);
 		os_free(buf);
-		WPA_PUT_BE16(key->key_data_length, keydatalen);
+		WPA_PUT_BE16(key->key_data_length, *key_data_len);
 	} else {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported key_info type %d", ver);
 		return -1;
 	}
 	wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
-			(u8 *) (key + 1), keydatalen);
+			key_data, *key_data_len);
 	return 0;
 }
 
@@ -1546,13 +1594,14 @@
 int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 		    const u8 *buf, size_t len)
 {
-	size_t plen, data_len, extra_len;
-	struct ieee802_1x_hdr *hdr;
+	size_t plen, data_len, key_data_len;
+	const struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
 	u16 key_info, ver;
-	u8 *tmp;
+	u8 *tmp = NULL;
 	int ret = -1;
 	struct wpa_peerkey *peerkey = NULL;
+	u8 *key_data;
 
 #ifdef CONFIG_IEEE80211R
 	sm->ft_completed = 0;
@@ -1567,13 +1616,7 @@
 		return 0;
 	}
 
-	tmp = os_malloc(len);
-	if (tmp == NULL)
-		return -1;
-	os_memcpy(tmp, buf, len);
-
-	hdr = (struct ieee802_1x_hdr *) tmp;
-	key = (struct wpa_eapol_key *) (hdr + 1);
+	hdr = (const struct ieee802_1x_hdr *) buf;
 	plen = be_to_host16(hdr->length);
 	data_len = plen + sizeof(*hdr);
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -1590,6 +1633,7 @@
 		ret = 0;
 		goto out;
 	}
+	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
 	if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"WPA: EAPOL frame payload size %lu "
@@ -1598,6 +1642,22 @@
 		ret = 0;
 		goto out;
 	}
+	if (data_len < len) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: ignoring %lu bytes after the IEEE 802.1X data",
+			(unsigned long) len - data_len);
+	}
+
+	/*
+	 * Make a copy of the frame since we need to modify the buffer during
+	 * MAC validation and Key Data decryption.
+	 */
+	tmp = os_malloc(data_len);
+	if (tmp == NULL)
+		goto out;
+	os_memcpy(tmp, buf, data_len);
+	key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
+	key_data = (u8 *) (key + 1);
 
 	if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
 	{
@@ -1609,26 +1669,38 @@
 	}
 	wpa_eapol_key_dump(sm, key);
 
-	eapol_sm_notify_lower_layer_success(sm->eapol, 0);
-	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len);
-	if (data_len < len) {
-		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-			"WPA: ignoring %lu bytes after the IEEE 802.1X data",
-			(unsigned long) len - data_len);
+	key_data_len = WPA_GET_BE16(key->key_data_length);
+	if (key_data_len > plen - sizeof(struct wpa_eapol_key)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
+			"frame - key_data overflow (%u > %u)",
+			(unsigned int) key_data_len,
+			(unsigned int) (plen - sizeof(struct wpa_eapol_key)));
+		goto out;
 	}
+
+	eapol_sm_notify_lower_layer_success(sm->eapol, 0);
 	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) {
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+	    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Unsupported EAPOL-Key descriptor version %d",
 			ver);
 		goto out;
 	}
 
+	if (sm->key_mgmt == WPA_KEY_MGMT_OSEN &&
+	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"OSEN: Unsupported EAPOL-Key descriptor version %d",
+			ver);
+		goto out;
+	}
+
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
 		/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
@@ -1641,7 +1713,8 @@
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
-		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+		    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"WPA: AP did not use the "
 				"negotiated AES-128-CMAC");
@@ -1663,11 +1736,13 @@
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"WPA: Backwards compatibility: allow invalid "
 				"version for non-CCMP group keys");
+		} else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
 		} else
 			goto out;
-	}
-	if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
-	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+	} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+		   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: GCMP is used, but EAPOL-Key "
 			"descriptor version (%d) is not 2", ver);
@@ -1746,22 +1821,11 @@
 		goto out;
 #endif /* CONFIG_PEERKEY */
 
-	extra_len = data_len - sizeof(*hdr) - sizeof(*key);
-
-	if (WPA_GET_BE16(key->key_data_length) > extra_len) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
-			"frame - key_data overflow (%d > %lu)",
-			WPA_GET_BE16(key->key_data_length),
-			(unsigned long) extra_len);
-		goto out;
-	}
-	extra_len = WPA_GET_BE16(key->key_data_length);
-
-	if (sm->proto == WPA_PROTO_RSN &&
+	if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
 	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-		if (wpa_supplicant_decrypt_key_data(sm, key, ver))
+		if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+						    &key_data_len))
 			goto out;
-		extra_len = WPA_GET_BE16(key->key_data_length);
 	}
 
 	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
@@ -1773,24 +1837,28 @@
 		}
 		if (peerkey) {
 			/* PeerKey 4-Way Handshake */
-			peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver);
+			peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
+					      key_data, key_data_len);
 		} else if (key_info & WPA_KEY_INFO_MIC) {
 			/* 3/4 4-Way Handshake */
-			wpa_supplicant_process_3_of_4(sm, key, ver);
+			wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
+						      key_data_len);
 		} else {
 			/* 1/4 4-Way Handshake */
 			wpa_supplicant_process_1_of_4(sm, src_addr, key,
-						      ver);
+						      ver, key_data,
+						      key_data_len);
 		}
 	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
 		/* PeerKey SMK Handshake */
-		peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info,
+		peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
 				     ver);
 	} else {
 		if (key_info & WPA_KEY_INFO_MIC) {
 			/* 1/2 Group Key Handshake */
 			wpa_supplicant_process_1_of_2(sm, src_addr, key,
-						      extra_len, ver);
+						      key_data, key_data_len,
+						      ver);
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: EAPOL-Key (Group) without Mic bit - "
@@ -1811,7 +1879,8 @@
 {
 	switch (sm->key_mgmt) {
 	case WPA_KEY_MGMT_IEEE8021X:
-		return (sm->proto == WPA_PROTO_RSN ?
+		return ((sm->proto == WPA_PROTO_RSN ||
+			 sm->proto == WPA_PROTO_OSEN) ?
 			RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
 			WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
 	case WPA_KEY_MGMT_PSK:
@@ -2074,12 +2143,18 @@
 		 */
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
 		sm->ptk_set = 0;
+		os_memset(&sm->ptk, 0, sizeof(sm->ptk));
 		sm->tptk_set = 0;
+		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 	}
 
 #ifdef CONFIG_TDLS
 	wpa_tdls_assoc(sm);
 #endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_P2P
+	os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr));
+#endif /* CONFIG_P2P */
 }
 
 
@@ -2092,6 +2167,7 @@
  */
 void wpa_sm_notify_disassoc(struct wpa_sm *sm)
 {
+	peerkey_deinit(sm);
 	rsn_preauth_deinit(sm);
 	pmksa_cache_clear_current(sm);
 	if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
@@ -2202,6 +2278,7 @@
 		} else
 			sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
+		sm->p2p = config->p2p;
 	} else {
 		sm->network_ctx = NULL;
 		sm->peerkey_enabled = 0;
@@ -2211,6 +2288,7 @@
 		sm->eap_conf_ctx = NULL;
 		sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = 0;
+		sm->p2p = 0;
 	}
 }
 
@@ -2321,44 +2399,6 @@
 
 
 /**
- * wpa_sm_get_param - Get WPA state machine parameters
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @param: Parameter field
- * Returns: Parameter value
- */
-unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param)
-{
-	if (sm == NULL)
-		return 0;
-
-	switch (param) {
-	case RSNA_PMK_LIFETIME:
-		return sm->dot11RSNAConfigPMKLifetime;
-	case RSNA_PMK_REAUTH_THRESHOLD:
-		return sm->dot11RSNAConfigPMKReauthThreshold;
-	case RSNA_SA_TIMEOUT:
-		return sm->dot11RSNAConfigSATimeout;
-	case WPA_PARAM_PROTO:
-		return sm->proto;
-	case WPA_PARAM_PAIRWISE:
-		return sm->pairwise_cipher;
-	case WPA_PARAM_GROUP:
-		return sm->group_cipher;
-	case WPA_PARAM_KEY_MGMT:
-		return sm->key_mgmt;
-#ifdef CONFIG_IEEE80211W
-	case WPA_PARAM_MGMT_GROUP:
-		return sm->mgmt_group_cipher;
-#endif /* CONFIG_IEEE80211W */
-	case WPA_PARAM_RSN_ENABLED:
-		return sm->rsn_enabled;
-	default:
-		return 0;
-	}
-}
-
-
-/**
  * wpa_sm_get_status - Get WPA state machine
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @buf: Buffer for status information
@@ -2599,6 +2639,7 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
@@ -2608,6 +2649,7 @@
 	os_memset(&sm->ptk, 0, sizeof(sm->ptk));
 	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 }
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
 int wpa_sm_has_ptk(struct wpa_sm *sm)
@@ -2633,29 +2675,22 @@
 #ifdef CONFIG_WNM
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 {
-	struct wpa_gtk_data gd;
-#ifdef CONFIG_IEEE80211W
-	struct wpa_igtk_kde igd;
-	u16 keyidx;
-#endif /* CONFIG_IEEE80211W */
 	u16 keyinfo;
 	u8 keylen;  /* plaintext key len */
 	u8 *key_rsc;
 
-	os_memset(&gd, 0, sizeof(gd));
-#ifdef CONFIG_IEEE80211W
-	os_memset(&igd, 0, sizeof(igd));
-#endif /* CONFIG_IEEE80211W */
-
-	keylen = wpa_cipher_key_len(sm->group_cipher);
-	gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
-	gd.alg = wpa_cipher_to_alg(sm->group_cipher);
-	if (gd.alg == WPA_ALG_NONE) {
-		wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
-		return -1;
-	}
-
 	if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
+		struct wpa_gtk_data gd;
+
+		os_memset(&gd, 0, sizeof(gd));
+		keylen = wpa_cipher_key_len(sm->group_cipher);
+		gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+		gd.alg = wpa_cipher_to_alg(sm->group_cipher);
+		if (gd.alg == WPA_ALG_NONE) {
+			wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
+			return -1;
+		}
+
 		key_rsc = buf + 5;
 		keyinfo = WPA_GET_LE16(buf + 2);
 		gd.gtk_len = keylen;
@@ -2673,27 +2708,37 @@
 		wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
 				gd.gtk, gd.gtk_len);
 		if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+			os_memset(&gd, 0, 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
 	} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
+		struct wpa_igtk_kde igd;
+		u16 keyidx;
+
+		os_memset(&igd, 0, sizeof(igd));
+		keylen = wpa_cipher_key_len(sm->mgmt_group_cipher);
 		os_memcpy(igd.keyid, buf + 2, 2);
 		os_memcpy(igd.pn, buf + 4, 6);
 
 		keyidx = WPA_GET_LE16(igd.keyid);
-		os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN);
+		os_memcpy(igd.igtk, buf + 10, keylen);
 
 		wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
-				igd.igtk, WPA_IGTK_LEN);
-		if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
+				igd.igtk, keylen);
+		if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+				   broadcast_ether_addr,
 				   keyidx, 0, igd.pn, sizeof(igd.pn),
-				   igd.igtk, WPA_IGTK_LEN) < 0) {
+				   igd.igtk, keylen) < 0) {
 			wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
 				   "WNM mode");
+			os_memset(&igd, 0, sizeof(igd));
 			return -1;
 		}
+		os_memset(&igd, 0, sizeof(igd));
 #endif /* CONFIG_IEEE80211W */
 	} else {
 		wpa_printf(MSG_DEBUG, "Unknown element id");
@@ -2703,3 +2748,37 @@
 	return 0;
 }
 #endif /* CONFIG_WNM */
+
+
+#ifdef CONFIG_PEERKEY
+int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+			    const u8 *buf, size_t len)
+{
+	struct wpa_peerkey *peerkey;
+
+	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+		if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (!peerkey)
+		return 0;
+
+	wpa_sm_rx_eapol(sm, src_addr, buf, len);
+
+	return 1;
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_P2P
+
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
+{
+	if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0)
+		return -1;
+	os_memcpy(buf, sm->p2p_ip_addr, 3 * 4);
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 26e9c6c..07a7bf9 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -54,7 +54,8 @@
 			     int *tdls_ext_setup);
 	int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
 			      u8 action_code, u8 dialog_token,
-			      u16 status_code, const u8 *buf, size_t len);
+			      u16 status_code, u32 peer_capab,
+			      const u8 *buf, size_t len);
 	int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
 	int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid,
 				u16 capability, const u8 *supp_rates,
@@ -62,7 +63,10 @@
 				const struct ieee80211_ht_capabilities *ht_capab,
 				const struct ieee80211_vht_capabilities *vht_capab,
 				u8 qosinfo, const u8 *ext_capab,
-				size_t ext_capab_len);
+				size_t ext_capab_len, const u8 *supp_channels,
+				size_t supp_channels_len,
+				const u8 *supp_oper_classes,
+				size_t supp_oper_classes_len);
 #endif /* CONFIG_TDLS */
 	void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
 				  const u8 *replay_ctr);
@@ -92,6 +96,7 @@
 	const u8 *ssid;
 	size_t ssid_len;
 	int wpa_ptk_rekey;
+	int p2p;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -118,8 +123,6 @@
 
 int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 		     unsigned int value);
-unsigned int wpa_sm_get_param(struct wpa_sm *sm,
-			      enum wpa_sm_conf_params param);
 
 int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
 		      int verbose);
@@ -142,6 +145,8 @@
 
 void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
 
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
@@ -233,12 +238,6 @@
 	return -1;
 }
 
-static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm,
-					    enum wpa_sm_conf_params param)
-{
-	return 0;
-}
-
 static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
 				    size_t buflen, int verbose)
 {
@@ -306,11 +305,19 @@
 
 #ifdef CONFIG_PEERKEY
 int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
+int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+			    const u8 *buf, size_t len);
 #else /* CONFIG_PEERKEY */
 static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
 {
 	return -1;
 }
+
+static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+					  const u8 *buf, size_t len)
+{
+	return 0;
+}
 #endif /* CONFIG_PEERKEY */
 
 #ifdef CONFIG_IEEE80211R
@@ -372,14 +379,14 @@
 void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
 int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr);
 void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr);
-int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
 int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
 int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_init(struct wpa_sm *sm);
 void wpa_tdls_teardown_peers(struct wpa_sm *sm);
 void wpa_tdls_deinit(struct wpa_sm *sm);
 void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
-void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr);
+const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 3a40c96..4a75b92 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -207,6 +207,8 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
 	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
 	else {
 		wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
 			   sm->key_mgmt);
@@ -400,8 +402,7 @@
 		}
 	}
 
-	if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-	    sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+	if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
 		wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
 			   "enabled for this connection");
 		return -1;
@@ -441,7 +442,8 @@
 	}
 
 	if (parse.r0kh_id_len != sm->r0kh_id_len ||
-	    os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+	    os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+	{
 		wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
 			   "the current R0KH-ID");
 		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
@@ -457,7 +459,8 @@
 	}
 
 	if (parse.rsn_pmkid == NULL ||
-	    os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) {
+	    os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN))
+	{
 		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
 			   "RSNIE");
 		return -1;
@@ -526,8 +529,7 @@
 	if (sm == NULL)
 		return 0;
 
-	if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-	    sm->key_mgmt != WPA_KEY_MGMT_FT_PSK)
+	if (!wpa_key_mgmt_ft(sm->key_mgmt))
 		return 0;
 
 	return sm->ft_completed;
@@ -678,8 +680,7 @@
 
 	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
 
-	if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-	    sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+	if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
 		wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
 			   "enabled for this connection");
 		return -1;
@@ -728,7 +729,8 @@
 	}
 
 	if (parse.r0kh_id_len != sm->r0kh_id_len ||
-	    os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+	    os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+	{
 		wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
 			   "the current R0KH-ID");
 		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
@@ -743,14 +745,15 @@
 		return -1;
 	}
 
-	if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
+	if (os_memcmp_const(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
 			   "ReassocResp");
 		return -1;
 	}
 
 	if (parse.rsn_pmkid == NULL ||
-	    os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+	    os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+	{
 		wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
 			   "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
 		return -1;
@@ -776,7 +779,7 @@
 		return -1;
 	}
 
-	if (os_memcmp(mic, ftie->mic, 16) != 0) {
+	if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
 		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
 		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 0e0d373..e20e9da 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -58,6 +58,7 @@
 	u8 ssid[32];
 	size_t ssid_len;
 	int wpa_ptk_rekey;
+	int p2p;
 
 	u8 own_addr[ETH_ALEN];
 	const char *ifname;
@@ -122,6 +123,10 @@
 	u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
 	size_t assoc_resp_ies_len;
 #endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_P2P
+	u8 p2p_ip_addr[3 * 4];
+#endif /* CONFIG_P2P */
 };
 
 
@@ -262,13 +267,13 @@
 
 static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
 					u8 action_code, u8 dialog_token,
-					u16 status_code, const u8 *buf,
-					size_t len)
+					u16 status_code, u32 peer_capab,
+					const u8 *buf, size_t len)
 {
 	if (sm->ctx->send_tdls_mgmt)
 		return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
 					       dialog_token, status_code,
-					       buf, len);
+					       peer_capab, buf, len);
 	return -1;
 }
 
@@ -286,14 +291,21 @@
 			size_t supp_rates_len,
 			const struct ieee80211_ht_capabilities *ht_capab,
 			const struct ieee80211_vht_capabilities *vht_capab,
-			u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len)
+			u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len,
+			const u8 *supp_channels, size_t supp_channels_len,
+			const u8 *supp_oper_classes,
+			size_t supp_oper_classes_len)
 {
 	if (sm->ctx->tdls_peer_addset)
 		return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
 						 aid, capability, supp_rates,
 						 supp_rates_len, ht_capab,
 						 vht_capab, qosinfo,
-						 ext_capab, ext_capab_len);
+						 ext_capab, ext_capab_len,
+						 supp_channels,
+						 supp_channels_len,
+						 supp_oper_classes,
+						 supp_oper_classes_len);
 	return -1;
 }
 #endif /* CONFIG_TDLS */
@@ -309,7 +321,6 @@
 int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
 			       const struct wpa_eapol_key *key,
 			       u16 ver, u16 key_info,
-			       const u8 *kde, size_t kde_len,
 			       struct wpa_ptk *ptk);
 
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 50b9272..2329033 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -201,7 +201,7 @@
 	}
 
 #ifdef CONFIG_IEEE80211W
-	if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+	if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
 		if (!sm->cur_pmksa) {
 			/* PMKID Count */
 			WPA_PUT_LE16(pos, 0);
@@ -209,7 +209,8 @@
 		}
 
 		/* Management Group Cipher Suite */
-		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+		RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+							  mgmt_group_cipher));
 		pos += RSN_SELECTOR_LEN;
 	}
 #endif /* CONFIG_IEEE80211W */
@@ -222,6 +223,64 @@
 }
 
 
+#ifdef CONFIG_HS20
+static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
+			       int pairwise_cipher, int group_cipher,
+			       int key_mgmt)
+{
+	u8 *pos, *len;
+	u32 suite;
+
+	if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
+	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
+		return -1;
+
+	pos = wpa_ie;
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	len = pos++; /* to be filled */
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = HS20_OSEN_OUI_TYPE;
+
+	/* Group Data Cipher Suite */
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
+	if (suite == 0) {
+		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+			   group_cipher);
+		return -1;
+	}
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += RSN_SELECTOR_LEN;
+
+	/* Pairwise Cipher Suite Count and List */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
+	if (suite == 0 ||
+	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+	     pairwise_cipher != WPA_CIPHER_NONE)) {
+		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+			   pairwise_cipher);
+		return -1;
+	}
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += RSN_SELECTOR_LEN;
+
+	/* AKM Suite Count and List */
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+	pos += RSN_SELECTOR_LEN;
+
+	*len = pos - len - 1;
+
+	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+	return pos - wpa_ie;
+}
+#endif /* CONFIG_HS20 */
+
+
 /**
  * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -237,6 +296,13 @@
 					  sm->group_cipher,
 					  sm->key_mgmt, sm->mgmt_group_cipher,
 					  sm);
+#ifdef CONFIG_HS20
+	else if (sm->proto == WPA_PROTO_OSEN)
+		return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
+					   sm->pairwise_cipher,
+					   sm->group_cipher,
+					   sm->key_mgmt);
+#endif /* CONFIG_HS20 */
 	else
 		return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
 					  sm->pairwise_cipher,
@@ -246,6 +312,42 @@
 
 
 /**
+ * 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
@@ -345,6 +447,25 @@
 	}
 #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 */
+
 	return 0;
 }
 
@@ -434,6 +555,20 @@
 			ie->vht_capabilities_len = pos[1];
 		} 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)
@@ -442,6 +577,14 @@
 				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]);
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 2c78801..0fc42cc 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -53,8 +53,18 @@
 	size_t ht_capabilities_len;
 	const u8 *vht_capabilities;
 	size_t vht_capabilities_len;
+	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 */
 };
 
 int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
diff --git a/src/tls/asn1.c b/src/tls/asn1.c
index 53acd53..97462fa 100644
--- a/src/tls/asn1.c
+++ b/src/tls/asn1.c
@@ -1,6 +1,6 @@
 /*
  * ASN.1 DER parsing
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,17 @@
 #include "common.h"
 #include "asn1.h"
 
+struct asn1_oid asn1_sha1_oid = {
+	.oid = { 1, 3, 14, 3, 2, 26 },
+	.len = 6
+};
+
+struct asn1_oid asn1_sha256_oid = {
+	.oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 },
+	.len = 9
+};
+
+
 int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
 {
 	const u8 *pos, *end;
@@ -140,7 +151,7 @@
 }
 
 
-void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len)
 {
 	char *pos = buf;
 	size_t i;
@@ -204,3 +215,19 @@
 
 	return val;
 }
+
+
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b)
+{
+	size_t i;
+
+	if (a->len != b->len)
+		return 0;
+
+	for (i = 0; i < a->len; i++) {
+		if (a->oid[i] != b->oid[i])
+			return 0;
+	}
+
+	return 1;
+}
diff --git a/src/tls/asn1.h b/src/tls/asn1.h
index 6342c4c..7475007 100644
--- a/src/tls/asn1.h
+++ b/src/tls/asn1.h
@@ -60,7 +60,11 @@
 int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid);
 int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
 		 const u8 **next);
-void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len);
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len);
 unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len);
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b);
+
+extern struct asn1_oid asn1_sha1_oid;
+extern struct asn1_oid asn1_sha256_oid;
 
 #endif /* ASN1_H */
diff --git a/src/tls/pkcs1.c b/src/tls/pkcs1.c
index b6fde5e..141ac50 100644
--- a/src/tls/pkcs1.c
+++ b/src/tls/pkcs1.c
@@ -1,6 +1,6 @@
 /*
  * PKCS #1 (RSA Encryption)
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,7 +9,9 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "rsa.h"
+#include "asn1.h"
 #include "pkcs1.h"
 
 
@@ -113,6 +115,11 @@
 		pos++;
 	if (pos == end)
 		return -1;
+	if (pos - out - 2 < 8) {
+		/* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+		wpa_printf(MSG_INFO, "LibTomCrypt: Too short padding");
+		return -1;
+	}
 	pos++;
 
 	*outlen -= pos - out;
@@ -142,35 +149,26 @@
 	 * BT = 00 or 01
 	 * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
 	 * k = length of modulus in octets
+	 *
+	 * Based on 10.1.3, "The block type shall be 01" for a signature.
 	 */
 
 	if (len < 3 + 8 + 16 /* min hash len */ ||
-	    plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
+	    plain[0] != 0x00 || plain[1] != 0x01) {
 		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
 			   "structure");
 		return -1;
 	}
 
 	pos = plain + 3;
-	if (plain[1] == 0x00) {
-		/* BT = 00 */
-		if (plain[2] != 0x00) {
-			wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
-				   "PS (BT=00)");
-			return -1;
-		}
-		while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
-			pos++;
-	} else {
-		/* BT = 01 */
-		if (plain[2] != 0xff) {
-			wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
-				   "PS (BT=01)");
-			return -1;
-		}
-		while (pos < plain + len && *pos == 0xff)
-			pos++;
+	/* BT = 01 */
+	if (plain[2] != 0xff) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+			   "PS (BT=01)");
+		return -1;
 	}
+	while (pos < plain + len && *pos == 0xff)
+		pos++;
 
 	if (pos - plain - 2 < 8) {
 		/* PKCS #1 v1.5, 8.1: At least eight octets long PS */
@@ -193,3 +191,130 @@
 
 	return 0;
 }
+
+
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+		      const u8 *s, size_t s_len,
+		      const struct asn1_oid *hash_alg,
+		      const u8 *hash, size_t hash_len)
+{
+	int res;
+	u8 *decrypted;
+	size_t decrypted_len;
+	const u8 *pos, *end, *next, *da_end;
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+
+	decrypted = os_malloc(s_len);
+	if (decrypted == NULL)
+		return -1;
+	decrypted_len = s_len;
+	res = crypto_public_key_decrypt_pkcs1(pk, s, s_len, decrypted,
+					      &decrypted_len);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "PKCS #1: RSA decrypt failed");
+		os_free(decrypted);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "Decrypted(S)", decrypted, decrypted_len);
+
+	/*
+	 * PKCS #1 v1.5, 10.1.2:
+	 *
+	 * DigestInfo ::= SEQUENCE {
+	 *     digestAlgorithm DigestAlgorithmIdentifier,
+	 *     digest Digest
+	 * }
+	 *
+	 * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+	 *
+	 * Digest ::= OCTET STRING
+	 *
+	 */
+	if (asn1_get_next(decrypted, decrypted_len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Expected SEQUENCE (DigestInfo) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		os_free(decrypted);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/*
+	 * X.509:
+	 * AlgorithmIdentifier ::= SEQUENCE {
+	 *     algorithm            OBJECT IDENTIFIER,
+	 *     parameters           ANY DEFINED BY algorithm OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Expected SEQUENCE (AlgorithmIdentifier) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		os_free(decrypted);
+		return -1;
+	}
+	da_end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Failed to parse digestAlgorithm");
+		os_free(decrypted);
+		return -1;
+	}
+
+	if (!asn1_oid_equal(&oid, hash_alg)) {
+		char txt[100], txt2[100];
+		asn1_oid_to_str(&oid, txt, sizeof(txt));
+		asn1_oid_to_str(hash_alg, txt2, sizeof(txt2));
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Hash alg OID mismatch: was %s, expected %s",
+			   txt, txt2);
+		os_free(decrypted);
+		return -1;
+	}
+
+	/* Digest ::= OCTET STRING */
+	pos = da_end;
+	end = decrypted + decrypted_len;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Expected OCTETSTRING (Digest) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		os_free(decrypted);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Decrypted Digest",
+		    hdr.payload, hdr.length);
+
+	if (hdr.length != hash_len ||
+	    os_memcmp_const(hdr.payload, hash, hdr.length) != 0) {
+		wpa_printf(MSG_INFO, "PKCS #1: Digest value does not match calculated hash");
+		os_free(decrypted);
+		return -1;
+	}
+
+	os_free(decrypted);
+
+	if (hdr.payload + hdr.length != end) {
+		wpa_printf(MSG_INFO,
+			   "PKCS #1: Extra data after signature - reject");
+
+		wpa_hexdump(MSG_DEBUG, "PKCS #1: Extra data",
+			    hdr.payload + hdr.length,
+			    end - hdr.payload - hdr.length);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/tls/pkcs1.h b/src/tls/pkcs1.h
index ed64def..f37ebf3 100644
--- a/src/tls/pkcs1.h
+++ b/src/tls/pkcs1.h
@@ -9,6 +9,9 @@
 #ifndef PKCS1_H
 #define PKCS1_H
 
+struct crypto_public_key;
+struct asn1_oid;
+
 int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
 		  int use_private, const u8 *in, size_t inlen,
 		  u8 *out, size_t *outlen);
@@ -18,5 +21,9 @@
 int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
 			     const u8 *crypt, size_t crypt_len,
 			     u8 *plain, size_t *plain_len);
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+		      const u8 *s, size_t s_len,
+		      const struct asn1_oid *hash_alg,
+		      const u8 *hash, size_t hash_len);
 
 #endif /* PKCS1_H */
diff --git a/src/tls/rsa.c b/src/tls/rsa.c
index 125c420..0b7b530 100644
--- a/src/tls/rsa.c
+++ b/src/tls/rsa.c
@@ -1,6 +1,6 @@
 /*
  * RSA
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -116,6 +116,29 @@
 }
 
 
+struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+				   const u8 *e, size_t e_len)
+{
+	struct crypto_rsa_key *key;
+
+	key = os_zalloc(sizeof(*key));
+	if (key == NULL)
+		return NULL;
+
+	key->n = bignum_init();
+	key->e = bignum_init();
+	if (key->n == NULL || key->e == NULL ||
+	    bignum_set_unsigned_bin(key->n, n, n_len) < 0 ||
+	    bignum_set_unsigned_bin(key->e, e, e_len) < 0) {
+		crypto_rsa_free(key);
+		return NULL;
+	}
+
+	return key;
+}
+
+
 /**
  * crypto_rsa_import_private_key - Import an RSA private key
  * @buf: Key buffer (DER encoded RSA private key)
diff --git a/src/tls/rsa.h b/src/tls/rsa.h
index c236a9d..b65818e 100644
--- a/src/tls/rsa.h
+++ b/src/tls/rsa.h
@@ -14,6 +14,9 @@
 struct crypto_rsa_key *
 crypto_rsa_import_public_key(const u8 *buf, size_t len);
 struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+				   const u8 *e, size_t e_len);
+struct crypto_rsa_key *
 crypto_rsa_import_private_key(const u8 *buf, size_t len);
 size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key);
 int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 12148b6..4a4f0b6 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -459,10 +459,15 @@
 
 	count = 0;
 	suites = conn->cipher_suites;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
 	suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 3269ecf..4f08e0f 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -409,10 +409,37 @@
 }
 
 
-static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
-					const u8 *buf, size_t len)
+static unsigned int count_bits(const u8 *val, size_t len)
 {
-	const u8 *pos, *end;
+	size_t i;
+	unsigned int bits;
+	u8 tmp;
+
+	for (i = 0; i < len; i++) {
+		if (val[i])
+			break;
+	}
+	if (i == len)
+		return 0;
+
+	bits = (len - i - 1) * 8;
+	tmp = val[i];
+	while (tmp) {
+		bits++;
+		tmp >>= 1;
+	}
+
+	return bits;
+}
+
+
+static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
+					const u8 *buf, size_t len,
+					tls_key_exchange key_exchange)
+{
+	const u8 *pos, *end, *server_params, *server_params_end;
+	u8 alert;
+	unsigned int bits;
 
 	tlsv1_client_free_dh(conn);
 
@@ -421,6 +448,7 @@
 
 	if (end - pos < 3)
 		goto fail;
+	server_params = pos;
 	conn->dh_p_len = WPA_GET_BE16(pos);
 	pos += 2;
 	if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
@@ -428,6 +456,14 @@
 			   (unsigned long) conn->dh_p_len);
 		goto fail;
 	}
+	bits = count_bits(pos, conn->dh_p_len);
+	if (bits < 768) {
+		wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)",
+			   bits);
+		wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime",
+			    pos, conn->dh_p_len);
+		goto fail;
+	}
 	conn->dh_p = os_malloc(conn->dh_p_len);
 	if (conn->dh_p == NULL)
 		goto fail;
@@ -465,6 +501,59 @@
 	pos += conn->dh_ys_len;
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
 		    conn->dh_ys, conn->dh_ys_len);
+	server_params_end = pos;
+
+	if (key_exchange == TLS_KEY_X_DHE_RSA) {
+		u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+		int hlen;
+
+		if (conn->rl.tls_version == TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+			/*
+			 * RFC 5246, 4.7:
+			 * TLS v1.2 adds explicit indication of the used
+			 * signature and hash algorithms.
+			 *
+			 * struct {
+			 *   HashAlgorithm hash;
+			 *   SignatureAlgorithm signature;
+			 * } SignatureAndHashAlgorithm;
+			 */
+			if (end - pos < 2)
+				goto fail;
+			if (pos[0] != TLS_HASH_ALG_SHA256 ||
+			    pos[1] != TLS_SIGN_ALG_RSA) {
+				wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm",
+					   pos[0], pos[1]);
+				goto fail;
+			}
+			pos += 2;
+
+			hlen = tlsv12_key_x_server_params_hash(
+				conn->rl.tls_version, conn->client_random,
+				conn->server_random, server_params,
+				server_params_end - server_params, hash);
+#else /* CONFIG_TLSV12 */
+			goto fail;
+#endif /* CONFIG_TLSV12 */
+		} else {
+			hlen = tls_key_x_server_params_hash(
+				conn->rl.tls_version, conn->client_random,
+				conn->server_random, server_params,
+				server_params_end - server_params, hash);
+		}
+
+		if (hlen < 0)
+			goto fail;
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash",
+			    hash, hlen);
+
+		if (tls_verify_signature(conn->rl.tls_version,
+					 conn->server_rsa_key,
+					 hash, hlen, pos, end - pos,
+					 &alert) < 0)
+			goto fail;
+	}
 
 	return 0;
 
@@ -543,8 +632,10 @@
 
 	wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
 	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
-	if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
-		if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+	if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon ||
+		      suite->key_exchange == TLS_KEY_X_DHE_RSA)) {
+		if (tlsv1_process_diffie_hellman(conn, pos, len,
+						 suite->key_exchange) < 0) {
 			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				  TLS_ALERT_DECODE_ERROR);
 			return -1;
@@ -871,8 +962,10 @@
 	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
 			verify_data, TLS_VERIFY_DATA_LEN);
 
-	if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+	if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
 		wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECRYPT_ERROR);
 		return -1;
 	}
 
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index d789efb..839eb90 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -205,7 +205,7 @@
 }
 
 
-static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
 {
 	/* ClientDiffieHellmanPublic */
 	u8 *csecret, *csecret_start, *dh_yc, *shared;
@@ -399,8 +399,8 @@
 	hs_length = pos;
 	pos += 3;
 	/* body - ClientKeyExchange */
-	if (keyx == TLS_KEY_X_DH_anon) {
-		if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
+	if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) {
+		if (tlsv1_key_x_dh(conn, &pos, end) < 0)
 			return -1;
 	} else {
 		if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index d212862..ced28cf 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 common routines
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/md5.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
 #include "x509v3.h"
@@ -33,6 +34,10 @@
 	  TLS_HASH_SHA },
 	{ TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
 	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC,
+	  TLS_HASH_SHA},
+	{ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
  	{ TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
 	  TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
  	{ TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
@@ -41,24 +46,31 @@
 	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
 	{ TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
 	  TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
 	{ TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
 	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
 	{ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
 	  TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
 	{ TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
 	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
 	{ TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
 	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
 	{ TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
 	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+	{ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+	{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
 	{ TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
 	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
 	{ TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
 	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
 };
 
-#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
-#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
+#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites)
 
 
 static const struct tls_cipher_data tls_ciphers[] = {
@@ -84,7 +96,7 @@
 	  CRYPTO_CIPHER_ALG_AES }
 };
 
-#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
+#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers)
 
 
 /**
@@ -320,3 +332,164 @@
 	return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
 				outlen);
 }
+
+
+#ifdef CONFIG_TLSV12
+int tlsv12_key_x_server_params_hash(u16 tls_version,
+				    const u8 *client_random,
+				    const u8 *server_random,
+				    const u8 *server_params,
+				    size_t server_params_len, u8 *hash)
+{
+	size_t hlen;
+	struct crypto_hash *ctx;
+
+	ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+	if (ctx == NULL)
+		return -1;
+	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_params, server_params_len);
+	hlen = SHA256_MAC_LEN;
+	if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+		return -1;
+
+	return hlen;
+}
+#endif /* CONFIG_TLSV12 */
+
+
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+				 const u8 *server_random,
+				 const u8 *server_params,
+				 size_t server_params_len, u8 *hash)
+{
+	u8 *hpos;
+	size_t hlen;
+	enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+	struct crypto_hash *ctx;
+
+	hpos = hash;
+
+	if (alg == SIGN_ALG_RSA) {
+		ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+		if (ctx == NULL)
+			return -1;
+		crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+		crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+		crypto_hash_update(ctx, server_params, server_params_len);
+		hlen = MD5_MAC_LEN;
+		if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+			return -1;
+		hpos += hlen;
+	}
+
+	ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	if (ctx == NULL)
+		return -1;
+	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_params, server_params_len);
+	hlen = hash + sizeof(hash) - hpos;
+	if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
+		return -1;
+	hpos += hlen;
+	return hpos - hash;
+}
+
+
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+			 const u8 *data, size_t data_len,
+			 const u8 *pos, size_t len, u8 *alert)
+{
+	u8 *buf;
+	const u8 *end = pos + len;
+	const u8 *decrypted;
+	u16 slen;
+	size_t buflen;
+
+	if (end - pos < 2) {
+		*alert = TLS_ALERT_DECODE_ERROR;
+		return -1;
+	}
+	slen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < slen) {
+		*alert = TLS_ALERT_DECODE_ERROR;
+		return -1;
+	}
+	if (end - pos > slen) {
+		wpa_hexdump(MSG_MSGDUMP, "Additional data after Signature",
+			    pos + slen, end - pos - slen);
+		end = pos + slen;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+	if (pk == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No public key to verify signature");
+		*alert = TLS_ALERT_INTERNAL_ERROR;
+		return -1;
+	}
+
+	buflen = end - pos;
+	buf = os_malloc(end - pos);
+	if (buf == NULL) {
+		*alert = TLS_ALERT_INTERNAL_ERROR;
+		return -1;
+	}
+	if (crypto_public_key_decrypt_pkcs1(pk, pos, end - pos, buf, &buflen) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+		os_free(buf);
+		*alert = TLS_ALERT_DECRYPT_ERROR;
+		return -1;
+	}
+	decrypted = buf;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+			decrypted, buflen);
+
+#ifdef CONFIG_TLSV12
+	if (tls_version >= TLS_VERSION_1_2) {
+		/*
+		 * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+		 *
+		 * DigestInfo ::= SEQUENCE {
+		 *   digestAlgorithm DigestAlgorithm,
+		 *   digest OCTET STRING
+		 * }
+		 *
+		 * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+		 *
+		 * DER encoded DigestInfo for SHA256 per RFC 3447:
+		 * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+		 * H
+		 */
+		if (buflen >= 19 + 32 &&
+		    os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
+			decrypted = buf + 19;
+			buflen -= 19;
+		} else {
+			wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
+			os_free(buf);
+			*alert = TLS_ALERT_DECRYPT_ERROR;
+			return -1;
+		}
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (buflen != data_len ||
+	    os_memcmp_const(decrypted, data, data_len) != 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in CertificateVerify - did not match calculated hash");
+		os_free(buf);
+		*alert = TLS_ALERT_DECRYPT_ERROR;
+		return -1;
+	}
+
+	os_free(buf);
+
+	return 0;
+}
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index f28c0cd..26e68af 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -1,6 +1,6 @@
 /*
  * TLSv1 common definitions
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -257,5 +257,16 @@
 const char * tls_version_str(u16 ver);
 int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
 	    const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
+int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+				    const u8 *server_random,
+				    const u8 *server_params,
+				    size_t server_params_len, u8 *hash);
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+				 const u8 *server_random,
+				 const u8 *server_params,
+				 size_t server_params_len, u8 *hash);
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+			 const u8 *data, size_t data_len,
+			 const u8 *pos, size_t len, u8 *alert);
 
 #endif /* TLSV1_COMMON_H */
diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c
index 3bec3be..0c6897a 100644
--- a/src/tls/tlsv1_record.c
+++ b/src/tls/tlsv1_record.c
@@ -456,7 +456,7 @@
 			return -1;
 		}
 		if (hlen != rl->hash_size ||
-		    os_memcmp(hash, out_data + plen, hlen) != 0 ||
+		    os_memcmp_const(hash, out_data + plen, hlen) != 0 ||
 		    force_mac_error) {
 			wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
 				   "received message (force_mac_error=%d)",
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 2880309..23d0b81 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -21,6 +21,31 @@
  */
 
 
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	va_start(ap, fmt);
+	vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+
+	wpa_printf(MSG_DEBUG, "TLSv1: %s", buf);
+	if (conn->log_cb)
+		conn->log_cb(conn->log_cb_ctx, buf);
+
+	os_free(buf);
+}
+
+
 void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
 {
 	conn->alert_level = level;
@@ -250,8 +275,7 @@
 		used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
 					    out_pos, &olen, &alert);
 		if (used < 0) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
-				   "failed");
+			tlsv1_server_log(conn, "Record layer processing failed");
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 			return -1;
 		}
@@ -265,14 +289,13 @@
 
 		if (ct == TLS_CONTENT_TYPE_ALERT) {
 			if (olen < 2) {
-				wpa_printf(MSG_DEBUG, "TLSv1: Alert "
-					   "underflow");
+				tlsv1_server_log(conn, "Alert underflow");
 				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 						   TLS_ALERT_DECODE_ERROR);
 				return -1;
 			}
-			wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
-				   out_pos[0], out_pos[1]);
+			tlsv1_server_log(conn, "Received alert %d:%d",
+					 out_pos[0], out_pos[1]);
 			if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
 				/* Continue processing */
 				pos += used;
@@ -285,13 +308,23 @@
 		}
 
 		if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
-				   "0x%x", pos[0]);
+			tlsv1_server_log(conn, "Unexpected content type 0x%x",
+					 pos[0]);
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_UNEXPECTED_MESSAGE);
 			return -1;
 		}
 
+#ifdef CONFIG_TESTING_OPTIONS
+		if ((conn->test_flags &
+		     (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH |
+		      TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+		    !conn->test_failure_reported) {
+			tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake");
+			conn->test_failure_reported = 1;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 		out_pos += olen;
 		if (out_pos > out_end) {
 			wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
@@ -361,8 +394,15 @@
 
 	count = 0;
 	suites = conn->cipher_suites;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
 	suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
 	suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
@@ -618,3 +658,125 @@
 	conn->session_ticket_cb = cb;
 	conn->session_ticket_cb_ctx = ctx;
 }
+
+
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+			     void (*cb)(void *ctx, const char *msg), void *ctx)
+{
+	conn->log_cb = cb;
+	conn->log_cb_ctx = ctx;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags)
+{
+	conn->test_flags = flags;
+}
+
+
+static const u8 test_tls_prime15[1] = {
+	15
+};
+
+static const u8 test_tls_prime511b[64] = {
+	0x50, 0xfb, 0xf1, 0xae, 0x01, 0xf1, 0xfe, 0xe6,
+	0xe1, 0xae, 0xdc, 0x1e, 0xbe, 0xfb, 0x9e, 0x58,
+	0x9a, 0xd7, 0x54, 0x9d, 0x6b, 0xb3, 0x78, 0xe2,
+	0x39, 0x7f, 0x30, 0x01, 0x25, 0xa1, 0xf9, 0x7c,
+	0x55, 0x0e, 0xa1, 0x15, 0xcc, 0x36, 0x34, 0xbb,
+	0x6c, 0x8b, 0x64, 0x45, 0x15, 0x7f, 0xd3, 0xe7,
+	0x31, 0xc8, 0x8e, 0x56, 0x8e, 0x95, 0xdc, 0xea,
+	0x9e, 0xdf, 0xf7, 0x56, 0xdd, 0xb0, 0x34, 0xdb
+};
+
+static const u8 test_tls_prime767b[96] = {
+	0x4c, 0xdc, 0xb8, 0x21, 0x20, 0x9d, 0xe8, 0xa3,
+	0x53, 0xd9, 0x1c, 0x18, 0xc1, 0x3a, 0x58, 0x67,
+	0xa7, 0x85, 0xf9, 0x28, 0x9b, 0xce, 0xc0, 0xd1,
+	0x05, 0x84, 0x61, 0x97, 0xb2, 0x86, 0x1c, 0xd0,
+	0xd1, 0x96, 0x23, 0x29, 0x8c, 0xc5, 0x30, 0x68,
+	0x3e, 0xf9, 0x05, 0xba, 0x60, 0xeb, 0xdb, 0xee,
+	0x2d, 0xdf, 0x84, 0x65, 0x49, 0x87, 0x90, 0x2a,
+	0xc9, 0x8e, 0x34, 0x63, 0x6d, 0x9a, 0x2d, 0x32,
+	0x1c, 0x46, 0xd5, 0x4e, 0x20, 0x20, 0x90, 0xac,
+	0xd5, 0x48, 0x79, 0x99, 0x0c, 0xe6, 0xed, 0xbf,
+	0x79, 0xc2, 0x47, 0x50, 0x95, 0x38, 0x38, 0xbc,
+	0xde, 0xb0, 0xd2, 0xe8, 0x97, 0xcb, 0x22, 0xbb
+};
+
+static const u8 test_tls_prime58[128] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0xc1, 0xba, 0xc8, 0x25, 0xbe, 0x2d, 0xf3
+};
+
+static const u8 test_tls_non_prime[] = {
+	/*
+	 * This is not a prime and the value has the following factors:
+	 * 13736783488716579923 * 16254860191773456563 * 18229434976173670763 *
+	 * 11112313018289079419 * 10260802278580253339 * 12394009491575311499 *
+	 * 12419059668711064739 * 14317973192687985827 * 10498605410533203179 *
+	 * 16338688760390249003 * 11128963991123878883 * 12990532258280301419 *
+	 * 3
+	 */
+	0x0C, 0x8C, 0x36, 0x9C, 0x6F, 0x71, 0x2E, 0xA7,
+	0xAB, 0x32, 0xD3, 0x0F, 0x68, 0x3D, 0xB2, 0x6D,
+	0x81, 0xDD, 0xC4, 0x84, 0x0D, 0x9C, 0x6E, 0x36,
+	0x29, 0x70, 0xF3, 0x1E, 0x9A, 0x42, 0x0B, 0x67,
+	0x82, 0x6B, 0xB1, 0xF2, 0xAF, 0x55, 0x28, 0xE7,
+	0xDB, 0x67, 0x6C, 0xF7, 0x6B, 0xAC, 0xAC, 0xE5,
+	0xF7, 0x9F, 0xD4, 0x63, 0x55, 0x70, 0x32, 0x7C,
+	0x70, 0xFB, 0xAF, 0xB8, 0xEB, 0x37, 0xCF, 0x3F,
+	0xFE, 0x94, 0x73, 0xF9, 0x7A, 0xC7, 0x12, 0x2E,
+	0x9B, 0xB4, 0x7D, 0x08, 0x60, 0x83, 0x43, 0x52,
+	0x83, 0x1E, 0xA5, 0xFC, 0xFA, 0x87, 0x12, 0xF4,
+	0x64, 0xE2, 0xCE, 0x71, 0x17, 0x72, 0xB6, 0xAB
+};
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+			   size_t *dh_p_len)
+{
+	*dh_p = conn->cred->dh_p;
+	*dh_p_len = conn->cred->dh_p_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (conn->test_flags & TLS_DHE_PRIME_511B) {
+		tlsv1_server_log(conn, "TESTING: Use short 511-bit prime with DHE");
+		*dh_p = test_tls_prime511b;
+		*dh_p_len = sizeof(test_tls_prime511b);
+	} else if (conn->test_flags & TLS_DHE_PRIME_767B) {
+		tlsv1_server_log(conn, "TESTING: Use short 767-bit prime with DHE");
+		*dh_p = test_tls_prime767b;
+		*dh_p_len = sizeof(test_tls_prime767b);
+	} else if (conn->test_flags & TLS_DHE_PRIME_15) {
+		tlsv1_server_log(conn, "TESTING: Use bogus 15 \"prime\" with DHE");
+		*dh_p = test_tls_prime15;
+		*dh_p_len = sizeof(test_tls_prime15);
+	} else if (conn->test_flags & TLS_DHE_PRIME_58B) {
+		tlsv1_server_log(conn, "TESTING: Use short 58-bit prime in long container with DHE");
+		*dh_p = test_tls_prime58;
+		*dh_p_len = sizeof(test_tls_prime58);
+	} else if (conn->test_flags & TLS_DHE_NON_PRIME) {
+		tlsv1_server_log(conn, "TESTING: Use claim non-prime as the DHE prime");
+		*dh_p = test_tls_non_prime;
+		*dh_p_len = sizeof(test_tls_non_prime);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+}
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index a18c69e..b2b28d1 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -45,4 +45,9 @@
 					tlsv1_server_session_ticket_cb cb,
 					void *ctx);
 
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+			     void (*cb)(void *ctx, const char *msg), void *ctx);
+
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags);
+
 #endif /* TLSV1_SERVER_H */
diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h
index 1f61533..96d79b3 100644
--- a/src/tls/tlsv1_server_i.h
+++ b/src/tls/tlsv1_server_i.h
@@ -51,13 +51,24 @@
 	tlsv1_server_session_ticket_cb session_ticket_cb;
 	void *session_ticket_cb_ctx;
 
+	void (*log_cb)(void *ctx, const char *msg);
+	void *log_cb_ctx;
+
 	int use_session_ticket;
 
 	u8 *dh_secret;
 	size_t dh_secret_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u32 test_flags;
+	int test_failure_reported;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
 void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description);
 int tlsv1_server_derive_keys(struct tlsv1_server *conn,
 			     const u8 *pre_master_secret,
@@ -67,5 +78,7 @@
 			     u8 description, size_t *out_len);
 int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
 				   const u8 *buf, size_t *len);
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+			   size_t *dh_p_len);
 
 #endif /* TLSV1_SERVER_I_H */
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 6f6539b..728e137 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 server - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,6 +27,25 @@
 					  size_t *in_len);
 
 
+static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if ((conn->test_flags &
+	     (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE |
+	      TLS_DHE_PRIME_511B | TLS_DHE_PRIME_767B | TLS_DHE_PRIME_15 |
+	      TLS_DHE_PRIME_58B | TLS_DHE_NON_PRIME)) &&
+	    suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 &&
+	    suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA &&
+	    suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 &&
+	    suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA &&
+	    suite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA)
+		return 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return 0;
+}
+
+
 static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
 				    const u8 *in_data, size_t *in_len)
 {
@@ -38,8 +57,8 @@
 	u16 ext_type, ext_len;
 
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-			   "received content type 0x%x", ct);
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -53,13 +72,13 @@
 
 	/* HandshakeType msg_type */
 	if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-			   "message %d (expected ClientHello)", *pos);
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientHello)",
+				 *pos);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
 	}
-	wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello");
+	tlsv1_server_log(conn, "Received ClientHello");
 	pos++;
 	/* uint24 length */
 	len = WPA_GET_BE24(pos);
@@ -78,13 +97,13 @@
 	if (end - pos < 2)
 		goto decode_error;
 	conn->client_version = WPA_GET_BE16(pos);
-	wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
-		   conn->client_version >> 8, conn->client_version & 0xff);
+	tlsv1_server_log(conn, "Client version %d.%d",
+			 conn->client_version >> 8,
+			 conn->client_version & 0xff);
 	if (conn->client_version < TLS_VERSION_1) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
-			   "ClientHello %u.%u",
-			   conn->client_version >> 8,
-			   conn->client_version & 0xff);
+		tlsv1_server_log(conn, "Unexpected protocol version in ClientHello %u.%u",
+				 conn->client_version >> 8,
+				 conn->client_version & 0xff);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_PROTOCOL_VERSION);
 		return -1;
@@ -101,8 +120,8 @@
 		conn->rl.tls_version = TLS_VERSION_1_1;
 	else
 		conn->rl.tls_version = conn->client_version;
-	wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
-		   tls_version_str(conn->rl.tls_version));
+	tlsv1_server_log(conn, "Using TLS v%s",
+			 tls_version_str(conn->rl.tls_version));
 
 	/* Random random */
 	if (end - pos < TLS_RANDOM_LEN)
@@ -137,6 +156,8 @@
 
 	cipher_suite = 0;
 	for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+		if (testing_cipher_suite_filter(conn, conn->cipher_suites[i]))
+			continue;
 		c = pos;
 		for (j = 0; j < num_suites; j++) {
 			u16 tmp = WPA_GET_BE16(c);
@@ -149,8 +170,7 @@
 	}
 	pos += num_suites * 2;
 	if (!cipher_suite) {
-		wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite "
-			   "available");
+		tlsv1_server_log(conn, "No supported cipher suite available");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_ILLEGAL_PARAMETER);
 		return -1;
@@ -180,16 +200,15 @@
 			compr_null_found = 1;
 	}
 	if (!compr_null_found) {
-		wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL "
-			   "compression");
+		tlsv1_server_log(conn, "Client does not accept NULL compression");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_ILLEGAL_PARAMETER);
 		return -1;
 	}
 
 	if (end - pos == 1) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the "
-			    "end of ClientHello: 0x%02x", *pos);
+		tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x",
+				 *pos);
 		goto decode_error;
 	}
 
@@ -198,12 +217,11 @@
 		ext_len = WPA_GET_BE16(pos);
 		pos += 2;
 
-		wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello "
-			   "extensions", ext_len);
+		tlsv1_server_log(conn, "%u bytes of ClientHello extensions",
+				 ext_len);
 		if (end - pos != ext_len) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello "
-				   "extension list length %u (expected %u)",
-				   ext_len, (unsigned int) (end - pos));
+			tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)",
+					 ext_len, (unsigned int) (end - pos));
 			goto decode_error;
 		}
 
@@ -216,8 +234,7 @@
 
 		while (pos < end) {
 			if (end - pos < 2) {
-				wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
-					   "extension_type field");
+				tlsv1_server_log(conn, "Invalid extension_type field");
 				goto decode_error;
 			}
 
@@ -225,8 +242,7 @@
 			pos += 2;
 
 			if (end - pos < 2) {
-				wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
-					   "extension_data length field");
+				tlsv1_server_log(conn, "Invalid extension_data length field");
 				goto decode_error;
 			}
 
@@ -234,13 +250,12 @@
 			pos += 2;
 
 			if (end - pos < ext_len) {
-				wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
-					   "extension_data field");
+				tlsv1_server_log(conn, "Invalid extension_data field");
 				goto decode_error;
 			}
 
-			wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension "
-				   "type %u", ext_type);
+			tlsv1_server_log(conn, "ClientHello Extension type %u",
+					 ext_type);
 			wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
 				    "Extension data", pos, ext_len);
 
@@ -260,14 +275,13 @@
 
 	*in_len = end - in_data;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to "
-		   "ServerHello");
+	tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello");
 	conn->state = SERVER_HELLO;
 
 	return 0;
 
 decode_error:
-	wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello");
+	tlsv1_server_log(conn, "Failed to decode ClientHello");
 	tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 			   TLS_ALERT_DECODE_ERROR);
 	return -1;
@@ -284,8 +298,8 @@
 	int reason;
 
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-			   "received content type 0x%x", ct);
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -295,8 +309,8 @@
 	left = *in_len;
 
 	if (left < 4) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
-			   "(len=%lu)", (unsigned long) left);
+		tlsv1_server_log(conn, "Too short Certificate message (len=%lu)",
+				 (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -308,9 +322,8 @@
 	left -= 4;
 
 	if (len > left) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
-			   "length (len=%lu != left=%lu)",
-			   (unsigned long) len, (unsigned long) left);
+		tlsv1_server_log(conn, "Unexpected Certificate message length (len=%lu != left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -318,8 +331,7 @@
 
 	if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
 		if (conn->verify_peer) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
-				   "Certificate");
+			tlsv1_server_log(conn, "Client did not include Certificate");
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_UNEXPECTED_MESSAGE);
 			return -1;
@@ -329,17 +341,15 @@
 						       in_len);
 	}
 	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-			   "message %d (expected Certificate/"
-			   "ClientKeyExchange)", type);
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected Certificate/ClientKeyExchange)",
+				 type);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG,
-		   "TLSv1: Received Certificate (certificate_list len %lu)",
-		   (unsigned long) len);
+	tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)",
+			 (unsigned long) len);
 
 	/*
 	 * opaque ASN.1Cert<2^24-1>;
@@ -352,8 +362,8 @@
 	end = pos + len;
 
 	if (end - pos < 3) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
-			   "(left=%lu)", (unsigned long) left);
+		tlsv1_server_log(conn, "Too short Certificate (left=%lu)",
+				 (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -363,10 +373,9 @@
 	pos += 3;
 
 	if ((size_t) (end - pos) != list_len) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
-			   "length (len=%lu left=%lu)",
-			   (unsigned long) list_len,
-			   (unsigned long) (end - pos));
+		tlsv1_server_log(conn, "Unexpected certificate_list length (len=%lu left=%lu)",
+				 (unsigned long) list_len,
+				 (unsigned long) (end - pos));
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -375,8 +384,7 @@
 	idx = 0;
 	while (pos < end) {
 		if (end - pos < 3) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
-				   "certificate_list");
+			tlsv1_server_log(conn, "Failed to parse certificate_list");
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_DECODE_ERROR);
 			x509_certificate_chain_free(chain);
@@ -387,25 +395,23 @@
 		pos += 3;
 
 		if ((size_t) (end - pos) < cert_len) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
-				   "length (len=%lu left=%lu)",
-				   (unsigned long) cert_len,
-				   (unsigned long) (end - pos));
+			tlsv1_server_log(conn, "Unexpected certificate length (len=%lu left=%lu)",
+					 (unsigned long) cert_len,
+					 (unsigned long) (end - pos));
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_DECODE_ERROR);
 			x509_certificate_chain_free(chain);
 			return -1;
 		}
 
-		wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
-			   (unsigned long) idx, (unsigned long) cert_len);
+		tlsv1_server_log(conn, "Certificate %lu (len %lu)",
+				 (unsigned long) idx, (unsigned long) cert_len);
 
 		if (idx == 0) {
 			crypto_public_key_free(conn->client_rsa_key);
 			if (tls_parse_cert(pos, cert_len,
 					   &conn->client_rsa_key)) {
-				wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
-					   "the certificate");
+				tlsv1_server_log(conn, "Failed to parse the certificate");
 				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 						   TLS_ALERT_BAD_CERTIFICATE);
 				x509_certificate_chain_free(chain);
@@ -415,8 +421,7 @@
 
 		cert = x509_certificate_parse(pos, cert_len);
 		if (cert == NULL) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
-				   "the certificate");
+			tlsv1_server_log(conn, "Failed to parse the certificate");
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_BAD_CERTIFICATE);
 			x509_certificate_chain_free(chain);
@@ -436,8 +441,8 @@
 	if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
 					    &reason, 0) < 0) {
 		int tls_reason;
-		wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
-			   "validation failed (reason=%d)", reason);
+		tlsv1_server_log(conn, "Server certificate chain validation failed (reason=%d)",
+				 reason);
 		switch (reason) {
 		case X509_VALIDATE_BAD_CERTIFICATE:
 			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
@@ -494,9 +499,8 @@
 	encr_len = WPA_GET_BE16(pos);
 	pos += 2;
 	if (pos + encr_len > end) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
-			   "format: encr_len=%u left=%u",
-			   encr_len, (unsigned int) (end - pos));
+		tlsv1_server_log(conn, "Invalid ClientKeyExchange format: encr_len=%u left=%u",
+				 encr_len, (unsigned int) (end - pos));
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -539,15 +543,13 @@
 	}
 
 	if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
-			   "length %lu", (unsigned long) outlen);
+		tlsv1_server_log(conn, "Unexpected PreMasterSecret length %lu",
+				 (unsigned long) outlen);
 		use_random = 1;
 	}
 
 	if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
-			   "ClientKeyExchange does not match with version in "
-			   "ClientHello");
+		tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello");
 		use_random = 1;
 	}
 
@@ -582,7 +584,7 @@
 }
 
 
-static int tls_process_client_key_exchange_dh_anon(
+static int tls_process_client_key_exchange_dh(
 	struct tlsv1_server *conn, const u8 *pos, const u8 *end)
 {
 	const u8 *dh_yc;
@@ -590,6 +592,8 @@
 	u8 *shared;
 	size_t shared_len;
 	int res;
+	const u8 *dh_p;
+	size_t dh_p_len;
 
 	/*
 	 * struct {
@@ -600,6 +604,7 @@
 	 * } ClientDiffieHellmanPublic;
 	 */
 
+	tlsv1_server_log(conn, "ClientDiffieHellmanPublic received");
 	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
 		    pos, end - pos);
 
@@ -612,8 +617,7 @@
 	}
 
 	if (end - pos < 3) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value "
-			   "length");
+		tlsv1_server_log(conn, "Invalid client public value length");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -623,8 +627,8 @@
 	dh_yc = pos + 2;
 
 	if (dh_yc + dh_yc_len > end) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow "
-			   "(length %d)", dh_yc_len);
+		tlsv1_server_log(conn, "Client public value overflow (length %d)",
+				 dh_yc_len);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -641,7 +645,9 @@
 		return -1;
 	}
 
-	shared_len = conn->cred->dh_p_len;
+	tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
+	shared_len = dh_p_len;
 	shared = os_malloc(shared_len);
 	if (shared == NULL) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
@@ -653,8 +659,7 @@
 
 	/* shared = Yc^secret mod p */
 	if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
-			   conn->dh_secret_len,
-			   conn->cred->dh_p, conn->cred->dh_p_len,
+			   conn->dh_secret_len, dh_p, dh_p_len,
 			   shared, &shared_len)) {
 		os_free(shared);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -695,8 +700,8 @@
 	const struct tls_cipher_suite *suite;
 
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-			   "received content type 0x%x", ct);
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -706,8 +711,8 @@
 	left = *in_len;
 
 	if (left < 4) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange "
-			   "(Left=%lu)", (unsigned long) left);
+		tlsv1_server_log(conn, "Too short ClientKeyExchange (Left=%lu)",
+				 (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -719,9 +724,8 @@
 	left -= 4;
 
 	if (len > left) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange "
-			   "length (len=%lu != left=%lu)",
-			   (unsigned long) len, (unsigned long) left);
+		tlsv1_server_log(conn, "Mismatch in ClientKeyExchange length (len=%lu != left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -730,14 +734,14 @@
 	end = pos + len;
 
 	if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-			   "message %d (expected ClientKeyExchange)", type);
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientKeyExchange)",
+				 type);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange");
+	tlsv1_server_log(conn, "Received ClientKeyExchange");
 
 	wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
 
@@ -747,11 +751,11 @@
 	else
 		keyx = suite->key_exchange;
 
-	if (keyx == TLS_KEY_X_DH_anon &&
-	    tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+	if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) &&
+	    tls_process_client_key_exchange_dh(conn, pos, end) < 0)
 		return -1;
 
-	if (keyx != TLS_KEY_X_DH_anon &&
+	if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA &&
 	    tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
 		return -1;
 
@@ -769,15 +773,14 @@
 	const u8 *pos, *end;
 	size_t left, len;
 	u8 type;
-	size_t hlen, buflen;
-	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf;
+	size_t hlen;
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
 	enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
-	u16 slen;
+	u8 alert;
 
 	if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
 		if (conn->verify_peer) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
-				   "CertificateVerify");
+			tlsv1_server_log(conn, "Client did not include CertificateVerify");
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_UNEXPECTED_MESSAGE);
 			return -1;
@@ -788,8 +791,8 @@
 	}
 
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-			   "received content type 0x%x", ct);
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -799,8 +802,8 @@
 	left = *in_len;
 
 	if (left < 4) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify "
-			   "message (len=%lu)", (unsigned long) left);
+		tlsv1_server_log(conn, "Too short CertificateVerify message (len=%lu)",
+				 (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -812,9 +815,8 @@
 	left -= 4;
 
 	if (len > left) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify "
-			   "message length (len=%lu != left=%lu)",
-			   (unsigned long) len, (unsigned long) left);
+		tlsv1_server_log(conn, "Unexpected CertificateVerify message length (len=%lu != left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -823,14 +825,14 @@
 	end = pos + len;
 
 	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-			   "message %d (expected CertificateVerify)", type);
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected CertificateVerify)",
+				 type);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify");
+	tlsv1_server_log(conn, "Received CertificateVerify");
 
 	/*
 	 * struct {
@@ -917,89 +919,12 @@
 
 	wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
 
-	if (end - pos < 2) {
-		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-				   TLS_ALERT_DECODE_ERROR);
+	if (tls_verify_signature(conn->rl.tls_version, conn->client_rsa_key,
+				 hash, hlen, pos, end - pos, &alert) < 0) {
+		tlsv1_server_log(conn, "Invalid Signature in CertificateVerify");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 		return -1;
 	}
-	slen = WPA_GET_BE16(pos);
-	pos += 2;
-	if (end - pos < slen) {
-		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-				   TLS_ALERT_DECODE_ERROR);
-		return -1;
-	}
-
-	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
-	if (conn->client_rsa_key == NULL) {
-		wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify "
-			   "signature");
-		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-				   TLS_ALERT_INTERNAL_ERROR);
-		return -1;
-	}
-
-	buflen = end - pos;
-	buf = os_malloc(end - pos);
-	if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key,
-					    pos, end - pos, buf, &buflen) < 0)
-	{
-		wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
-		os_free(buf);
-		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-				   TLS_ALERT_DECRYPT_ERROR);
-		return -1;
-	}
-
-	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
-			buf, buflen);
-
-#ifdef CONFIG_TLSV12
-	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
-		/*
-		 * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
-		 *
-		 * DigestInfo ::= SEQUENCE {
-		 *   digestAlgorithm DigestAlgorithm,
-		 *   digest OCTET STRING
-		 * }
-		 *
-		 * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
-		 *
-		 * DER encoded DigestInfo for SHA256 per RFC 3447:
-		 * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
-		 * H
-		 */
-		if (buflen >= 19 + 32 &&
-		    os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
-			      "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
-		{
-			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = "
-				   "SHA-256");
-			os_memmove(buf, buf + 19, buflen - 19);
-			buflen -= 19;
-		} else {
-			wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized "
-				   "DigestInfo");
-			os_free(buf);
-			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-					   TLS_ALERT_DECRYPT_ERROR);
-			return -1;
-		}
-	}
-#endif /* CONFIG_TLSV12 */
-
-	if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
-			   "CertificateVerify - did not match with calculated "
-			   "hash");
-		os_free(buf);
-		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-				   TLS_ALERT_DECRYPT_ERROR);
-		return -1;
-	}
-
-	os_free(buf);
 
 	*in_len = end - in_data;
 
@@ -1017,8 +942,8 @@
 	size_t left;
 
 	if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
-			   "received content type 0x%x", ct);
+		tlsv1_server_log(conn, "Expected ChangeCipherSpec; received content type 0x%x",
+				 ct);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -1028,21 +953,21 @@
 	left = *in_len;
 
 	if (left < 1) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+		tlsv1_server_log(conn, "Too short ChangeCipherSpec");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
 	}
 
 	if (*pos != TLS_CHANGE_CIPHER_SPEC) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
-			   "received data 0x%x", *pos);
+		tlsv1_server_log(conn, "Expected ChangeCipherSpec; received data 0x%x",
+				 *pos);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+	tlsv1_server_log(conn, "Received ChangeCipherSpec");
 	if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
 			   "for record layer");
@@ -1067,9 +992,48 @@
 	u8 verify_data[TLS_VERIFY_DATA_LEN];
 	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if ((conn->test_flags &
+	     (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after invalid ServerKeyExchange");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_15) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after bogus DHE \"prime\" 15");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_58B) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after short 58-bit DHE prime in long container");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_511B) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-WARNING: Client Finished received after short 511-bit DHE prime (insecure)");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_767B) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after 767-bit DHE prime (relatively insecure)");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_NON_PRIME) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after non-prime claimed as DHE prime");
+		conn->test_failure_reported = 1;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
-			   "received content type 0x%x", ct);
+		tlsv1_server_log(conn, "Expected Finished; received content type 0x%x",
+				 ct);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -1079,9 +1043,8 @@
 	left = *in_len;
 
 	if (left < 4) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
-			   "Finished",
-			   (unsigned long) left);
+		tlsv1_server_log(conn, "Too short record (left=%lu) forFinished",
+				 (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -1101,18 +1064,16 @@
 	left -= 4;
 
 	if (len > left) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
-			   "(len=%lu > left=%lu)",
-			   (unsigned long) len, (unsigned long) left);
+		tlsv1_server_log(conn, "Too short buffer for Finished (len=%lu > left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
 	}
 	end = pos + len;
 	if (len != TLS_VERIFY_DATA_LEN) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
-			   "in Finished: %lu (expected %d)",
-			   (unsigned long) len, TLS_VERIFY_DATA_LEN);
+		tlsv1_server_log(conn, "Unexpected verify_data length in Finished: %lu (expected %d)",
+				 (unsigned long) len, TLS_VERIFY_DATA_LEN);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_DECODE_ERROR);
 		return -1;
@@ -1174,19 +1135,18 @@
 	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
 			verify_data, TLS_VERIFY_DATA_LEN);
 
-	if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
-		wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+	if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+		tlsv1_server_log(conn, "Mismatch in verify_data");
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+	tlsv1_server_log(conn, "Received Finished");
 
 	*in_len = end - in_data;
 
 	if (conn->use_session_ticket) {
 		/* Abbreviated handshake using session ticket; RFC 4507 */
-		wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed "
-			   "successfully");
+		tlsv1_server_log(conn, "Abbreviated handshake completed successfully");
 		conn->state = ESTABLISHED;
 	} else {
 		/* Full handshake */
@@ -1202,13 +1162,12 @@
 {
 	if (ct == TLS_CONTENT_TYPE_ALERT) {
 		if (*len < 2) {
-			wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+			tlsv1_server_log(conn, "Alert underflow");
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_DECODE_ERROR);
 			return -1;
 		}
-		wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
-			   buf[0], buf[1]);
+		tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]);
 		*len = 2;
 		conn->state = FAILED;
 		return -1;
@@ -1240,9 +1199,8 @@
 			return -1;
 		break;
 	default:
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
-			   "while processing received message",
-			   conn->state);
+		tlsv1_server_log(conn, "Unexpected state %d while processing received message",
+				 conn->state);
 		return -1;
 	}
 
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 6d8e55e..15e6692 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 server - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -48,7 +48,7 @@
 
 	pos = *msgpos;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
+	tlsv1_server_log(conn, "Send ServerHello");
 	rhdr = pos;
 	pos += TLS_RECORD_HEADER_LEN;
 
@@ -104,8 +104,7 @@
 			conn->client_random, conn->server_random,
 			conn->master_secret);
 		if (res < 0) {
-			wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
-				   "indicated failure");
+			tlsv1_server_log(conn, "SessionTicket callback indicated failure");
 			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 					   TLS_ALERT_HANDSHAKE_FAILURE);
 			return -1;
@@ -170,7 +169,7 @@
 
 	pos = *msgpos;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+	tlsv1_server_log(conn, "Send Certificate");
 	rhdr = pos;
 	pos += TLS_RECORD_HEADER_LEN;
 
@@ -245,10 +244,12 @@
 {
 	tls_key_exchange keyx;
 	const struct tls_cipher_suite *suite;
-	u8 *pos, *rhdr, *hs_start, *hs_length;
+	u8 *pos, *rhdr, *hs_start, *hs_length, *server_params;
 	size_t rlen;
 	u8 *dh_ys;
 	size_t dh_ys_len;
+	const u8 *dh_p;
+	size_t dh_p_len;
 
 	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
 	if (suite == NULL)
@@ -261,8 +262,7 @@
 		return 0;
 	}
 
-	if (keyx != TLS_KEY_X_DH_anon) {
-		/* TODO? */
+	if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) {
 		wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
 			   "supported with key exchange type %d", keyx);
 		return -1;
@@ -275,8 +275,10 @@
 		return -1;
 	}
 
+	tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
 	os_free(conn->dh_secret);
-	conn->dh_secret_len = conn->cred->dh_p_len;
+	conn->dh_secret_len = dh_p_len;
 	conn->dh_secret = os_malloc(conn->dh_secret_len);
 	if (conn->dh_secret == NULL) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
@@ -295,8 +297,7 @@
 		return -1;
 	}
 
-	if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
-	    0)
+	if (os_memcmp(conn->dh_secret, dh_p, conn->dh_secret_len) > 0)
 		conn->dh_secret[0] = 0; /* make sure secret < p */
 
 	pos = conn->dh_secret;
@@ -311,7 +312,7 @@
 			conn->dh_secret, conn->dh_secret_len);
 
 	/* Ys = g^secret mod p */
-	dh_ys_len = conn->cred->dh_p_len;
+	dh_ys_len = dh_p_len;
 	dh_ys = os_malloc(dh_ys_len);
 	if (dh_ys == NULL) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
@@ -322,8 +323,7 @@
 	}
 	if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
 			   conn->dh_secret, conn->dh_secret_len,
-			   conn->cred->dh_p, conn->cred->dh_p_len,
-			   dh_ys, &dh_ys_len)) {
+			   dh_p, dh_p_len, dh_ys, &dh_ys_len)) {
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 				   TLS_ALERT_INTERNAL_ERROR);
 		os_free(dh_ys);
@@ -354,7 +354,7 @@
 
 	pos = *msgpos;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
+	tlsv1_server_log(conn, "Send ServerKeyExchange");
 	rhdr = pos;
 	pos += TLS_RECORD_HEADER_LEN;
 
@@ -369,8 +369,9 @@
 	pos += 3;
 
 	/* body - ServerDHParams */
+	server_params = pos;
 	/* dh_p */
-	if (pos + 2 + conn->cred->dh_p_len > end) {
+	if (pos + 2 + dh_p_len > end) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
 			   "dh_p");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -378,10 +379,10 @@
 		os_free(dh_ys);
 		return -1;
 	}
-	WPA_PUT_BE16(pos, conn->cred->dh_p_len);
+	WPA_PUT_BE16(pos, dh_p_len);
 	pos += 2;
-	os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
-	pos += conn->cred->dh_p_len;
+	os_memcpy(pos, dh_p, dh_p_len);
+	pos += dh_p_len;
 
 	/* dh_g */
 	if (pos + 2 + conn->cred->dh_g_len > end) {
@@ -412,6 +413,138 @@
 	pos += dh_ys_len;
 	os_free(dh_ys);
 
+	/*
+	 * select (SignatureAlgorithm)
+	 * {   case anonymous: struct { };
+	 *     case rsa:
+	 *         digitally-signed struct {
+	 *             opaque md5_hash[16];
+	 *             opaque sha_hash[20];
+	 *         };
+	 *     case dsa:
+	 *         digitally-signed struct {
+	 *             opaque sha_hash[20];
+	 *         };
+	 * } Signature;
+	 *
+	 * md5_hash
+	 *     MD5(ClientHello.random + ServerHello.random + ServerParams);
+	 *
+	 * sha_hash
+	 *     SHA(ClientHello.random + ServerHello.random + ServerParams);
+	 */
+
+	if (keyx == TLS_KEY_X_DHE_RSA) {
+		u8 hash[100];
+		u8 *signed_start;
+		size_t clen;
+		int hlen;
+
+		if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+			hlen = tlsv12_key_x_server_params_hash(
+				conn->rl.tls_version, conn->client_random,
+				conn->server_random, server_params,
+				pos - server_params, hash + 19);
+
+			/*
+			 * RFC 5246, 4.7:
+			 * TLS v1.2 adds explicit indication of the used
+			 * signature and hash algorithms.
+			 *
+			 * struct {
+			 *   HashAlgorithm hash;
+			 *   SignatureAlgorithm signature;
+			 * } SignatureAndHashAlgorithm;
+			 */
+			if (hlen < 0 || pos + 2 > end) {
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+			*pos++ = TLS_HASH_ALG_SHA256;
+			*pos++ = TLS_SIGN_ALG_RSA;
+
+			/*
+			 * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+			 *
+			 * DigestInfo ::= SEQUENCE {
+			 *   digestAlgorithm DigestAlgorithm,
+			 *   digest OCTET STRING
+			 * }
+			 *
+			 * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+			 *
+			 * DER encoded DigestInfo for SHA256 per RFC 3447:
+			 * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00
+			 * 04 20 || H
+			 */
+			hlen += 19;
+			os_memcpy(hash,
+				  "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+				  "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+
+#else /* CONFIG_TLSV12 */
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+#endif /* CONFIG_TLSV12 */
+		} else {
+			hlen = tls_key_x_server_params_hash(
+				conn->rl.tls_version, conn->client_random,
+				conn->server_random, server_params,
+				pos - server_params, hash);
+		}
+
+		if (hlen < 0) {
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+
+		wpa_hexdump(MSG_MSGDUMP, "TLS: ServerKeyExchange signed_params hash",
+			    hash, hlen);
+#ifdef CONFIG_TESTING_OPTIONS
+		if (conn->test_flags & TLS_BREAK_SRV_KEY_X_HASH) {
+			tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params hash");
+			hash[hlen - 1] ^= 0x80;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		/*
+		 * RFC 2246, 4.7:
+		 * In digital signing, one-way hash functions are used as input
+		 * for a signing algorithm. A digitally-signed element is
+		 * encoded as an opaque vector <0..2^16-1>, where the length is
+		 * specified by the signing algorithm and key.
+		 *
+		 * In RSA signing, a 36-byte structure of two hashes (one SHA
+		 * and one MD5) is signed (encrypted with the private key). It
+		 * is encoded with PKCS #1 block type 0 or type 1 as described
+		 * in [PKCS1].
+		 */
+		signed_start = pos; /* length to be filled */
+		pos += 2;
+		clen = end - pos;
+		if (conn->cred == NULL ||
+		    crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+						  pos, &clen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		WPA_PUT_BE16(signed_start, clen);
+#ifdef CONFIG_TESTING_OPTIONS
+		if (conn->test_flags & TLS_BREAK_SRV_KEY_X_SIGNATURE) {
+			tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params signature");
+			pos[clen - 1] ^= 0x80;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		pos += clen;
+	}
+
 	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
 	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
@@ -445,7 +578,7 @@
 
 	pos = *msgpos;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
+	tlsv1_server_log(conn, "Send CertificateRequest");
 	rhdr = pos;
 	pos += TLS_RECORD_HEADER_LEN;
 
@@ -505,7 +638,7 @@
 	size_t rlen;
 	u8 payload[4];
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
+	tlsv1_server_log(conn, "Send ServerHelloDone");
 
 	/* opaque fragment[TLSPlaintext.length] */
 
@@ -541,7 +674,7 @@
 	size_t rlen;
 	u8 payload[1];
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+	tlsv1_server_log(conn, "Send ChangeCipherSpec");
 
 	payload[0] = TLS_CHANGE_CIPHER_SPEC;
 
@@ -578,7 +711,7 @@
 
 	pos = *msgpos;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+	tlsv1_server_log(conn, "Send Finished");
 
 	/* Encrypted Handshake Message: Finished */
 
@@ -635,6 +768,12 @@
 	}
 	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
 			verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (conn->test_flags & TLS_BREAK_VERIFY_DATA) {
+		tlsv1_server_log(conn, "TESTING: Break verify_data (server)");
+		verify_data[1 + 3 + 1] ^= 0x80;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	/* Handshake */
 	pos = hs_start = verify_data;
@@ -736,7 +875,7 @@
 
 	*out_len = pos - msg;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
+	tlsv1_server_log(conn, "Handshake completed successfully");
 	conn->state = ESTABLISHED;
 
 	return msg;
@@ -755,8 +894,8 @@
 			/* Abbreviated handshake was already completed. */
 			return NULL;
 		}
-		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
-			   "generating reply", conn->state);
+		tlsv1_server_log(conn, "Unexpected state %d while generating reply",
+				 conn->state);
 		return NULL;
 	}
 }
@@ -767,7 +906,7 @@
 {
 	u8 *alert, *pos, *length;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+	tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description);
 	*out_len = 0;
 
 	alert = os_malloc(10);
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index 06540bf..e1e4df8 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -1348,7 +1348,8 @@
 		wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
 		/* TODO: parse UniqueIdentifier ::= BIT STRING */
 
-		if (hdr.payload + hdr.length == end)
+		pos = hdr.payload + hdr.length;
+		if (pos == end)
 			return 0;
 
 		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
@@ -1366,7 +1367,8 @@
 		wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
 		/* TODO: parse UniqueIdentifier ::= BIT STRING */
 
-		if (hdr.payload + hdr.length == end)
+		pos = hdr.payload + hdr.length;
+		if (pos == end)
 			return 0;
 
 		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
@@ -1774,13 +1776,22 @@
 	}
 
 	if (hdr.length != hash_len ||
-	    os_memcmp(hdr.payload, hash, hdr.length) != 0) {
+	    os_memcmp_const(hdr.payload, hash, hdr.length) != 0) {
 		wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
 			   "with calculated tbsCertificate hash");
 		os_free(data);
 		return -1;
 	}
 
+	if (hdr.payload + hdr.length < data + data_len) {
+		wpa_hexdump(MSG_INFO,
+			    "X509: Extra data after certificate signature hash",
+			    hdr.payload + hdr.length,
+			    data + data_len - hdr.payload - hdr.length);
+		os_free(data);
+		return -1;
+	}
+
 	os_free(data);
 
 	wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 940b4d8..8aad813 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -1,7 +1,7 @@
 all: libutils.a
 
 clean:
-	rm -f *~ *.o *.d libutils.a
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a
 
 install:
 	@echo Nothing to be made.
@@ -11,6 +11,7 @@
 
 #CFLAGS += -DWPA_TRACE
 CFLAGS += -DCONFIG_IPV6
+CFLAGS += -DCONFIG_DEBUG_FILE
 
 LIB_OBJS= \
 	base64.o \
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
new file mode 100644
index 0000000..a066392
--- /dev/null
+++ b/src/utils/browser-android.c
@@ -0,0 +1,117 @@
+/*
+ * Hotspot 2.0 client - Web browser using Android browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+	int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+	wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+		   "complete");
+	eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+	struct browser_data *data = ctx;
+	struct wpabuf *resp;
+	const char *url;
+	int done = 0;
+
+	url = http_request_get_uri(req);
+	wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+	if (os_strcmp(url, "/") == 0) {
+		data->success = 1;
+		done = 1;
+	} else if (os_strncmp(url, "/osu/", 5) == 0) {
+		data->success = atoi(url + 5);
+		done = 1;
+	}
+
+	resp = wpabuf_alloc(1);
+	if (resp == NULL) {
+		http_request_deinit(req);
+		if (done)
+			eloop_terminate();
+		return;
+	}
+
+	if (done) {
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+	}
+
+	http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+	char cmd[2000];
+	int ret;
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+
+	wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	ret = os_snprintf(cmd, sizeof(cmd),
+			  "am start -a android.intent.action.VIEW -d '%s' "
+			  "-n com.android.browser/.BrowserActivity", url);
+	if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+		wpa_printf(MSG_ERROR, "Too long URL");
+		return -1;
+	}
+
+	if (eloop_init() < 0) {
+		wpa_printf(MSG_ERROR, "eloop_init failed");
+		return -1;
+	}
+	addr.s_addr = htonl((127 << 24) | 1);
+	http = http_server_init(&addr, 12345, http_req, &data);
+	if (http == NULL) {
+		wpa_printf(MSG_ERROR, "http_server_init failed");
+		eloop_destroy();
+		return -1;
+	}
+
+	if (system(cmd) != 0) {
+		wpa_printf(MSG_INFO, "Failed to launch Android browser");
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	eloop_register_timeout(30, 0, browser_timeout, &data, NULL);
+	eloop_run();
+	eloop_cancel_timeout(browser_timeout, &data, NULL);
+	http_server_deinit(http);
+	eloop_destroy();
+
+	wpa_printf(MSG_INFO, "Closing Android browser");
+	if (system("input keyevent 3") != 0) {
+		wpa_printf(MSG_INFO, "Failed to inject keyevent");
+	}
+
+	return data.success;
+}
diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c
new file mode 100644
index 0000000..2884d34
--- /dev/null
+++ b/src/utils/browser-system.c
@@ -0,0 +1,112 @@
+/*
+ * Hotspot 2.0 client - Web browser using system browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+	int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+	wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+		   "complete");
+	eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+	struct browser_data *data = ctx;
+	struct wpabuf *resp;
+	const char *url;
+	int done = 0;
+
+	url = http_request_get_uri(req);
+	wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+	if (os_strcmp(url, "/") == 0) {
+		data->success = 1;
+		done = 1;
+	} else if (os_strncmp(url, "/osu/", 5) == 0) {
+		data->success = atoi(url + 5);
+		done = 1;
+	}
+
+	resp = wpabuf_alloc(1);
+	if (resp == NULL) {
+		http_request_deinit(req);
+		if (done)
+			eloop_terminate();
+		return;
+	}
+
+	if (done) {
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+	}
+
+	http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+	char cmd[2000];
+	int ret;
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+
+	wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	ret = os_snprintf(cmd, sizeof(cmd), "x-www-browser '%s' &", url);
+	if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+		wpa_printf(MSG_ERROR, "Too long URL");
+		return -1;
+	}
+
+	if (eloop_init() < 0) {
+		wpa_printf(MSG_ERROR, "eloop_init failed");
+		return -1;
+	}
+	addr.s_addr = htonl((127 << 24) | 1);
+	http = http_server_init(&addr, 12345, http_req, &data);
+	if (http == NULL) {
+		wpa_printf(MSG_ERROR, "http_server_init failed");
+		eloop_destroy();
+		return -1;
+	}
+
+	if (system(cmd) != 0) {
+		wpa_printf(MSG_INFO, "Failed to launch browser");
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	eloop_register_timeout(120, 0, browser_timeout, &data, NULL);
+	eloop_run();
+	eloop_cancel_timeout(browser_timeout, &data, NULL);
+	http_server_deinit(http);
+	eloop_destroy();
+
+	/* TODO: Close browser */
+
+	return data.success;
+}
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
new file mode 100644
index 0000000..eeb8f65
--- /dev/null
+++ b/src/utils/browser-wpadebug.c
@@ -0,0 +1,123 @@
+/*
+ * Hotspot 2.0 client - Web browser using wpadebug on Android
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+	int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+	wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+		   "complete");
+	eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+	struct browser_data *data = ctx;
+	struct wpabuf *resp;
+	const char *url;
+	int done = 0;
+
+	url = http_request_get_uri(req);
+	wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+	if (os_strcmp(url, "/") == 0) {
+		data->success = 1;
+		done = 1;
+	} else if (os_strncmp(url, "/osu/", 5) == 0) {
+		data->success = atoi(url + 5);
+		done = 1;
+	}
+
+	resp = wpabuf_alloc(100);
+	if (resp == NULL) {
+		http_request_deinit(req);
+		if (done)
+			eloop_terminate();
+		return;
+	}
+	wpabuf_put_str(resp, "User input completed");
+
+	if (done) {
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+	}
+
+	http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+	char cmd[2000];
+	int ret;
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+
+	wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	ret = os_snprintf(cmd, sizeof(cmd),
+			  "am start -a android.action.MAIN "
+			  "-c android.intent.category.LAUNCHER "
+			  "-n w1.fi.wpadebug/.WpaWebViewActivity "
+			  "-e w1.fi.wpadebug.URL '%s'", url);
+	if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+		wpa_printf(MSG_ERROR, "Too long URL");
+		return -1;
+	}
+
+	if (eloop_init() < 0) {
+		wpa_printf(MSG_ERROR, "eloop_init failed");
+		return -1;
+	}
+	addr.s_addr = htonl((127 << 24) | 1);
+	http = http_server_init(&addr, 12345, http_req, &data);
+	if (http == NULL) {
+		wpa_printf(MSG_ERROR, "http_server_init failed");
+		eloop_destroy();
+		return -1;
+	}
+
+	if (system(cmd) != 0) {
+		wpa_printf(MSG_INFO, "Failed to launch wpadebug browser");
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	eloop_register_timeout(300, 0, browser_timeout, &data, NULL);
+	eloop_run();
+	eloop_cancel_timeout(browser_timeout, &data, NULL);
+	http_server_deinit(http);
+	eloop_destroy();
+
+	wpa_printf(MSG_INFO, "Closing Android browser");
+	if (system("am start -a android.action.MAIN "
+		   "-c android.intent.category.LAUNCHER "
+		   "-n w1.fi.wpadebug/.WpaWebViewActivity "
+		   "-e w1.fi.wpadebug.URL FINISH") != 0) {
+		wpa_printf(MSG_INFO, "Failed to close wpadebug browser");
+	}
+
+	return data.success;
+}
diff --git a/src/utils/browser.c b/src/utils/browser.c
new file mode 100644
index 0000000..9cf6152
--- /dev/null
+++ b/src/utils/browser.c
@@ -0,0 +1,219 @@
+/*
+ * Hotspot 2.0 client - Web browser using WebKit
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <webkit/webkit.h>
+
+#include "common.h"
+#include "browser.h"
+
+
+struct browser_context {
+	GtkWidget *win;
+	int success;
+	int progress;
+	char *hover_link;
+	char *title;
+};
+
+static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__);
+	gtk_main_quit();
+}
+
+
+static void browser_update_title(struct browser_context *ctx)
+{
+	char buf[100];
+
+	if (ctx->hover_link) {
+		gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link);
+		return;
+	}
+
+	if (ctx->progress == 100) {
+		gtk_window_set_title(GTK_WINDOW(ctx->win),
+				     ctx->title ? ctx->title :
+				     "Hotspot 2.0 client");
+		return;
+	}
+
+	snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress,
+		 ctx->title ? ctx->title : "Hotspot 2.0 client");
+	gtk_window_set_title(GTK_WINDOW(ctx->win), buf);
+}
+
+
+static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec,
+				    struct browser_context *ctx)
+{
+	ctx->progress = 100 * webkit_web_view_get_progress(view);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
+		   ctx->progress);
+	browser_update_title(ctx);
+}
+
+
+static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec,
+				       struct browser_context *ctx)
+{
+	int status = webkit_web_view_get_load_status(view);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s",
+		   __func__, status, webkit_web_view_get_uri(view));
+}
+
+
+static void view_cb_resource_request_starting(WebKitWebView *view,
+					      WebKitWebFrame *frame,
+					      WebKitWebResource *res,
+					      WebKitNetworkRequest *req,
+					      WebKitNetworkResponse *resp,
+					      struct browser_context *ctx)
+{
+	const gchar *uri = webkit_network_request_get_uri(req);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+	if (g_str_has_suffix(uri, "/favicon.ico"))
+		webkit_network_request_set_uri(req, "about:blank");
+	if (g_str_has_prefix(uri, "osu://")) {
+		ctx->success = atoi(uri + 6);
+		gtk_main_quit();
+	}
+	if (g_str_has_prefix(uri, "http://localhost:12345")) {
+		/*
+		 * This is used as a special trigger to indicate that the
+		 * user exchange has been completed.
+		 */
+		ctx->success = 1;
+		gtk_main_quit();
+	}
+}
+
+
+static gboolean view_cb_mime_type_policy_decision(
+	WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req,
+	gchar *mime, WebKitWebPolicyDecision *policy,
+	struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime);
+
+	if (!webkit_web_view_can_show_mime_type(view, mime)) {
+		webkit_web_policy_decision_download(policy);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static gboolean view_cb_download_requested(WebKitWebView *view,
+					   WebKitDownload *dl,
+					   struct browser_context *ctx)
+{
+	const gchar *uri;
+	uri = webkit_download_get_uri(dl);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+	return FALSE;
+}
+
+
+static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title,
+				       gchar *uri, struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title,
+		   uri);
+	os_free(ctx->hover_link);
+	if (uri)
+		ctx->hover_link = os_strdup(uri);
+	else
+		ctx->hover_link = NULL;
+
+	browser_update_title(ctx);
+}
+
+
+static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame,
+				  const char *title,
+				  struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
+	os_free(ctx->title);
+	ctx->title = os_strdup(title);
+	browser_update_title(ctx);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+	GtkWidget *scroll;
+	SoupSession *s;
+	WebKitWebView *view;
+	WebKitWebSettings *settings;
+	struct browser_context ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+	if (!gtk_init_check(NULL, NULL))
+		return -1;
+
+	s = webkit_get_default_session();
+	g_object_set(G_OBJECT(s), "ssl-ca-file",
+		     "/etc/ssl/certs/ca-certificates.crt", NULL);
+	g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
+
+	ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client",
+			       "Hotspot 2.0 client");
+	gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
+
+	scroll = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+				       GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+	g_signal_connect(G_OBJECT(ctx.win), "destroy",
+			 G_CALLBACK(win_cb_destroy), &ctx);
+
+	view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+	g_signal_connect(G_OBJECT(view), "notify::progress",
+			 G_CALLBACK(view_cb_notify_progress), &ctx);
+	g_signal_connect(G_OBJECT(view), "notify::load-status",
+			 G_CALLBACK(view_cb_notify_load_status), &ctx);
+	g_signal_connect(G_OBJECT(view), "resource-request-starting",
+			 G_CALLBACK(view_cb_resource_request_starting), &ctx);
+	g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
+			 G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
+	g_signal_connect(G_OBJECT(view), "download-requested",
+			 G_CALLBACK(view_cb_download_requested), &ctx);
+	g_signal_connect(G_OBJECT(view), "hovering-over-link",
+			 G_CALLBACK(view_cb_hovering_over_link), &ctx);
+	g_signal_connect(G_OBJECT(view), "title-changed",
+			 G_CALLBACK(view_cb_title_changed), &ctx);
+
+	gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
+	gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
+
+	gtk_widget_grab_focus(GTK_WIDGET(view));
+	gtk_widget_show_all(ctx.win);
+
+	settings = webkit_web_view_get_settings(view);
+	g_object_set(G_OBJECT(settings), "user-agent",
+		     "Mozilla/5.0 (X11; U; Unix; en-US) "
+		     "AppleWebKit/537.15 (KHTML, like Gecko) "
+		     "hs20-client/1.0", NULL);
+	g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
+
+	webkit_web_view_load_uri(view, url);
+
+	gtk_main();
+	gtk_widget_destroy(ctx.win);
+	while (gtk_events_pending())
+		gtk_main_iteration();
+
+	free(ctx.hover_link);
+	free(ctx.title);
+	return ctx.success;
+}
diff --git a/src/utils/browser.h b/src/utils/browser.h
new file mode 100644
index 0000000..aaa0eed
--- /dev/null
+++ b/src/utils/browser.h
@@ -0,0 +1,21 @@
+/*
+ * Hotspot 2.0 client - Web browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BROWSER_H
+#define BROWSER_H
+
+#ifdef CONFIG_NO_BROWSER
+static inline int hs20_web_browser(const char *url)
+{
+	return -1;
+}
+#else /* CONFIG_NO_BROWSER */
+int hs20_web_browser(const char *url);
+#endif /* CONFIG_NO_BROWSER */
+
+#endif /* BROWSER_H */
diff --git a/src/utils/common.c b/src/utils/common.c
index bf326cd..5b017e5 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -350,7 +350,7 @@
 	size_t i;
 
 	for (i = 0; i < len; i++) {
-		if (txt + 4 > end)
+		if (txt + 4 >= end)
 			break;
 
 		switch (data[i]) {
@@ -400,7 +400,7 @@
 	int val;
 
 	while (*pos) {
-		if (len == maxlen)
+		if (len + 1 >= maxlen)
 			break;
 		switch (*pos) {
 		case '\\':
@@ -468,6 +468,8 @@
 			break;
 		}
 	}
+	if (maxlen > len)
+		buf[len] = '\0';
 
 	return len;
 }
@@ -576,6 +578,21 @@
 }
 
 
+int find_first_bit(u32 value)
+{
+	int pos = 0;
+
+	while (value) {
+		if (value & 0x1)
+			return pos;
+		value >>= 1;
+		pos++;
+	}
+
+	return -1;
+}
+
+
 size_t merge_byte_arrays(u8 *res, size_t res_len,
 			 const u8 *src1, size_t src1_len,
 			 const u8 *src2, size_t src2_len)
@@ -622,3 +639,210 @@
 
 	return res;
 }
+
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value)
+{
+	struct wpa_freq_range *freq = NULL, *n;
+	unsigned int count = 0;
+	const char *pos, *pos2, *pos3;
+
+	/*
+	 * Comma separated list of frequency ranges.
+	 * For example: 2412-2432,2462,5000-6000
+	 */
+	pos = value;
+	while (pos && pos[0]) {
+		n = os_realloc_array(freq, count + 1,
+				     sizeof(struct wpa_freq_range));
+		if (n == NULL) {
+			os_free(freq);
+			return -1;
+		}
+		freq = n;
+		freq[count].min = atoi(pos);
+		pos2 = os_strchr(pos, '-');
+		pos3 = os_strchr(pos, ',');
+		if (pos2 && (!pos3 || pos2 < pos3)) {
+			pos2++;
+			freq[count].max = atoi(pos2);
+		} else
+			freq[count].max = freq[count].min;
+		pos = pos3;
+		if (pos)
+			pos++;
+		count++;
+	}
+
+	os_free(res->range);
+	res->range = freq;
+	res->num = count;
+
+	return 0;
+}
+
+
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+			     unsigned int freq)
+{
+	unsigned int i;
+
+	if (list == NULL)
+		return 0;
+
+	for (i = 0; i < list->num; i++) {
+		if (freq >= list->range[i].min && freq <= list->range[i].max)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+char * freq_range_list_str(const struct wpa_freq_range_list *list)
+{
+	char *buf, *pos, *end;
+	size_t maxlen;
+	unsigned int i;
+	int res;
+
+	if (list->num == 0)
+		return NULL;
+
+	maxlen = list->num * 30;
+	buf = os_malloc(maxlen);
+	if (buf == NULL)
+		return NULL;
+	pos = buf;
+	end = buf + maxlen;
+
+	for (i = 0; i < list->num; i++) {
+		struct wpa_freq_range *range = &list->range[i];
+
+		if (range->min == range->max)
+			res = os_snprintf(pos, end - pos, "%s%u",
+					  i == 0 ? "" : ",", range->min);
+		else
+			res = os_snprintf(pos, end - pos, "%s%u-%u",
+					  i == 0 ? "" : ",",
+					  range->min, range->max);
+		if (res < 0 || res > end - pos) {
+			os_free(buf);
+			return NULL;
+		}
+		pos += res;
+	}
+
+	return buf;
+}
+
+
+int int_array_len(const int *a)
+{
+	int i;
+	for (i = 0; a && a[i]; i++)
+		;
+	return i;
+}
+
+
+void int_array_concat(int **res, const int *a)
+{
+	int reslen, alen, i;
+	int *n;
+
+	reslen = int_array_len(*res);
+	alen = int_array_len(a);
+
+	n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
+	if (n == NULL) {
+		os_free(*res);
+		*res = NULL;
+		return;
+	}
+	for (i = 0; i <= alen; i++)
+		n[reslen + i] = a[i];
+	*res = n;
+}
+
+
+static int freq_cmp(const void *a, const void *b)
+{
+	int _a = *(int *) a;
+	int _b = *(int *) b;
+
+	if (_a == 0)
+		return 1;
+	if (_b == 0)
+		return -1;
+	return _a - _b;
+}
+
+
+void int_array_sort_unique(int *a)
+{
+	int alen;
+	int i, j;
+
+	if (a == NULL)
+		return;
+
+	alen = int_array_len(a);
+	qsort(a, alen, sizeof(int), freq_cmp);
+
+	i = 0;
+	j = 1;
+	while (a[i] && a[j]) {
+		if (a[i] == a[j]) {
+			j++;
+			continue;
+		}
+		a[++i] = a[j++];
+	}
+	if (a[i])
+		i++;
+	a[i] = 0;
+}
+
+
+void int_array_add_unique(int **res, int a)
+{
+	int reslen;
+	int *n;
+
+	for (reslen = 0; *res && (*res)[reslen]; reslen++) {
+		if ((*res)[reslen] == a)
+			return; /* already in the list */
+	}
+
+	n = os_realloc_array(*res, reslen + 2, sizeof(int));
+	if (n == NULL) {
+		os_free(*res);
+		*res = NULL;
+		return;
+	}
+
+	n[reslen] = a;
+	n[reslen + 1] = 0;
+
+	*res = n;
+}
+
+
+void str_clear_free(char *str)
+{
+	if (str) {
+		size_t len = os_strlen(str);
+		os_memset(str, 0, len);
+		os_free(str);
+	}
+}
+
+
+void bin_clear_free(void *bin, size_t len)
+{
+	if (bin) {
+		os_memset(bin, 0, len);
+		os_free(bin);
+	}
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 29f0b95..2bc8fe1 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -459,6 +459,14 @@
 #endif /* __GNUC__ */
 #endif /* __must_check */
 
+#ifndef __maybe_unused
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __maybe_unused __attribute__((unused))
+#else
+#define __maybe_unused
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
 int hwaddr_aton(const char *txt, u8 *addr);
 int hwaddr_compact_aton(const char *txt, u8 *addr);
 int hwaddr_aton2(const char *txt, u8 *addr);
@@ -485,6 +493,7 @@
 
 char * wpa_config_parse_string(const char *value, size_t *len);
 int is_hex(const u8 *data, size_t len);
+int find_first_bit(u32 value);
 size_t merge_byte_arrays(u8 *res, size_t res_len,
 			 const u8 *src1, size_t src1_len,
 			 const u8 *src2, size_t src2_len);
@@ -505,6 +514,31 @@
 #include "wpa_debug.h"
 
 
+struct wpa_freq_range_list {
+	struct wpa_freq_range {
+		unsigned int min;
+		unsigned int max;
+	} *range;
+	unsigned int num;
+};
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value);
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+			     unsigned int freq);
+char * freq_range_list_str(const struct wpa_freq_range_list *list);
+
+int int_array_len(const int *a);
+void int_array_concat(int **res, const int *a);
+void int_array_sort_unique(int *a);
+void int_array_add_unique(int **res, int a);
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+
+void str_clear_free(char *str);
+void bin_clear_free(void *bin, 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/edit.c b/src/utils/edit.c
index 177ecf4..d340bfa 100644
--- a/src/utils/edit.c
+++ b/src/utils/edit.c
@@ -14,7 +14,7 @@
 #include "list.h"
 #include "edit.h"
 
-#define CMD_BUF_LEN 256
+#define CMD_BUF_LEN 4096
 static char cmdbuf[CMD_BUF_LEN];
 static int cmdbuf_pos = 0;
 static int cmdbuf_len = 0;
diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c
index a095ea6..13173cb 100644
--- a/src/utils/edit_simple.c
+++ b/src/utils/edit_simple.c
@@ -13,7 +13,7 @@
 #include "edit.h"
 
 
-#define CMD_BUF_LEN 256
+#define CMD_BUF_LEN 4096
 static char cmdbuf[CMD_BUF_LEN];
 static int cmdbuf_pos = 0;
 static const char *ps2 = NULL;
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index f62e2b7..0da6de4 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -7,17 +7,28 @@
  */
 
 #include "includes.h"
+#include <assert.h>
 
 #include "common.h"
 #include "trace.h"
 #include "list.h"
 #include "eloop.h"
 
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
+#error Do not define both of poll and epoll
+#endif
+
+#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL)
+#define CONFIG_ELOOP_SELECT
+#endif
+
 #ifdef CONFIG_ELOOP_POLL
-#include <assert.h>
 #include <poll.h>
 #endif /* CONFIG_ELOOP_POLL */
 
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
 
 struct eloop_sock {
 	int sock;
@@ -31,7 +42,7 @@
 
 struct eloop_timeout {
 	struct dl_list list;
-	struct os_time time;
+	struct os_reltime time;
 	void *eloop_data;
 	void *user_data;
 	eloop_timeout_handler handler;
@@ -50,7 +61,11 @@
 struct eloop_sock_table {
 	int count;
 	struct eloop_sock *table;
+#ifdef CONFIG_ELOOP_EPOLL
+	eloop_event_type type;
+#else /* CONFIG_ELOOP_EPOLL */
 	int changed;
+#endif /* CONFIG_ELOOP_EPOLL */
 };
 
 struct eloop_data {
@@ -63,6 +78,13 @@
 	struct pollfd *pollfds;
 	struct pollfd **pollfds_map;
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+	int epollfd;
+	int epoll_max_event_num;
+	int epoll_max_fd;
+	struct eloop_sock *epoll_table;
+	struct epoll_event *epoll_events;
+#endif /* CONFIG_ELOOP_EPOLL */
 	struct eloop_sock_table readers;
 	struct eloop_sock_table writers;
 	struct eloop_sock_table exceptions;
@@ -75,7 +97,6 @@
 	int pending_terminate;
 
 	int terminate;
-	int reader_table_changed;
 };
 
 static struct eloop_data eloop;
@@ -128,6 +149,17 @@
 {
 	os_memset(&eloop, 0, sizeof(eloop));
 	dl_list_init(&eloop.timeout);
+#ifdef CONFIG_ELOOP_EPOLL
+	eloop.epollfd = epoll_create1(0);
+	if (eloop.epollfd < 0) {
+		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	eloop.readers.type = EVENT_TYPE_READ;
+	eloop.writers.type = EVENT_TYPE_WRITE;
+	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
+#endif /* CONFIG_ELOOP_EPOLL */
 #ifdef WPA_TRACE
 	signal(SIGSEGV, eloop_sigsegv_handler);
 #endif /* WPA_TRACE */
@@ -139,6 +171,11 @@
                                      int sock, eloop_sock_handler handler,
                                      void *eloop_data, void *user_data)
 {
+#ifdef CONFIG_ELOOP_EPOLL
+	struct eloop_sock *temp_table;
+	struct epoll_event ev, *temp_events;
+	int next;
+#endif /* CONFIG_ELOOP_EPOLL */
 	struct eloop_sock *tmp;
 	int new_max_sock;
 
@@ -174,6 +211,33 @@
 		eloop.pollfds = n;
 	}
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+	if (new_max_sock >= eloop.epoll_max_fd) {
+		next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2;
+		temp_table = os_realloc_array(eloop.epoll_table, next,
+					      sizeof(struct eloop_sock));
+		if (temp_table == NULL)
+			return -1;
+
+		eloop.epoll_max_fd = next;
+		eloop.epoll_table = temp_table;
+	}
+
+	if (eloop.count + 1 > eloop.epoll_max_event_num) {
+		next = eloop.epoll_max_event_num == 0 ? 8 :
+			eloop.epoll_max_event_num * 2;
+		temp_events = os_realloc_array(eloop.epoll_events, next,
+					       sizeof(struct epoll_event));
+		if (temp_events == NULL) {
+			wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. "
+				   "%s\n", __func__, strerror(errno));
+			return -1;
+		}
+
+		eloop.epoll_max_event_num = next;
+		eloop.epoll_events = temp_events;
+	}
+#endif /* CONFIG_ELOOP_EPOLL */
 
 	eloop_trace_sock_remove_ref(table);
 	tmp = os_realloc_array(table->table, table->count + 1,
@@ -190,9 +254,38 @@
 	table->table = tmp;
 	eloop.max_sock = new_max_sock;
 	eloop.count++;
+#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
 	eloop_trace_sock_add_ref(table);
 
+#ifdef CONFIG_ELOOP_EPOLL
+	os_memset(&ev, 0, sizeof(ev));
+	switch (table->type) {
+	case EVENT_TYPE_READ:
+		ev.events = EPOLLIN;
+		break;
+	case EVENT_TYPE_WRITE:
+		ev.events = EPOLLOUT;
+		break;
+	/*
+	 * Exceptions are always checked when using epoll, but I suppose it's
+	 * possible that someone registered a socket *only* for exception
+	 * handling.
+	 */
+	case EVENT_TYPE_EXCEPTION:
+		ev.events = EPOLLERR | EPOLLHUP;
+		break;
+	}
+	ev.data.fd = sock;
+	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
+		wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d "
+			   "failed. %s\n", __func__, sock, strerror(errno));
+		return -1;
+	}
+	os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
+		  sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
 	return 0;
 }
 
@@ -219,8 +312,18 @@
 	}
 	table->count--;
 	eloop.count--;
+#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
 	eloop_trace_sock_add_ref(table);
+#ifdef CONFIG_ELOOP_EPOLL
+	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
+		wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d "
+			   "failed. %s\n", __func__, sock, strerror(errno));
+		return;
+	}
+	os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
 }
 
 
@@ -362,7 +465,9 @@
 					max_pollfd_map, POLLERR | POLLHUP);
 }
 
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_SELECT
 
 static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
 				     fd_set *fds)
@@ -374,8 +479,10 @@
 	if (table->table == NULL)
 		return;
 
-	for (i = 0; i < table->count; i++)
+	for (i = 0; i < table->count; i++) {
+		assert(table->table[i].sock >= 0);
 		FD_SET(table->table[i].sock, fds);
+	}
 }
 
 
@@ -399,7 +506,24 @@
 	}
 }
 
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+
+
+#ifdef CONFIG_ELOOP_EPOLL
+static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
+{
+	struct eloop_sock *table;
+	int i;
+
+	for (i = 0; i < nfds; i++) {
+		table = &eloop.epoll_table[events[i].data.fd];
+		if (table->handler == NULL)
+			continue;
+		table->handler(table->sock, table->eloop_data,
+			       table->user_data);
+	}
+}
+#endif /* CONFIG_ELOOP_EPOLL */
 
 
 static void eloop_sock_table_destroy(struct eloop_sock_table *table)
@@ -459,6 +583,7 @@
 {
 	struct eloop_sock_table *table;
 
+	assert(sock >= 0);
 	table = eloop_get_sock_table(type);
 	return eloop_sock_table_add_sock(table, sock, handler,
 					 eloop_data, user_data);
@@ -484,7 +609,7 @@
 	timeout = os_zalloc(sizeof(*timeout));
 	if (timeout == NULL)
 		return -1;
-	if (os_get_time(&timeout->time) < 0) {
+	if (os_get_reltime(&timeout->time) < 0) {
 		os_free(timeout);
 		return -1;
 	}
@@ -514,7 +639,7 @@
 
 	/* Maintain timeouts in order of increasing time */
 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
-		if (os_time_before(&timeout->time, &tmp->time)) {
+		if (os_reltime_before(&timeout->time, &tmp->time)) {
 			dl_list_add(tmp->list.prev, &timeout->list);
 			return 0;
 		}
@@ -558,13 +683,13 @@
 
 int eloop_cancel_timeout_one(eloop_timeout_handler handler,
 			     void *eloop_data, void *user_data,
-			     struct os_time *remaining)
+			     struct os_reltime *remaining)
 {
 	struct eloop_timeout *timeout, *prev;
 	int removed = 0;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	remaining->sec = remaining->usec = 0;
 
 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
@@ -573,8 +698,8 @@
 		    (timeout->eloop_data == eloop_data) &&
 		    (timeout->user_data == user_data)) {
 			removed = 1;
-			if (os_time_before(&now, &timeout->time))
-				os_time_sub(&timeout->time, &now, remaining);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, remaining);
 			eloop_remove_timeout(timeout);
 			break;
 		}
@@ -599,6 +724,70 @@
 }
 
 
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+			  eloop_timeout_handler handler, void *eloop_data,
+			  void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&requested, &remaining)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+			    eloop_timeout_handler handler, void *eloop_data,
+			    void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&remaining, &requested)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
 #ifndef CONFIG_NATIVE_WINDOWS
 static void eloop_handle_alarm(int sig)
 {
@@ -709,20 +898,24 @@
 #ifdef CONFIG_ELOOP_POLL
 	int num_poll_fds;
 	int timeout_ms = 0;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
 	fd_set *rfds, *wfds, *efds;
 	struct timeval _tv;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+	int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
 	int res;
-	struct os_time tv, now;
+	struct os_reltime tv, now;
 
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
 	rfds = os_malloc(sizeof(*rfds));
 	wfds = os_malloc(sizeof(*wfds));
 	efds = os_malloc(sizeof(*efds));
 	if (rfds == NULL || wfds == NULL || efds == NULL)
 		goto out;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
 
 	while (!eloop.terminate &&
 	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
@@ -731,17 +924,18 @@
 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
 					list);
 		if (timeout) {
-			os_get_time(&now);
-			if (os_time_before(&now, &timeout->time))
-				os_time_sub(&timeout->time, &now, &tv);
+			os_get_reltime(&now);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, &tv);
 			else
 				tv.sec = tv.usec = 0;
-#ifdef CONFIG_ELOOP_POLL
+#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
 			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_SELECT
 			_tv.tv_sec = tv.sec;
 			_tv.tv_usec = tv.usec;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
 		}
 
 #ifdef CONFIG_ELOOP_POLL
@@ -751,30 +945,44 @@
 			eloop.max_pollfd_map);
 		res = poll(eloop.pollfds, num_poll_fds,
 			   timeout ? timeout_ms : -1);
-
-		if (res < 0 && errno != EINTR && errno != 0) {
-			perror("poll");
-			goto out;
-		}
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
 		eloop_sock_table_set_fds(&eloop.readers, rfds);
 		eloop_sock_table_set_fds(&eloop.writers, wfds);
 		eloop_sock_table_set_fds(&eloop.exceptions, efds);
 		res = select(eloop.max_sock + 1, rfds, wfds, efds,
 			     timeout ? &_tv : NULL);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+		if (eloop.count == 0) {
+			res = 0;
+		} else {
+			res = epoll_wait(eloop.epollfd, eloop.epoll_events,
+					 eloop.count, timeout_ms);
+		}
+#endif /* CONFIG_ELOOP_EPOLL */
 		if (res < 0 && errno != EINTR && errno != 0) {
-			perror("select");
+			wpa_printf(MSG_ERROR, "eloop: %s: %s",
+#ifdef CONFIG_ELOOP_POLL
+				   "poll"
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+				   "select"
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+				   "epoll"
+#endif /* CONFIG_ELOOP_EPOLL */
+				   , strerror(errno));
 			goto out;
 		}
-#endif /* CONFIG_ELOOP_POLL */
 		eloop_process_pending_signals();
 
 		/* check if some registered timeouts have occurred */
 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
 					list);
 		if (timeout) {
-			os_get_time(&now);
-			if (!os_time_before(&now, &timeout->time)) {
+			os_get_reltime(&now);
+			if (!os_reltime_before(&now, &timeout->time)) {
 				void *eloop_data = timeout->eloop_data;
 				void *user_data = timeout->user_data;
 				eloop_timeout_handler handler =
@@ -792,20 +1000,24 @@
 		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
 					  &eloop.exceptions, eloop.pollfds_map,
 					  eloop.max_pollfd_map);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
 		eloop_sock_table_dispatch(&eloop.readers, rfds);
 		eloop_sock_table_dispatch(&eloop.writers, wfds);
 		eloop_sock_table_dispatch(&eloop.exceptions, efds);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+		eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
 	}
 
 	eloop.terminate = 0;
 out:
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
 	os_free(rfds);
 	os_free(wfds);
 	os_free(efds);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
 	return;
 }
 
@@ -819,9 +1031,9 @@
 void eloop_destroy(void)
 {
 	struct eloop_timeout *timeout, *prev;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
 			      struct eloop_timeout, list) {
 		int sec, usec;
@@ -849,6 +1061,11 @@
 	os_free(eloop.pollfds);
 	os_free(eloop.pollfds_map);
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+	os_free(eloop.epoll_table);
+	os_free(eloop.epoll_events);
+	close(eloop.epollfd);
+#endif /* CONFIG_ELOOP_EPOLL */
 }
 
 
@@ -871,7 +1088,13 @@
 	pfd.events = POLLIN;
 
 	poll(&pfd, 1, -1);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
+	/*
+	 * We can use epoll() here. But epoll() requres 4 system calls.
+	 * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
+	 * epoll fd. So select() is better for performance here.
+	 */
 	fd_set rfds;
 
 	if (sock < 0)
@@ -880,5 +1103,9 @@
 	FD_ZERO(&rfds);
 	FD_SET(sock, &rfds);
 	select(sock + 1, &rfds, NULL, NULL, NULL);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
 }
+
+#ifdef CONFIG_ELOOP_SELECT
+#undef CONFIG_ELOOP_SELECT
+#endif /* CONFIG_ELOOP_SELECT */
diff --git a/src/utils/eloop.h b/src/utils/eloop.h
index 0037c63..07b8c0d 100644
--- a/src/utils/eloop.h
+++ b/src/utils/eloop.h
@@ -207,7 +207,7 @@
  */
 int eloop_cancel_timeout_one(eloop_timeout_handler handler,
 			     void *eloop_data, void *user_data,
-			     struct os_time *remaining);
+			     struct os_reltime *remaining);
 
 /**
  * eloop_is_timeout_registered - Check if a timeout is already registered
@@ -223,6 +223,40 @@
 				void *eloop_data, void *user_data);
 
 /**
+ * eloop_deplete_timeout - Deplete a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * deplete the timeout if remaining time is more than the requested time.
+ */
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+			  eloop_timeout_handler handler, void *eloop_data,
+			  void *user_data);
+
+/**
+ * eloop_replenish_timeout - Replenish a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * replenish the timeout if remaining time is less than the requested time.
+ */
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+			    eloop_timeout_handler handler, void *eloop_data,
+			    void *user_data);
+
+/**
  * eloop_register_signal - Register handler for signals
  * @sig: Signal number (e.g., SIGHUP)
  * @handler: Callback function to be called when the signal is received
diff --git a/src/utils/eloop_none.c b/src/utils/eloop_none.c
deleted file mode 100644
index cb5e922..0000000
--- a/src/utils/eloop_none.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Event loop - empty template (basic structure, but no OS specific operations)
- * Copyright (c) 2002-2009, 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 "list.h"
-#include "eloop.h"
-
-
-struct eloop_sock {
-	int sock;
-	void *eloop_data;
-	void *user_data;
-	eloop_sock_handler handler;
-};
-
-struct eloop_timeout {
-	struct dl_list list;
-	struct os_time time;
-	void *eloop_data;
-	void *user_data;
-	eloop_timeout_handler handler;
-};
-
-struct eloop_signal {
-	int sig;
-	void *user_data;
-	eloop_signal_handler handler;
-	int signaled;
-};
-
-struct eloop_data {
-	int max_sock, reader_count;
-	struct eloop_sock *readers;
-
-	struct dl_list timeout;
-
-	int signal_count;
-	struct eloop_signal *signals;
-	int signaled;
-	int pending_terminate;
-
-	int terminate;
-	int reader_table_changed;
-};
-
-static struct eloop_data eloop;
-
-
-int eloop_init(void)
-{
-	os_memset(&eloop, 0, sizeof(eloop));
-	dl_list_init(&eloop.timeout);
-	return 0;
-}
-
-
-int eloop_register_read_sock(int sock, eloop_sock_handler handler,
-			     void *eloop_data, void *user_data)
-{
-	struct eloop_sock *tmp;
-
-	tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
-			       sizeof(struct eloop_sock));
-	if (tmp == NULL)
-		return -1;
-
-	tmp[eloop.reader_count].sock = sock;
-	tmp[eloop.reader_count].eloop_data = eloop_data;
-	tmp[eloop.reader_count].user_data = user_data;
-	tmp[eloop.reader_count].handler = handler;
-	eloop.reader_count++;
-	eloop.readers = tmp;
-	if (sock > eloop.max_sock)
-		eloop.max_sock = sock;
-	eloop.reader_table_changed = 1;
-
-	return 0;
-}
-
-
-void eloop_unregister_read_sock(int sock)
-{
-	int i;
-
-	if (eloop.readers == NULL || eloop.reader_count == 0)
-		return;
-
-	for (i = 0; i < eloop.reader_count; i++) {
-		if (eloop.readers[i].sock == sock)
-			break;
-	}
-	if (i == eloop.reader_count)
-		return;
-	if (i != eloop.reader_count - 1) {
-		os_memmove(&eloop.readers[i], &eloop.readers[i + 1],
-			   (eloop.reader_count - i - 1) *
-			   sizeof(struct eloop_sock));
-	}
-	eloop.reader_count--;
-	eloop.reader_table_changed = 1;
-}
-
-
-int eloop_register_timeout(unsigned int secs, unsigned int usecs,
-			   eloop_timeout_handler handler,
-			   void *eloop_data, void *user_data)
-{
-	struct eloop_timeout *timeout, *tmp;
-	os_time_t now_sec;
-
-	timeout = os_zalloc(sizeof(*timeout));
-	if (timeout == NULL)
-		return -1;
-	if (os_get_time(&timeout->time) < 0) {
-		os_free(timeout);
-		return -1;
-	}
-	now_sec = timeout->time.sec;
-	timeout->time.sec += secs;
-	if (timeout->time.sec < now_sec) {
-		/*
-		 * Integer overflow - assume long enough timeout to be assumed
-		 * to be infinite, i.e., the timeout would never happen.
-		 */
-		wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
-			   "ever happen - ignore it", secs);
-		os_free(timeout);
-		return 0;
-	}
-	timeout->time.usec += usecs;
-	while (timeout->time.usec >= 1000000) {
-		timeout->time.sec++;
-		timeout->time.usec -= 1000000;
-	}
-	timeout->eloop_data = eloop_data;
-	timeout->user_data = user_data;
-	timeout->handler = handler;
-
-	/* Maintain timeouts in order of increasing time */
-	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
-		if (os_time_before(&timeout->time, &tmp->time)) {
-			dl_list_add(tmp->list.prev, &timeout->list);
-			return 0;
-		}
-	}
-	dl_list_add_tail(&eloop.timeout, &timeout->list);
-
-	return 0;
-}
-
-
-static void eloop_remove_timeout(struct eloop_timeout *timeout)
-{
-	dl_list_del(&timeout->list);
-	os_free(timeout);
-}
-
-
-int eloop_cancel_timeout(eloop_timeout_handler handler,
-			 void *eloop_data, void *user_data)
-{
-	struct eloop_timeout *timeout, *prev;
-	int removed = 0;
-
-	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
-			      struct eloop_timeout, list) {
-		if (timeout->handler == handler &&
-		    (timeout->eloop_data == eloop_data ||
-		     eloop_data == ELOOP_ALL_CTX) &&
-		    (timeout->user_data == user_data ||
-		     user_data == ELOOP_ALL_CTX)) {
-			eloop_remove_timeout(timeout);
-			removed++;
-		}
-	}
-
-	return removed;
-}
-
-
-int eloop_cancel_timeout_one(eloop_timeout_handler handler,
-			     void *eloop_data, void *user_data,
-			     struct os_time *remaining)
-{
-	struct eloop_timeout *timeout, *prev;
-	int removed = 0;
-	struct os_time now;
-
-	os_get_time(&now);
-	remaining->sec = remaining->usec = 0;
-
-	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
-			      struct eloop_timeout, list) {
-		if (timeout->handler == handler &&
-		    (timeout->eloop_data == eloop_data) &&
-		    (timeout->user_data == user_data)) {
-			removed = 1;
-			if (os_time_before(&now, &timeout->time))
-				os_time_sub(&timeout->time, &now, remaining);
-			eloop_remove_timeout(timeout);
-			break;
-		}
-	}
-	return removed;
-}
-
-
-int eloop_is_timeout_registered(eloop_timeout_handler handler,
-				void *eloop_data, void *user_data)
-{
-	struct eloop_timeout *tmp;
-
-	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
-		if (tmp->handler == handler &&
-		    tmp->eloop_data == eloop_data &&
-		    tmp->user_data == user_data)
-			return 1;
-	}
-
-	return 0;
-}
-
-
-/* TODO: replace with suitable signal handler */
-#if 0
-static void eloop_handle_signal(int sig)
-{
-	int i;
-
-	eloop.signaled++;
-	for (i = 0; i < eloop.signal_count; i++) {
-		if (eloop.signals[i].sig == sig) {
-			eloop.signals[i].signaled++;
-			break;
-		}
-	}
-}
-#endif
-
-
-static void eloop_process_pending_signals(void)
-{
-	int i;
-
-	if (eloop.signaled == 0)
-		return;
-	eloop.signaled = 0;
-
-	if (eloop.pending_terminate) {
-		eloop.pending_terminate = 0;
-	}
-
-	for (i = 0; i < eloop.signal_count; i++) {
-		if (eloop.signals[i].signaled) {
-			eloop.signals[i].signaled = 0;
-			eloop.signals[i].handler(eloop.signals[i].sig,
-						 eloop.signals[i].user_data);
-		}
-	}
-}
-
-
-int eloop_register_signal(int sig, eloop_signal_handler handler,
-			  void *user_data)
-{
-	struct eloop_signal *tmp;
-
-	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
-			       sizeof(struct eloop_signal));
-	if (tmp == NULL)
-		return -1;
-
-	tmp[eloop.signal_count].sig = sig;
-	tmp[eloop.signal_count].user_data = user_data;
-	tmp[eloop.signal_count].handler = handler;
-	tmp[eloop.signal_count].signaled = 0;
-	eloop.signal_count++;
-	eloop.signals = tmp;
-
-	/* TODO: register signal handler */
-
-	return 0;
-}
-
-
-int eloop_register_signal_terminate(eloop_signal_handler handler,
-				    void *user_data)
-{
-#if 0
-	/* TODO: for example */
-	int ret = eloop_register_signal(SIGINT, handler, user_data);
-	if (ret == 0)
-		ret = eloop_register_signal(SIGTERM, handler, user_data);
-	return ret;
-#endif
-	return 0;
-}
-
-
-int eloop_register_signal_reconfig(eloop_signal_handler handler,
-				   void *user_data)
-{
-#if 0
-	/* TODO: for example */
-	return eloop_register_signal(SIGHUP, handler, user_data);
-#endif
-	return 0;
-}
-
-
-void eloop_run(void)
-{
-	int i;
-	struct os_time tv, now;
-
-	while (!eloop.terminate &&
-		(!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0)) {
-		struct eloop_timeout *timeout;
-		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
-					list);
-		if (timeout) {
-			os_get_time(&now);
-			if (os_time_before(&now, &timeout->time))
-				os_time_sub(&timeout->time, &now, &tv);
-			else
-				tv.sec = tv.usec = 0;
-		}
-
-		/*
-		 * TODO: wait for any event (read socket ready, timeout (tv),
-		 * signal
-		 */
-		os_sleep(1, 0); /* just a dummy wait for testing */
-
-		eloop_process_pending_signals();
-
-		/* check if some registered timeouts have occurred */
-		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
-					list);
-		if (timeout) {
-			os_get_time(&now);
-			if (!os_time_before(&now, &timeout->time)) {
-				void *eloop_data = timeout->eloop_data;
-				void *user_data = timeout->user_data;
-				eloop_timeout_handler handler =
-					timeout->handler;
-				eloop_remove_timeout(timeout);
-				handler(eloop_data, user_data);
-			}
-
-		}
-
-		eloop.reader_table_changed = 0;
-		for (i = 0; i < eloop.reader_count; i++) {
-			/*
-			 * TODO: call each handler that has pending data to
-			 * read
-			 */
-			if (0 /* TODO: eloop.readers[i].sock ready */) {
-				eloop.readers[i].handler(
-					eloop.readers[i].sock,
-					eloop.readers[i].eloop_data,
-					eloop.readers[i].user_data);
-				if (eloop.reader_table_changed)
-					break;
-			}
-		}
-	}
-}
-
-
-void eloop_terminate(void)
-{
-	eloop.terminate = 1;
-}
-
-
-void eloop_destroy(void)
-{
-	struct eloop_timeout *timeout, *prev;
-
-	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
-			      struct eloop_timeout, list) {
-		eloop_remove_timeout(timeout);
-	}
-	os_free(eloop.readers);
-	os_free(eloop.signals);
-}
-
-
-int eloop_terminated(void)
-{
-	return eloop.terminate;
-}
-
-
-void eloop_wait_for_read_sock(int sock)
-{
-	/*
-	 * TODO: wait for the file descriptor to have something available for
-	 * reading
-	 */
-}
diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c
index eda412f..de47fb2 100644
--- a/src/utils/eloop_win.c
+++ b/src/utils/eloop_win.c
@@ -31,7 +31,7 @@
 
 struct eloop_timeout {
 	struct dl_list list;
-	struct os_time time;
+	struct os_reltime time;
 	void *eloop_data;
 	void *user_data;
 	eloop_timeout_handler handler;
@@ -244,7 +244,7 @@
 	timeout = os_zalloc(sizeof(*timeout));
 	if (timeout == NULL)
 		return -1;
-	if (os_get_time(&timeout->time) < 0) {
+	if (os_get_reltime(&timeout->time) < 0) {
 		os_free(timeout);
 		return -1;
 	}
@@ -271,7 +271,7 @@
 
 	/* Maintain timeouts in order of increasing time */
 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
-		if (os_time_before(&timeout->time, &tmp->time)) {
+		if (os_reltime_before(&timeout->time, &tmp->time)) {
 			dl_list_add(tmp->list.prev, &timeout->list);
 			return 0;
 		}
@@ -313,13 +313,13 @@
 
 int eloop_cancel_timeout_one(eloop_timeout_handler handler,
 			     void *eloop_data, void *user_data,
-			     struct os_time *remaining)
+			     struct os_reltime *remaining)
 {
 	struct eloop_timeout *timeout, *prev;
 	int removed = 0;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	remaining->sec = remaining->usec = 0;
 
 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
@@ -328,8 +328,8 @@
 		    (timeout->eloop_data == eloop_data) &&
 		    (timeout->user_data == user_data)) {
 			removed = 1;
-			if (os_time_before(&now, &timeout->time))
-				os_time_sub(&timeout->time, &now, remaining);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, remaining);
 			eloop_remove_timeout(timeout);
 			break;
 		}
@@ -354,6 +354,70 @@
 }
 
 
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+			  eloop_timeout_handler handler, void *eloop_data,
+			  void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&requested, &remaining)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+			    eloop_timeout_handler handler, void *eloop_data,
+			    void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&remaining, &requested)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
 /* TODO: replace with suitable signal handler */
 #if 0
 static void eloop_handle_signal(int sig)
@@ -468,7 +532,7 @@
 
 void eloop_run(void)
 {
-	struct os_time tv, now;
+	struct os_reltime tv, now;
 	DWORD count, ret, timeout_val, err;
 	size_t i;
 
@@ -480,9 +544,9 @@
 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
 					list);
 		if (timeout) {
-			os_get_time(&now);
-			if (os_time_before(&now, &timeout->time))
-				os_time_sub(&timeout->time, &now, &tv);
+			os_get_reltime(&now);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, &tv);
 		}
 
 		count = 0;
@@ -521,8 +585,8 @@
 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
 					list);
 		if (timeout) {
-			os_get_time(&now);
-			if (!os_time_before(&now, &timeout->time)) {
+			os_get_reltime(&now);
+			if (!os_reltime_before(&now, &timeout->time)) {
 				void *eloop_data = timeout->eloop_data;
 				void *user_data = timeout->user_data;
 				eloop_timeout_handler handler =
diff --git a/src/utils/ext_password_test.c b/src/utils/ext_password_test.c
index 3801bb8..b3a4552 100644
--- a/src/utils/ext_password_test.c
+++ b/src/utils/ext_password_test.c
@@ -36,7 +36,7 @@
 {
 	struct ext_password_test_data *data = ctx;
 
-	os_free(data->params);
+	str_clear_free(data->params);
 	os_free(data);
 }
 
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
new file mode 100644
index 0000000..8d4399a
--- /dev/null
+++ b/src/utils/http-utils.h
@@ -0,0 +1,63 @@
+/*
+ * HTTP wrapper
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HTTP_UTILS_H
+#define HTTP_UTILS_H
+
+struct http_ctx;
+
+struct http_othername {
+	char *oid;
+	u8 *data;
+	size_t len;
+};
+
+#define HTTP_MAX_CERT_LOGO_HASH 32
+
+struct http_logo {
+	char *alg_oid;
+	u8 *hash;
+	size_t hash_len;
+	char *uri;
+};
+
+struct http_cert {
+	char **dnsname;
+	unsigned int num_dnsname;
+	struct http_othername *othername;
+	unsigned int num_othername;
+	struct http_logo *logo;
+	unsigned int num_logo;
+};
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+		     const char *ca_fname, const char *username,
+		     const char *password, const char *client_cert,
+		     const char *client_key);
+int soap_reinit_client(struct http_ctx *ctx);
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
+void http_ocsp_set(struct http_ctx *ctx, int val);
+void http_deinit_ctx(struct http_ctx *ctx);
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+		       const char *fname, const char *ca_fname);
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+		 const char *content_type, const char *ext_hdr,
+		 const char *ca_fname,
+		 const char *username, const char *password,
+		 const char *client_cert, const char *client_key,
+		 size_t *resp_len);
+void http_set_cert_cb(struct http_ctx *ctx,
+		      int (*cb)(void *ctx, struct http_cert *cert),
+		      void *cb_ctx);
+const char * http_get_err(struct http_ctx *ctx);
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname);
+
+#endif /* HTTP_UTILS_H */
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
new file mode 100644
index 0000000..eb79b86
--- /dev/null
+++ b/src/utils/http_curl.c
@@ -0,0 +1,1641 @@
+/*
+ * HTTP wrapper for libcurl
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <curl/curl.h>
+#ifdef EAP_TLS_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+
+#ifdef SSL_set_tlsext_status_type
+#ifndef OPENSSL_NO_TLSEXT
+#define HAVE_OCSP
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#endif /* OPENSSL_NO_TLSEXT */
+#endif /* SSL_set_tlsext_status_type */
+#endif /* EAP_TLS_OPENSSL */
+
+#include "common.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+
+
+struct http_ctx {
+	void *ctx;
+	struct xml_node_ctx *xml;
+	CURL *curl;
+	struct curl_slist *curl_hdr;
+	char *svc_address;
+	char *svc_ca_fname;
+	char *svc_username;
+	char *svc_password;
+	char *svc_client_cert;
+	char *svc_client_key;
+	char *curl_buf;
+	size_t curl_buf_len;
+
+	int (*cert_cb)(void *ctx, struct http_cert *cert);
+	void *cert_cb_ctx;
+
+	enum {
+		NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP
+	} ocsp;
+	X509 *peer_cert;
+	X509 *peer_issuer;
+	X509 *peer_issuer_issuer;
+
+	const char *last_err;
+};
+
+
+static void clear_curl(struct http_ctx *ctx)
+{
+	if (ctx->curl) {
+		curl_easy_cleanup(ctx->curl);
+		ctx->curl = NULL;
+	}
+	if (ctx->curl_hdr) {
+		curl_slist_free_all(ctx->curl_hdr);
+		ctx->curl_hdr = NULL;
+	}
+}
+
+
+static void clone_str(char **dst, const char *src)
+{
+	os_free(*dst);
+	if (src)
+		*dst = os_strdup(src);
+	else
+		*dst = NULL;
+}
+
+
+static void debug_dump(struct http_ctx *ctx, const char *title,
+		       const char *buf, size_t len)
+{
+	char *txt;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (buf[i] < 32 && buf[i] != '\t' && buf[i] != '\n' &&
+		    buf[i] != '\r') {
+			wpa_hexdump_ascii(MSG_MSGDUMP, title, buf, len);
+			return;
+		}
+	}
+
+	txt = os_malloc(len + 1);
+	if (txt == NULL)
+		return;
+	os_memcpy(txt, buf, len);
+	txt[len] = '\0';
+	while (len > 0) {
+		len--;
+		if (txt[len] == '\n' || txt[len] == '\r')
+			txt[len] = '\0';
+		else
+			break;
+	}
+	wpa_printf(MSG_MSGDUMP, "%s[%s]", title, txt);
+	os_free(txt);
+}
+
+
+static int curl_cb_debug(CURL *curl, curl_infotype info, char *buf, size_t len,
+			 void *userdata)
+{
+	struct http_ctx *ctx = userdata;
+	switch (info) {
+	case CURLINFO_TEXT:
+		debug_dump(ctx, "CURLINFO_TEXT", buf, len);
+		break;
+	case CURLINFO_HEADER_IN:
+		debug_dump(ctx, "CURLINFO_HEADER_IN", buf, len);
+		break;
+	case CURLINFO_HEADER_OUT:
+		debug_dump(ctx, "CURLINFO_HEADER_OUT", buf, len);
+		break;
+	case CURLINFO_DATA_IN:
+		debug_dump(ctx, "CURLINFO_DATA_IN", buf, len);
+		break;
+	case CURLINFO_DATA_OUT:
+		debug_dump(ctx, "CURLINFO_DATA_OUT", buf, len);
+		break;
+	case CURLINFO_SSL_DATA_IN:
+		wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_IN - %d",
+			   (int) len);
+		break;
+	case CURLINFO_SSL_DATA_OUT:
+		wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_OUT - %d",
+			   (int) len);
+		break;
+	case CURLINFO_END:
+		wpa_printf(MSG_DEBUG, "debug - CURLINFO_END - %d",
+			   (int) len);
+		break;
+	}
+	return 0;
+}
+
+
+static size_t curl_cb_write(void *ptr, size_t size, size_t nmemb,
+			    void *userdata)
+{
+	struct http_ctx *ctx = userdata;
+	char *n;
+	n = os_realloc(ctx->curl_buf, ctx->curl_buf_len + size * nmemb + 1);
+	if (n == NULL)
+		return 0;
+	ctx->curl_buf = n;
+	os_memcpy(n + ctx->curl_buf_len, ptr, size * nmemb);
+	n[ctx->curl_buf_len + size * nmemb] = '\0';
+	ctx->curl_buf_len += size * nmemb;
+	return size * nmemb;
+}
+
+
+#ifdef EAP_TLS_OPENSSL
+
+static void debug_dump_cert(const char *title, X509 *cert)
+{
+	BIO *out;
+	char *txt;
+	size_t rlen;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		int res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_MSGDUMP, "%s:\n%s", title, txt);
+		}
+		os_free(txt);
+	}
+	BIO_free(out);
+}
+
+
+static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert,
+				   OTHERNAME *o)
+{
+	char txt[100];
+	int res;
+	struct http_othername *on;
+	ASN1_TYPE *val;
+
+	on = os_realloc_array(cert->othername, cert->num_othername + 1,
+			      sizeof(struct http_othername));
+	if (on == NULL)
+		return;
+	cert->othername = on;
+	on = &on[cert->num_othername];
+	os_memset(on, 0, sizeof(*on));
+
+	res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1);
+	if (res < 0 || res >= (int) sizeof(txt))
+		return;
+
+	on->oid = os_strdup(txt);
+	if (on->oid == NULL)
+		return;
+
+	val = o->value;
+	on->data = val->value.octet_string->data;
+	on->len = val->value.octet_string->length;
+
+	cert->num_othername++;
+}
+
+
+static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert,
+			     ASN1_STRING *name)
+{
+	char *buf;
+	char **n;
+
+	buf = NULL;
+	if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0)
+		return;
+
+	n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1,
+			     sizeof(char *));
+	if (n == NULL)
+		return;
+
+	cert->dnsname = n;
+	n[cert->num_dnsname] = buf;
+	cert->num_dnsname++;
+}
+
+
+static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert,
+			 const GENERAL_NAME *name)
+{
+	switch (name->type) {
+	case GEN_OTHERNAME:
+		add_alt_name_othername(ctx, cert, name->d.otherName);
+		break;
+	case GEN_DNS:
+		add_alt_name_dns(ctx, cert, name->d.dNSName);
+		break;
+	}
+}
+
+
+static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert,
+			  GENERAL_NAMES *names)
+{
+	int num, i;
+
+	num = sk_GENERAL_NAME_num(names);
+	for (i = 0; i < num; i++) {
+		const GENERAL_NAME *name;
+		name = sk_GENERAL_NAME_value(names, i);
+		add_alt_name(ctx, cert, name);
+	}
+}
+
+
+/* RFC 3709 */
+
+typedef struct {
+	X509_ALGOR *hashAlg;
+	ASN1_OCTET_STRING *hashValue;
+} HashAlgAndValue;
+
+typedef struct {
+	STACK_OF(HashAlgAndValue) *refStructHash;
+	STACK_OF(ASN1_IA5STRING) *refStructURI;
+} LogotypeReference;
+
+typedef struct {
+	ASN1_IA5STRING *mediaType;
+	STACK_OF(HashAlgAndValue) *logotypeHash;
+	STACK_OF(ASN1_IA5STRING) *logotypeURI;
+} LogotypeDetails;
+
+typedef struct {
+	int type;
+	union {
+		ASN1_INTEGER *numBits;
+		ASN1_INTEGER *tableSize;
+	} d;
+} LogotypeImageResolution;
+
+typedef struct {
+	ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */
+	ASN1_INTEGER *fileSize;
+	ASN1_INTEGER *xSize;
+	ASN1_INTEGER *ySize;
+	LogotypeImageResolution *resolution;
+	ASN1_IA5STRING *language;
+} LogotypeImageInfo;
+
+typedef struct {
+	LogotypeDetails *imageDetails;
+	LogotypeImageInfo *imageInfo;
+} LogotypeImage;
+
+typedef struct {
+	ASN1_INTEGER *fileSize;
+	ASN1_INTEGER *playTime;
+	ASN1_INTEGER *channels;
+	ASN1_INTEGER *sampleRate;
+	ASN1_IA5STRING *language;
+} LogotypeAudioInfo;
+
+typedef struct {
+	LogotypeDetails *audioDetails;
+	LogotypeAudioInfo *audioInfo;
+} LogotypeAudio;
+
+typedef struct {
+	STACK_OF(LogotypeImage) *image;
+	STACK_OF(LogotypeAudio) *audio;
+} LogotypeData;
+
+typedef struct {
+	int type;
+	union {
+		LogotypeData *direct;
+		LogotypeReference *indirect;
+	} d;
+} LogotypeInfo;
+
+typedef struct {
+	ASN1_OBJECT *logotypeType;
+	LogotypeInfo *info;
+} OtherLogotypeInfo;
+
+typedef struct {
+	STACK_OF(LogotypeInfo) *communityLogos;
+	LogotypeInfo *issuerLogo;
+	LogotypeInfo *subjectLogo;
+	STACK_OF(OtherLogotypeInfo) *otherLogos;
+} LogotypeExtn;
+
+ASN1_SEQUENCE(HashAlgAndValue) = {
+	ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR),
+	ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(HashAlgAndValue);
+
+ASN1_SEQUENCE(LogotypeReference) = {
+	ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue),
+	ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeReference);
+
+ASN1_SEQUENCE(LogotypeDetails) = {
+	ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING),
+	ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue),
+	ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeDetails);
+
+ASN1_CHOICE(LogotypeImageResolution) = {
+	ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1),
+	ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2)
+} ASN1_CHOICE_END(LogotypeImageResolution);
+
+ASN1_SEQUENCE(LogotypeImageInfo) = {
+	ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0),
+	ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER),
+	ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution),
+	ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4),
+} ASN1_SEQUENCE_END(LogotypeImageInfo);
+
+ASN1_SEQUENCE(LogotypeImage) = {
+	ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails),
+	ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo)
+} ASN1_SEQUENCE_END(LogotypeImage);
+
+ASN1_SEQUENCE(LogotypeAudioInfo) = {
+	ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER),
+	ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3),
+	ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4)
+} ASN1_SEQUENCE_END(LogotypeAudioInfo);
+
+ASN1_SEQUENCE(LogotypeAudio) = {
+	ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails),
+	ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo)
+} ASN1_SEQUENCE_END(LogotypeAudio);
+
+ASN1_SEQUENCE(LogotypeData) = {
+	ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage),
+	ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1)
+} ASN1_SEQUENCE_END(LogotypeData);
+
+ASN1_CHOICE(LogotypeInfo) = {
+	ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0),
+	ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1)
+} ASN1_CHOICE_END(LogotypeInfo);
+
+ASN1_SEQUENCE(OtherLogotypeInfo) = {
+	ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT),
+	ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo)
+} ASN1_SEQUENCE_END(OtherLogotypeInfo);
+
+ASN1_SEQUENCE(LogotypeExtn) = {
+	ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0),
+	ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1),
+	ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2),
+	ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3)
+} ASN1_SEQUENCE_END(LogotypeExtn);
+
+IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
+
+#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
+#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
+#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
+#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i))
+#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st))
+#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i))
+#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st))
+#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
+#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
+#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+
+
+static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
+		     HashAlgAndValue *hash, ASN1_IA5STRING *uri)
+{
+	char txt[100];
+	int res, len;
+	struct http_logo *n;
+
+	if (hash == NULL || uri == NULL)
+		return;
+
+	res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1);
+	if (res < 0 || res >= (int) sizeof(txt))
+		return;
+
+	n = os_realloc_array(hcert->logo, hcert->num_logo + 1,
+			     sizeof(struct http_logo));
+	if (n == NULL)
+		return;
+	hcert->logo = n;
+	n = &hcert->logo[hcert->num_logo];
+	os_memset(n, 0, sizeof(*n));
+
+	n->alg_oid = os_strdup(txt);
+	if (n->alg_oid == NULL)
+		return;
+
+	n->hash_len = ASN1_STRING_length(hash->hashValue);
+	n->hash = os_malloc(n->hash_len);
+	if (n->hash == NULL) {
+		os_free(n->alg_oid);
+		return;
+	}
+	os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len);
+
+	len = ASN1_STRING_length(uri);
+	n->uri = os_malloc(len + 1);
+	if (n->uri == NULL) {
+		os_free(n->alg_oid);
+		os_free(n->hash);
+		return;
+	}
+	os_memcpy(n->uri, ASN1_STRING_data(uri), len);
+	n->uri[len] = '\0';
+
+	hcert->num_logo++;
+}
+
+
+static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert,
+			    LogotypeData *data)
+{
+	int i, num;
+
+	if (data->image == NULL)
+		return;
+
+	num = sk_LogotypeImage_num(data->image);
+	for (i = 0; i < num; i++) {
+		LogotypeImage *image;
+		LogotypeDetails *details;
+		int j, hash_num, uri_num;
+		HashAlgAndValue *found_hash = NULL;
+
+		image = sk_LogotypeImage_value(data->image, i);
+		if (image == NULL)
+			continue;
+
+		details = image->imageDetails;
+		if (details == NULL)
+			continue;
+
+		hash_num = sk_HashAlgAndValue_num(details->logotypeHash);
+		for (j = 0; j < hash_num; j++) {
+			HashAlgAndValue *hash;
+			char txt[100];
+			int res;
+			hash = sk_HashAlgAndValue_value(details->logotypeHash,
+							j);
+			if (hash == NULL)
+				continue;
+			res = OBJ_obj2txt(txt, sizeof(txt),
+					  hash->hashAlg->algorithm, 1);
+			if (res < 0 || res >= (int) sizeof(txt))
+				continue;
+			if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) {
+				found_hash = hash;
+				break;
+			}
+		}
+
+		if (!found_hash) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo");
+			continue;
+		}
+
+		uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI);
+		for (j = 0; j < uri_num; j++) {
+			ASN1_IA5STRING *uri;
+			uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j);
+			add_logo(ctx, hcert, found_hash, uri);
+		}
+	}
+}
+
+
+static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert,
+			      LogotypeReference *ref)
+{
+	int j, hash_num, uri_num;
+
+	hash_num = sk_HashAlgAndValue_num(ref->refStructHash);
+	uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI);
+	if (hash_num != uri_num) {
+		wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d",
+			   hash_num, uri_num);
+		return;
+	}
+
+	for (j = 0; j < hash_num; j++) {
+		HashAlgAndValue *hash;
+		ASN1_IA5STRING *uri;
+		hash = sk_HashAlgAndValue_value(ref->refStructHash, j);
+		uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j);
+		add_logo(ctx, hcert, hash, uri);
+	}
+}
+
+
+static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent)
+{
+	int i;
+	const unsigned char *data;
+
+	BIO_printf(out, "%*shashAlg: ", indent, "");
+	i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm);
+	BIO_printf(out, "\n");
+
+	BIO_printf(out, "%*shashValue: ", indent, "");
+	data = hash->hashValue->data;
+	for (i = 0; i < hash->hashValue->length; i++)
+		BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]);
+	BIO_printf(out, "\n");
+}
+
+static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent)
+{
+	int i, num;
+
+	BIO_printf(out, "%*sLogotypeDetails\n", indent, "");
+	if (details->mediaType) {
+		BIO_printf(out, "%*smediaType: ", indent, "");
+		ASN1_STRING_print(out, details->mediaType);
+		BIO_printf(out, "\n");
+	}
+
+	num = details->logotypeHash ?
+		sk_HashAlgAndValue_num(details->logotypeHash) : 0;
+	for (i = 0; i < num; i++) {
+		HashAlgAndValue *hash;
+		hash = sk_HashAlgAndValue_value(details->logotypeHash, i);
+		i2r_HashAlgAndValue(hash, out, indent);
+	}
+
+	num = details->logotypeURI ?
+		sk_ASN1_IA5STRING_num(details->logotypeURI) : 0;
+	for (i = 0; i < num; i++) {
+		ASN1_IA5STRING *uri;
+		uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i);
+		BIO_printf(out, "%*slogotypeURI: ", indent, "");
+		ASN1_STRING_print(out, uri);
+		BIO_printf(out, "\n");
+	}
+}
+
+static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent)
+{
+	long val;
+
+	BIO_printf(out, "%*sLogotypeImageInfo\n", indent, "");
+	if (info->type) {
+		val = ASN1_INTEGER_get(info->type);
+		BIO_printf(out, "%*stype: %ld\n", indent, "", val);
+	} else {
+		BIO_printf(out, "%*stype: default (1)\n", indent, "");
+	}
+	val = ASN1_INTEGER_get(info->xSize);
+	BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
+	val = ASN1_INTEGER_get(info->ySize);
+	BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
+	if (info->resolution) {
+		BIO_printf(out, "%*sresolution\n", indent, "");
+		/* TODO */
+	}
+	if (info->language) {
+		BIO_printf(out, "%*slanguage: ", indent, "");
+		ASN1_STRING_print(out, info->language);
+		BIO_printf(out, "\n");
+	}
+}
+
+static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent)
+{
+	BIO_printf(out, "%*sLogotypeImage\n", indent, "");
+	if (image->imageDetails) {
+		i2r_LogotypeDetails(image->imageDetails, out, indent + 4);
+	}
+	if (image->imageInfo) {
+		i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4);
+	}
+}
+
+static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out,
+			     int indent)
+{
+	int i, num;
+
+	BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title);
+
+	num = data->image ? sk_LogotypeImage_num(data->image) : 0;
+	for (i = 0; i < num; i++) {
+		LogotypeImage *image = sk_LogotypeImage_value(data->image, i);
+		i2r_LogotypeImage(image, out, indent + 4);
+	}
+
+	num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0;
+	for (i = 0; i < num; i++) {
+		BIO_printf(out, "%*saudio: TODO\n", indent, "");
+	}
+}
+
+static void i2r_LogotypeReference(LogotypeReference *ref, const char *title,
+				  BIO *out, int indent)
+{
+	int i, hash_num, uri_num;
+
+	BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title);
+
+	hash_num = ref->refStructHash ?
+		sk_HashAlgAndValue_num(ref->refStructHash) : 0;
+	uri_num = ref->refStructURI ?
+		sk_ASN1_IA5STRING_num(ref->refStructURI) : 0;
+	if (hash_num != uri_num) {
+		BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n",
+			   indent, "", hash_num, uri_num);
+		return;
+	}
+
+	for (i = 0; i < hash_num; i++) {
+		HashAlgAndValue *hash;
+		ASN1_IA5STRING *uri;
+
+		hash = sk_HashAlgAndValue_value(ref->refStructHash, i);
+		i2r_HashAlgAndValue(hash, out, indent);
+
+		uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i);
+		BIO_printf(out, "%*srefStructURI: ", indent, "");
+		ASN1_STRING_print(out, uri);
+		BIO_printf(out, "\n");
+	}
+}
+
+static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out,
+			     int indent)
+{
+	switch (info->type) {
+	case 0:
+		i2r_LogotypeData(info->d.direct, title, out, indent);
+		break;
+	case 1:
+		i2r_LogotypeReference(info->d.indirect, title, out, indent);
+		break;
+	}
+}
+
+static void debug_print_logotypeext(LogotypeExtn *logo)
+{
+	BIO *out;
+	int i, num;
+	int indent = 0;
+
+	out = BIO_new_fp(stdout, BIO_NOCLOSE);
+	if (out == NULL)
+		return;
+
+	if (logo->communityLogos) {
+		num = sk_LogotypeInfo_num(logo->communityLogos);
+		for (i = 0; i < num; i++) {
+			LogotypeInfo *info;
+			info = sk_LogotypeInfo_value(logo->communityLogos, i);
+			i2r_LogotypeInfo(info, "communityLogo", out, indent);
+		}
+	}
+
+	if (logo->issuerLogo) {
+		i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent );
+	}
+
+	if (logo->subjectLogo) {
+		i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent);
+	}
+
+	if (logo->otherLogos) {
+		BIO_printf(out, "%*sotherLogos - TODO\n", indent, "");
+	}
+
+	BIO_free(out);
+}
+
+
+static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
+			     X509 *cert)
+{
+	ASN1_OBJECT *obj;
+	int pos;
+	X509_EXTENSION *ext;
+	ASN1_OCTET_STRING *os;
+	LogotypeExtn *logo;
+	const unsigned char *data;
+	int i, num;
+
+	obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0);
+	if (obj == NULL)
+		return;
+
+	pos = X509_get_ext_by_OBJ(cert, obj, -1);
+	if (pos < 0) {
+		wpa_printf(MSG_INFO, "No logotype extension included");
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "Parsing logotype extension");
+	ext = X509_get_ext(cert, pos);
+	if (!ext) {
+		wpa_printf(MSG_INFO, "Could not get logotype extension");
+		return;
+	}
+
+	os = X509_EXTENSION_get_data(ext);
+	if (os == NULL) {
+		wpa_printf(MSG_INFO, "Could not get logotype extension data");
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "logotypeExtn",
+		    ASN1_STRING_data(os), ASN1_STRING_length(os));
+
+	data = ASN1_STRING_data(os);
+	logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os));
+	if (logo == NULL) {
+		wpa_printf(MSG_INFO, "Failed to parse logotypeExtn");
+		return;
+	}
+
+	if (wpa_debug_level < MSG_INFO)
+		debug_print_logotypeext(logo);
+
+	if (!logo->communityLogos) {
+		wpa_printf(MSG_INFO, "No communityLogos included");
+		LogotypeExtn_free(logo);
+		return;
+	}
+
+	num = sk_LogotypeInfo_num(logo->communityLogos);
+	for (i = 0; i < num; i++) {
+		LogotypeInfo *info;
+		info = sk_LogotypeInfo_value(logo->communityLogos, i);
+		switch (info->type) {
+		case 0:
+			add_logo_direct(ctx, hcert, info->d.direct);
+			break;
+		case 1:
+			add_logo_indirect(ctx, hcert, info->d.indirect);
+			break;
+		}
+	}
+
+	LogotypeExtn_free(logo);
+}
+
+
+static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert,
+		       X509 *cert, GENERAL_NAMES **names)
+{
+	os_memset(hcert, 0, sizeof(*hcert));
+
+	*names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+	if (*names)
+		add_alt_names(ctx, hcert, *names);
+
+	add_logotype_ext(ctx, hcert, cert);
+}
+
+
+static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names)
+{
+	unsigned int i;
+
+	for (i = 0; i < hcert->num_dnsname; i++)
+		OPENSSL_free(hcert->dnsname[i]);
+	os_free(hcert->dnsname);
+
+	for (i = 0; i < hcert->num_othername; i++)
+		os_free(hcert->othername[i].oid);
+	os_free(hcert->othername);
+
+	for (i = 0; i < hcert->num_logo; i++) {
+		os_free(hcert->logo[i].alg_oid);
+		os_free(hcert->logo[i].hash);
+		os_free(hcert->logo[i].uri);
+	}
+	os_free(hcert->logo);
+
+	sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+}
+
+
+static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
+{
+	GENERAL_NAMES *names;
+	struct http_cert hcert;
+	int ret;
+
+	if (ctx->cert_cb == NULL)
+		return 0;
+
+	if (0) {
+		BIO *out;
+		out = BIO_new_fp(stdout, BIO_NOCLOSE);
+		X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+		BIO_free(out);
+	}
+
+	parse_cert(ctx, &hcert, cert, &names);
+	ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
+	parse_cert_free(&hcert, names);
+
+	return ret;
+}
+
+
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
+{
+	BIO *in, *out;
+	X509 *cert;
+	GENERAL_NAMES *names;
+	struct http_cert hcert;
+	unsigned int i;
+
+	in = BIO_new_file(fname, "r");
+	if (in == NULL) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
+		return;
+	}
+
+	cert = d2i_X509_bio(in, NULL);
+	BIO_free(in);
+
+	if (cert == NULL) {
+		wpa_printf(MSG_ERROR, "Could not parse certificate");
+		return;
+	}
+
+	out = BIO_new_fp(stdout, BIO_NOCLOSE);
+	if (out) {
+		X509_print_ex(out, cert, XN_FLAG_COMPAT,
+			      X509_FLAG_COMPAT);
+		BIO_free(out);
+	}
+
+	wpa_printf(MSG_INFO, "Additional parsing information:");
+	parse_cert(ctx, &hcert, cert, &names);
+	for (i = 0; i < hcert.num_othername; i++) {
+		if (os_strcmp(hcert.othername[i].oid,
+			      "1.3.6.1.4.1.40808.1.1.1") == 0) {
+			char *name = os_zalloc(hcert.othername[i].len + 1);
+			if (name) {
+				os_memcpy(name, hcert.othername[i].data,
+					  hcert.othername[i].len);
+				wpa_printf(MSG_INFO,
+					   "id-wfa-hotspot-friendlyName: %s",
+					   name);
+				os_free(name);
+			}
+			wpa_hexdump_ascii(MSG_INFO,
+					  "id-wfa-hotspot-friendlyName",
+					  hcert.othername[i].data,
+					  hcert.othername[i].len);
+		} else {
+			wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
+				   hcert.othername[i].oid);
+			wpa_hexdump_ascii(MSG_INFO, "unknown othername",
+					  hcert.othername[i].data,
+					  hcert.othername[i].len);
+		}
+	}
+	parse_cert_free(&hcert, names);
+
+	X509_free(cert);
+}
+
+
+static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+	struct http_ctx *ctx;
+	X509 *cert;
+	int err, depth;
+	char buf[256];
+	X509_NAME *name;
+	const char *err_str;
+	SSL *ssl;
+	SSL_CTX *ssl_ctx;
+
+	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+					 SSL_get_ex_data_X509_STORE_CTX_idx());
+	ssl_ctx = ssl->ctx;
+	ctx = SSL_CTX_get_app_data(ssl_ctx);
+
+	wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify");
+
+	err = X509_STORE_CTX_get_error(x509_ctx);
+	err_str = X509_verify_cert_error_string(err);
+	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+	cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+	if (!cert) {
+		wpa_printf(MSG_INFO, "No server certificate available");
+		ctx->last_err = "No server certificate available";
+		return 0;
+	}
+
+	if (depth == 0)
+		ctx->peer_cert = cert;
+	else if (depth == 1)
+		ctx->peer_issuer = cert;
+	else if (depth == 2)
+		ctx->peer_issuer_issuer = cert;
+
+	name = X509_get_subject_name(cert);
+	X509_NAME_oneline(name, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "Server certificate chain - depth=%d err=%d (%s) subject=%s",
+		   depth, err, err_str, buf);
+	debug_dump_cert("Server certificate chain - certificate", cert);
+
+	if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
+		return 0;
+
+	if (!preverify_ok)
+		ctx->last_err = "TLS validation failed";
+
+	return preverify_ok;
+}
+
+
+#ifdef HAVE_OCSP
+
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
+{
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	OCSP_RESPONSE_print(out, rsp, 0);
+	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_MSGDUMP, "OpenSSL: OCSP Response\n%s", txt);
+	}
+	os_free(txt);
+	BIO_free(out);
+}
+
+
+static void tls_show_errors(const char *func, const char *txt)
+{
+	unsigned long err;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - %s %s",
+		   func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: pending error: %s",
+			   ERR_error_string(err, NULL));
+	}
+}
+
+
+static int ocsp_resp_cb(SSL *s, void *arg)
+{
+	struct http_ctx *ctx = arg;
+	const unsigned char *p;
+	int len, status, reason;
+	OCSP_RESPONSE *rsp;
+	OCSP_BASICRESP *basic;
+	OCSP_CERTID *id;
+	ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
+	X509_STORE *store;
+	STACK_OF(X509) *certs = NULL;
+
+	len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+	if (!p) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+		if (ctx->ocsp == MANDATORY_OCSP)
+			ctx->last_err = "No OCSP response received";
+		return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
+
+	rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+	if (!rsp) {
+		wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
+		ctx->last_err = "Failed to parse OCSP response";
+		return 0;
+	}
+
+	ocsp_debug_print_resp(rsp);
+
+	status = OCSP_response_status(rsp);
+	if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+		wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
+			   status, OCSP_response_status_str(status));
+		ctx->last_err = "OCSP responder error";
+		return 0;
+	}
+
+	basic = OCSP_response_get1_basic(rsp);
+	if (!basic) {
+		wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
+		ctx->last_err = "Could not find BasicOCSPResponse";
+		return 0;
+	}
+
+	store = SSL_CTX_get_cert_store(s->ctx);
+	if (ctx->peer_issuer) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer");
+		debug_dump_cert("OpenSSL: Issuer certificate",
+				ctx->peer_issuer);
+
+		if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) {
+			tls_show_errors(__func__,
+					"OpenSSL: Could not add issuer to certificate store\n");
+		}
+		certs = sk_X509_new_null();
+		if (certs) {
+			X509 *cert;
+			cert = X509_dup(ctx->peer_issuer);
+			if (cert && !sk_X509_push(certs, cert)) {
+				tls_show_errors(
+					__func__,
+					"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+				X509_free(cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (ctx->peer_issuer_issuer) {
+				X509 *cert;
+				cert = X509_dup(ctx->peer_issuer_issuer);
+				if (cert && !sk_X509_push(certs, cert)) {
+					tls_show_errors(
+						__func__,
+						"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+					X509_free(cert);
+				}
+			}
+		}
+	}
+
+	status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
+	sk_X509_pop_free(certs, X509_free);
+	if (status <= 0) {
+		tls_show_errors(__func__,
+				"OpenSSL: OCSP response failed verification");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "OCSP response failed verification";
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
+
+	if (!ctx->peer_cert) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "Peer certificate not available for OCSP status check";
+		return 0;
+	}
+
+	if (!ctx->peer_issuer) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "Peer issuer certificate not available for OCSP status check";
+		return 0;
+	}
+
+	id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+	if (!id) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "Could not create OCSP certificate identifier";
+		return 0;
+	}
+
+	if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+				   &this_update, &next_update)) {
+		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
+			   (ctx->ocsp == MANDATORY_OCSP) ? "" :
+			   " (OCSP not required)");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		if (ctx->ocsp == MANDATORY_OCSP)
+
+			ctx->last_err = "Could not find current server certificate from OCSP response";
+		return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+	}
+
+	if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+		tls_show_errors(__func__, "OpenSSL: OCSP status times invalid");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "OCSP status times invalid";
+		return 0;
+	}
+
+	OCSP_BASICRESP_free(basic);
+	OCSP_RESPONSE_free(rsp);
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
+		   OCSP_cert_status_str(status));
+
+	if (status == V_OCSP_CERTSTATUS_GOOD)
+		return 1;
+	if (status == V_OCSP_CERTSTATUS_REVOKED)
+		ctx->last_err = "Server certificate has been revoked";
+		return 0;
+	if (ctx->ocsp == MANDATORY_OCSP) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
+		ctx->last_err = "OCSP status unknown";
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+	return 1;
+}
+
+
+static SSL_METHOD patch_ssl_method;
+static const SSL_METHOD *real_ssl_method;
+
+static int curl_patch_ssl_new(SSL *s)
+{
+	SSL_CTX *ssl = s->ctx;
+	int ret;
+
+	ssl->method = real_ssl_method;
+	s->method = real_ssl_method;
+
+	ret = s->method->ssl_new(s);
+	SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp);
+
+	return ret;
+}
+
+#endif /* HAVE_OCSP */
+
+
+static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
+{
+	struct http_ctx *ctx = parm;
+	SSL_CTX *ssl = sslctx;
+
+	wpa_printf(MSG_DEBUG, "curl_cb_ssl");
+	SSL_CTX_set_app_data(ssl, ctx);
+	SSL_CTX_set_verify(ssl, SSL_VERIFY_PEER, curl_cb_ssl_verify);
+
+#ifdef HAVE_OCSP
+	if (ctx->ocsp != NO_OCSP) {
+		SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb);
+		SSL_CTX_set_tlsext_status_arg(ssl, ctx);
+
+		/*
+		 * Use a temporary SSL_METHOD to get a callback on SSL_new()
+		 * from libcurl since there is no proper callback registration
+		 * available for this.
+		 */
+		os_memset(&patch_ssl_method, 0, sizeof(patch_ssl_method));
+		patch_ssl_method.ssl_new = curl_patch_ssl_new;
+		real_ssl_method = ssl->method;
+		ssl->method = &patch_ssl_method;
+	}
+#endif /* HAVE_OCSP */
+
+	return CURLE_OK;
+}
+
+#endif /* EAP_TLS_OPENSSL */
+
+
+static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
+			      const char *ca_fname, const char *username,
+			      const char *password, const char *client_cert,
+			      const char *client_key)
+{
+	CURL *curl;
+
+	wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
+		   "username=%s", address, ca_fname, username);
+
+	curl = curl_easy_init();
+	if (curl == NULL)
+		return NULL;
+
+	curl_easy_setopt(curl, CURLOPT_URL, address);
+	curl_easy_setopt(curl, CURLOPT_POST, 1L);
+	if (ca_fname) {
+		curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+#ifdef EAP_TLS_OPENSSL
+		curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
+		curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#endif /* EAP_TLS_OPENSSL */
+	} else {
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+	}
+	if (client_cert && client_key) {
+		curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert);
+		curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key);
+	}
+	/* TODO: use curl_easy_getinfo() with CURLINFO_CERTINFO to fetch
+	 * information about the server certificate */
+	curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+	curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+	curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
+	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+	if (username) {
+		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
+		curl_easy_setopt(curl, CURLOPT_USERNAME, username);
+		curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
+	}
+
+	return curl;
+}
+
+
+static int post_init_client(struct http_ctx *ctx, const char *address,
+			    const char *ca_fname, const char *username,
+			    const char *password, const char *client_cert,
+			    const char *client_key)
+{
+	char *pos;
+	int count;
+
+	clone_str(&ctx->svc_address, address);
+	clone_str(&ctx->svc_ca_fname, ca_fname);
+	clone_str(&ctx->svc_username, username);
+	clone_str(&ctx->svc_password, password);
+	clone_str(&ctx->svc_client_cert, client_cert);
+	clone_str(&ctx->svc_client_key, client_key);
+
+	/*
+	 * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
+	 * 'foo' provided via HTTP are different.
+	 */
+	for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos;
+	     pos++) {
+		if (*pos == '/')
+			count++;
+		*pos = tolower(*pos);
+	}
+
+	ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
+				    password, client_cert, client_key);
+	if (ctx->curl == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+		     const char *ca_fname, const char *username,
+		     const char *password, const char *client_cert,
+		     const char *client_key)
+{
+	if (post_init_client(ctx, address, ca_fname, username, password,
+			     client_cert, client_key) < 0)
+		return -1;
+
+	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
+					  "Content-Type: application/soap+xml");
+	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: ");
+	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:");
+	curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr);
+
+	return 0;
+}
+
+
+int soap_reinit_client(struct http_ctx *ctx)
+{
+	char *address = NULL;
+	char *ca_fname = NULL;
+	char *username = NULL;
+	char *password = NULL;
+	char *client_cert = NULL;
+	char *client_key = NULL;
+	int ret;
+
+	clear_curl(ctx);
+
+	clone_str(&address, ctx->svc_address);
+	clone_str(&ca_fname, ctx->svc_ca_fname);
+	clone_str(&username, ctx->svc_username);
+	clone_str(&password, ctx->svc_password);
+	clone_str(&client_cert, ctx->svc_client_cert);
+	clone_str(&client_key, ctx->svc_client_key);
+
+	ret = soap_init_client(ctx, address, ca_fname, username, password,
+			       client_cert, client_key);
+	os_free(address);
+	os_free(ca_fname);
+	str_clear_free(username);
+	str_clear_free(password);
+	os_free(client_cert);
+	os_free(client_key);
+	return ret;
+}
+
+
+static void free_curl_buf(struct http_ctx *ctx)
+{
+	os_free(ctx->curl_buf);
+	ctx->curl_buf = NULL;
+	ctx->curl_buf_len = 0;
+}
+
+
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node)
+{
+	char *str;
+	xml_node_t *envelope, *ret, *resp, *n;
+	CURLcode res;
+	long http = 0;
+
+	ctx->last_err = NULL;
+
+	wpa_printf(MSG_DEBUG, "SOAP: Sending message");
+	envelope = soap_build_envelope(ctx->xml, node);
+	str = xml_node_to_str(ctx->xml, envelope);
+	xml_node_free(ctx->xml, envelope);
+	wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str);
+
+	curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str);
+	free_curl_buf(ctx);
+
+	res = curl_easy_perform(ctx->curl);
+	if (res != CURLE_OK) {
+		if (!ctx->last_err)
+			ctx->last_err = curl_easy_strerror(res);
+		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+			   ctx->last_err);
+		os_free(str);
+		free_curl_buf(ctx);
+		return NULL;
+	}
+	os_free(str);
+
+	curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http);
+	wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http);
+	if (http != 200) {
+		ctx->last_err = "HTTP download failed";
+		wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+		free_curl_buf(ctx);
+		return NULL;
+	}
+
+	if (ctx->curl_buf == NULL)
+		return NULL;
+
+	wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf);
+	resp = xml_node_from_buf(ctx->xml, ctx->curl_buf);
+	free_curl_buf(ctx);
+	if (resp == NULL) {
+		wpa_printf(MSG_INFO, "Could not parse SOAP response");
+		ctx->last_err = "Could not parse SOAP response";
+		return NULL;
+	}
+
+	ret = soap_get_body(ctx->xml, resp);
+	if (ret == NULL) {
+		wpa_printf(MSG_INFO, "Could not get SOAP body");
+		ctx->last_err = "Could not get SOAP body";
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'",
+		   xml_node_get_localname(ctx->xml, ret));
+	n = xml_node_copy(ctx->xml, ret);
+	xml_node_free(ctx->xml, resp);
+
+	return n;
+}
+
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx)
+{
+	struct http_ctx *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	ctx->ctx = upper_ctx;
+	ctx->xml = xml_ctx;
+	ctx->ocsp = OPTIONAL_OCSP;
+
+	curl_global_init(CURL_GLOBAL_ALL);
+
+	return ctx;
+}
+
+
+void http_ocsp_set(struct http_ctx *ctx, int val)
+{
+	if (val == 0)
+		ctx->ocsp = NO_OCSP;
+	else if (val == 1)
+		ctx->ocsp = OPTIONAL_OCSP;
+	if (val == 2)
+		ctx->ocsp = MANDATORY_OCSP;
+}
+
+
+void http_deinit_ctx(struct http_ctx *ctx)
+{
+	clear_curl(ctx);
+	os_free(ctx->curl_buf);
+	curl_global_cleanup();
+
+	os_free(ctx->svc_address);
+	os_free(ctx->svc_ca_fname);
+	str_clear_free(ctx->svc_username);
+	str_clear_free(ctx->svc_password);
+	os_free(ctx->svc_client_cert);
+	os_free(ctx->svc_client_key);
+
+	os_free(ctx);
+}
+
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+		       const char *fname, const char *ca_fname)
+{
+	CURL *curl;
+	FILE *f;
+	CURLcode res;
+	long http = 0;
+
+	ctx->last_err = NULL;
+
+	wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)",
+		   url, fname, ca_fname);
+	curl = curl_easy_init();
+	if (curl == NULL)
+		return -1;
+
+	f = fopen(fname, "wb");
+	if (f == NULL) {
+		curl_easy_cleanup(curl);
+		return -1;
+	}
+
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	if (ca_fname) {
+		curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+		curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+	} else {
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+	}
+	curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+	curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
+	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+	res = curl_easy_perform(curl);
+	if (res != CURLE_OK) {
+		if (!ctx->last_err)
+			ctx->last_err = curl_easy_strerror(res);
+		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+			   ctx->last_err);
+		curl_easy_cleanup(curl);
+		fclose(f);
+		return -1;
+	}
+
+	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+	wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+	if (http != 200) {
+		ctx->last_err = "HTTP download failed";
+		wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+		curl_easy_cleanup(curl);
+		fclose(f);
+		return -1;
+	}
+
+	curl_easy_cleanup(curl);
+	fclose(f);
+
+	return 0;
+}
+
+
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+		 const char *content_type, const char *ext_hdr,
+		 const char *ca_fname,
+		 const char *username, const char *password,
+		 const char *client_cert, const char *client_key,
+		 size_t *resp_len)
+{
+	long http = 0;
+	CURLcode res;
+	char *ret;
+	CURL *curl;
+	struct curl_slist *curl_hdr = NULL;
+
+	ctx->last_err = NULL;
+	wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
+	curl = setup_curl_post(ctx, url, ca_fname, username, password,
+			       client_cert, client_key);
+	if (curl == NULL)
+		return NULL;
+
+	if (content_type) {
+		char ct[200];
+		snprintf(ct, sizeof(ct), "Content-Type: %s", content_type);
+		curl_hdr = curl_slist_append(curl_hdr, ct);
+	}
+	if (ext_hdr)
+		curl_hdr = curl_slist_append(curl_hdr, ext_hdr);
+	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdr);
+
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
+	free_curl_buf(ctx);
+
+	res = curl_easy_perform(curl);
+	if (res != CURLE_OK) {
+		if (!ctx->last_err)
+			ctx->last_err = curl_easy_strerror(res);
+		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+			   ctx->last_err);
+		free_curl_buf(ctx);
+		return NULL;
+	}
+
+	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+	wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+	if (http != 200) {
+		ctx->last_err = "HTTP POST failed";
+		wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http);
+		free_curl_buf(ctx);
+		return NULL;
+	}
+
+	if (ctx->curl_buf == NULL)
+		return NULL;
+
+	ret = ctx->curl_buf;
+	if (resp_len)
+		*resp_len = ctx->curl_buf_len;
+	ctx->curl_buf = NULL;
+	ctx->curl_buf_len = 0;
+
+	wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret);
+
+	return ret;
+}
+
+
+void http_set_cert_cb(struct http_ctx *ctx,
+		      int (*cb)(void *ctx, struct http_cert *cert),
+		      void *cb_ctx)
+{
+	ctx->cert_cb = cb;
+	ctx->cert_cb_ctx = cb_ctx;
+}
+
+
+const char * http_get_err(struct http_ctx *ctx)
+{
+	return ctx->last_err;
+}
diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c
index 3647c76..92a3590 100644
--- a/src/utils/ip_addr.c
+++ b/src/utils/ip_addr.c
@@ -33,30 +33,6 @@
 }
 
 
-int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b)
-{
-	if (a == NULL && b == NULL)
-		return 0;
-	if (a == NULL || b == NULL)
-		return 1;
-
-	switch (a->af) {
-	case AF_INET:
-		if (a->u.v4.s_addr != b->u.v4.s_addr)
-			return 1;
-		break;
-#ifdef CONFIG_IPV6
-	case AF_INET6:
-		if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0)
-			return 1;
-		break;
-#endif /* CONFIG_IPV6 */
-	}
-
-	return 0;
-}
-
-
 int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr)
 {
 #ifndef CONFIG_NATIVE_WINDOWS
diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h
index 79ac20c..0670411 100644
--- a/src/utils/ip_addr.h
+++ b/src/utils/ip_addr.h
@@ -22,7 +22,6 @@
 
 const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
 			    size_t buflen);
-int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b);
 int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
 
 #endif /* IP_ADDR_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index ad20834..b9247d8 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -23,6 +23,11 @@
 	os_time_t usec;
 };
 
+struct os_reltime {
+	os_time_t sec;
+	os_time_t usec;
+};
+
 /**
  * os_get_time - Get current time (sec, usec)
  * @t: Pointer to buffer for the time
@@ -30,21 +35,84 @@
  */
 int os_get_time(struct os_time *t);
 
+/**
+ * os_get_reltime - Get relative time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_reltime(struct os_reltime *t);
 
-/* Helper macros for handling struct os_time */
 
-#define os_time_before(a, b) \
-	((a)->sec < (b)->sec || \
-	 ((a)->sec == (b)->sec && (a)->usec < (b)->usec))
+/* Helpers for handling struct os_time */
 
-#define os_time_sub(a, b, res) do { \
-	(res)->sec = (a)->sec - (b)->sec; \
-	(res)->usec = (a)->usec - (b)->usec; \
-	if ((res)->usec < 0) { \
-		(res)->sec--; \
-		(res)->usec += 1000000; \
-	} \
-} while (0)
+static inline int os_time_before(struct os_time *a, struct os_time *b)
+{
+	return (a->sec < b->sec) ||
+	       (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_time_sub(struct os_time *a, struct os_time *b,
+			       struct os_time *res)
+{
+	res->sec = a->sec - b->sec;
+	res->usec = a->usec - b->usec;
+	if (res->usec < 0) {
+		res->sec--;
+		res->usec += 1000000;
+	}
+}
+
+
+/* Helpers for handling struct os_reltime */
+
+static inline int os_reltime_before(struct os_reltime *a,
+				    struct os_reltime *b)
+{
+	return (a->sec < b->sec) ||
+	       (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,
+				  struct os_reltime *res)
+{
+	res->sec = a->sec - b->sec;
+	res->usec = a->usec - b->usec;
+	if (res->usec < 0) {
+		res->sec--;
+		res->usec += 1000000;
+	}
+}
+
+
+static inline void os_reltime_age(struct os_reltime *start,
+				  struct os_reltime *age)
+{
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, start, age);
+}
+
+
+static inline int os_reltime_expired(struct os_reltime *now,
+				     struct os_reltime *ts,
+				     os_time_t timeout_secs)
+{
+	struct os_reltime age;
+
+	os_reltime_sub(now, ts, &age);
+	return (age.sec > timeout_secs) ||
+	       (age.sec == timeout_secs && age.usec > 0);
+}
+
+
+static inline int os_reltime_initialized(struct os_reltime *t)
+{
+	return t->sec != 0 || t->usec != 0;
+}
+
 
 /**
  * os_mktime - Convert broken-down time into seconds since 1970-01-01
@@ -172,6 +240,13 @@
 char * os_readfile(const char *name, size_t *len);
 
 /**
+ * os_file_exists - Check whether the specified file exists
+ * @fname: Path and name of the file
+ * Returns: 1 if the file exists or 0 if not
+ */
+int os_file_exists(const char *fname);
+
+/**
  * os_zalloc - Allocate and zero memory
  * @size: Number of bytes to allocate
  * Returns: Pointer to allocated and zeroed memory or %NULL on failure
@@ -361,15 +436,6 @@
 int os_strncmp(const char *s1, const char *s2, size_t n);
 
 /**
- * os_strncpy - Copy a string
- * @dest: Destination
- * @src: Source
- * @n: Maximum number of characters to copy
- * Returns: dest
- */
-char * os_strncpy(char *dest, const char *src, size_t n);
-
-/**
  * os_strstr - Locate a substring
  * @haystack: String (haystack) to search from
  * @needle: Needle to search from haystack
@@ -465,9 +531,6 @@
 #ifndef os_strncmp
 #define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
 #endif
-#ifndef os_strncpy
-#define os_strncpy(d, s, n) strncpy((d), (s), (n))
-#endif
 #ifndef os_strrchr
 #define os_strrchr(s, c) strrchr((s), (c))
 #endif
@@ -493,6 +556,21 @@
 	return os_realloc(ptr, nmemb * size);
 }
 
+/**
+ * os_remove_in_array - Remove a member from an array by index
+ * @ptr: Pointer to the array
+ * @nmemb: Current member count of the array
+ * @size: The size per member of the array
+ * @idx: Index of the member to be removed
+ */
+static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size,
+				      size_t idx)
+{
+	if (idx < nmemb - 1)
+		os_memmove(((unsigned char *) ptr) + idx * size,
+			   ((unsigned char *) ptr) + (idx + 1) * size,
+			   (nmemb - idx - 1) * size);
+}
 
 /**
  * os_strlcpy - Copy a string with size bound and NUL-termination
@@ -506,6 +584,32 @@
  */
 size_t os_strlcpy(char *dest, const char *src, size_t siz);
 
+/**
+ * os_memcmp_const - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: 0 if buffers are equal, non-zero if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time could provide external observer information
+ * about the location of the difference in the memory buffers. The return value
+ * does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to
+ * sort items into a defined order. Unlike os_memcmp(), execution time of
+ * os_memcmp_const() does not depend on the contents of the compared memory
+ * buffers, but only on the total compared length.
+ */
+int os_memcmp_const(const void *a, const void *b, size_t len);
+
+/**
+ * os_exec - Execute an external program
+ * @program: Path to the program
+ * @arg: Command line argument string
+ * @wait_completion: Whether to wait until the program execution completes
+ * Returns: 0 on success, -1 on error
+ */
+int os_exec(const char *program, const char *arg, int wait_completion);
+
 
 #ifdef OS_REJECT_C_LIB_FUNCTIONS
 #define malloc OS_DO_NOT_USE_malloc
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index e4b7fdb..90b6688 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -41,6 +41,17 @@
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+	int res;
+	struct timeval tv;
+	res = gettimeofday(&tv, NULL);
+	t->sec = tv.tv_sec;
+	t->usec = tv.tv_usec;
+	return res;
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
 	      os_time_t *t)
 {
@@ -452,6 +463,20 @@
 }
 
 
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res;
+
+	for (res = 0, i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return res;
+}
+
+
 char * os_strstr(const char *haystack, const char *needle)
 {
 	size_t len = os_strlen(needle);
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index cabf73b..2649111 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -26,6 +26,12 @@
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+	return -1;
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
 	      os_time_t *t)
 {
@@ -212,6 +218,11 @@
 }
 
 
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	return 0;
+}
+
 char * os_strstr(const char *haystack, const char *needle)
 {
 	return NULL;
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index 10b9e0d..523a4d0 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -9,18 +9,19 @@
 #include "includes.h"
 
 #include <time.h>
+#include <sys/wait.h>
 
 #ifdef ANDROID
 #include <sys/capability.h>
-#include <linux/prctl.h>
+#include <sys/prctl.h>
 #include <private/android_filesystem_config.h>
 #endif /* ANDROID */
 
 #include "os.h"
+#include "common.h"
 
 #ifdef WPA_TRACE
 
-#include "common.h"
 #include "wpa_debug.h"
 #include "trace.h"
 #include "list.h"
@@ -60,6 +61,43 @@
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+#if defined(CLOCK_BOOTTIME)
+	static clockid_t clock_id = CLOCK_BOOTTIME;
+#elif defined(CLOCK_MONOTONIC)
+	static clockid_t clock_id = CLOCK_MONOTONIC;
+#else
+	static clockid_t clock_id = CLOCK_REALTIME;
+#endif
+	struct timespec ts;
+	int res;
+
+	while (1) {
+		res = clock_gettime(clock_id, &ts);
+		if (res == 0) {
+			t->sec = ts.tv_sec;
+			t->usec = ts.tv_nsec / 1000;
+			return 0;
+		}
+		switch (clock_id) {
+#ifdef CLOCK_BOOTTIME
+		case CLOCK_BOOTTIME:
+			clock_id = CLOCK_MONOTONIC;
+			break;
+#endif
+#ifdef CLOCK_MONOTONIC
+		case CLOCK_MONOTONIC:
+			clock_id = CLOCK_REALTIME;
+			break;
+#endif
+		case CLOCK_REALTIME:
+			return -1;
+		}
+	}
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
 	      os_time_t *t)
 {
@@ -268,7 +306,7 @@
 	struct __user_cap_header_struct header;
 	struct __user_cap_data_struct cap;
 
-	setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+	setgroups(ARRAY_SIZE(groups), groups);
 
 	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
 
@@ -370,6 +408,16 @@
 }
 
 
+int os_file_exists(const char *fname)
+{
+	FILE *f = fopen(fname, "rb");
+	if (f == NULL)
+		return 0;
+	fclose(f);
+	return 1;
+}
+
+
 #ifndef WPA_TRACE
 void * os_zalloc(size_t size)
 {
@@ -403,6 +451,20 @@
 }
 
 
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res;
+
+	for (res = 0, i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return res;
+}
+
+
 #ifdef WPA_TRACE
 
 void * os_malloc(size_t size)
@@ -493,3 +555,57 @@
 }
 
 #endif /* WPA_TRACE */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	pid_t pid;
+	int pid_status;
+
+	pid = fork();
+	if (pid < 0) {
+		perror("fork");
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		const int MAX_ARG = 30;
+		char *_program, *_arg, *pos;
+		char *argv[MAX_ARG + 1];
+		int i;
+
+		_program = os_strdup(program);
+		_arg = os_strdup(arg);
+
+		argv[0] = _program;
+
+		i = 1;
+		pos = _arg;
+		while (i < MAX_ARG && pos && *pos) {
+			while (*pos == ' ')
+				pos++;
+			if (*pos == '\0')
+				break;
+			argv[i++] = pos;
+			pos = os_strchr(pos, ' ');
+			if (pos)
+				*pos++ = '\0';
+		}
+		argv[i] = NULL;
+
+		execv(program, argv);
+		perror("execv");
+		os_free(_program);
+		os_free(_arg);
+		exit(0);
+		return -1;
+	}
+
+	if (wait_completion) {
+		/* wait for the child process to complete in the parent */
+		waitpid(pid, &pid_status, 0);
+	}
+
+	return 0;
+}
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index 163cebe..57ee132 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -47,6 +47,17 @@
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+	/* consider using performance counters or so instead */
+	struct os_time now;
+	int res = os_get_time(&now);
+	t->sec = now.sec;
+	t->usec = now.usec;
+	return res;
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
 	      os_time_t *t)
 {
@@ -233,3 +244,23 @@
 
 	return s - src - 1;
 }
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res;
+
+	for (res = 0, i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return res;
+}
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	return -1;
+}
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index 08510d0..d955dc4 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -485,17 +485,15 @@
 
 /**
  * scard_init - Initialize SIM/USIM connection using PC/SC
- * @sim_type: Allowed SIM types (SIM, USIM, or both)
  * @reader: Reader name prefix to search for
  * Returns: Pointer to private data structure, or %NULL on failure
  *
  * This function is used to initialize SIM/USIM connection. PC/SC is used to
- * open connection to the SIM/USIM card and the card is verified to support the
- * selected sim_type. In addition, local flag is set if a PIN is needed to
- * access some of the card functions. Once the connection is not needed
- * anymore, scard_deinit() can be used to close it.
+ * open connection to the SIM/USIM card. In addition, local flag is set if a
+ * PIN is needed to access some of the card functions. Once the connection is
+ * not needed anymore, scard_deinit() can be used to close it.
  */
-struct scard_data * scard_init(scard_sim_type sim_type, const char *reader)
+struct scard_data * scard_init(const char *reader)
 {
 	long ret;
 	unsigned long len, pos;
@@ -612,20 +610,14 @@
 
 	blen = sizeof(buf);
 
-	scard->sim_type = SCARD_GSM_SIM;
-	if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) {
-		wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
-		if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
-				       SCARD_USIM, NULL, 0)) {
-			wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported");
-			if (sim_type == SCARD_USIM_ONLY)
-				goto failed;
-			wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM");
-			scard->sim_type = SCARD_GSM_SIM;
-		} else {
-			wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
-			scard->sim_type = SCARD_USIM;
-		}
+	wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
+	if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
+			       SCARD_USIM, NULL, 0)) {
+		wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM");
+		scard->sim_type = SCARD_GSM_SIM;
+	} else {
+		wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
+		scard->sim_type = SCARD_USIM;
 	}
 
 	if (scard->sim_type == SCARD_GSM_SIM) {
@@ -1245,6 +1237,7 @@
 		cmd[4] = 17;
 		cmd[5] = 16;
 		os_memcpy(cmd + 6, _rand, 16);
+		get_resp[0] = USIM_CLA;
 	}
 	len = sizeof(resp);
 	ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
@@ -1413,6 +1406,12 @@
 		pos += IK_LEN;
 		wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
 
+		if (end > pos) {
+			wpa_hexdump(MSG_DEBUG,
+				    "SCARD: Ignore extra data in end",
+				    pos, end - pos);
+		}
+
 		return 0;
 	}
 
diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h
index b4ebc99..eacd2a2 100644
--- a/src/utils/pcsc_funcs.h
+++ b/src/utils/pcsc_funcs.h
@@ -9,15 +9,8 @@
 #ifndef PCSC_FUNCS_H
 #define PCSC_FUNCS_H
 
-typedef enum {
-	SCARD_GSM_SIM_ONLY,
-	SCARD_USIM_ONLY,
-	SCARD_TRY_BOTH
-} scard_sim_type;
-
-
 #ifdef PCSC_FUNCS
-struct scard_data * scard_init(scard_sim_type sim_type, const char *reader);
+struct scard_data * scard_init(const char *reader);
 void scard_deinit(struct scard_data *scard);
 
 int scard_set_pin(struct scard_data *scard, const char *pin);
@@ -34,7 +27,7 @@
 
 #else /* PCSC_FUNCS */
 
-#define scard_init(s, r) NULL
+#define scard_init(r) NULL
 #define scard_deinit(s) do { } while (0)
 #define scard_set_pin(s, p) -1
 #define scard_get_imsi(s, i, l) -1
diff --git a/src/utils/platform.h b/src/utils/platform.h
new file mode 100644
index 0000000..46cfe78
--- /dev/null
+++ b/src/utils/platform.h
@@ -0,0 +1,21 @@
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include "includes.h"
+#include "common.h"
+
+#define le16_to_cpu		le_to_host16
+#define le32_to_cpu		le_to_host32
+
+#define get_unaligned(p)					\
+({								\
+	struct packed_dummy_struct {				\
+		typeof(*(p)) __val;				\
+	} __attribute__((packed)) *__ptr = (void *) (p);	\
+								\
+	__ptr->__val;						\
+})
+#define get_unaligned_le16(p)	le16_to_cpu(get_unaligned((uint16_t *)(p)))
+#define get_unaligned_le32(p)	le32_to_cpu(get_unaligned((uint32_t *)(p)))
+
+#endif /* PLATFORM_H */
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
index 804473f..197a4af 100644
--- a/src/utils/radiotap.c
+++ b/src/utils/radiotap.c
@@ -2,6 +2,7 @@
  * Radiotap parser
  *
  * Copyright 2007		Andy Green <andy@warmcat.com>
+ * Copyright 2009		Johannes Berg <johannes@sipsolutions.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -10,34 +11,44 @@
  * Alternatively, this software may be distributed under the terms of BSD
  * license.
  *
- * See README and COPYING for more details.
- *
- *
- * Modified for userspace by Johannes Berg <johannes@sipsolutions.net>
- * I only modified some things on top to ease syncing should bugs be found.
+ * See COPYING for more details.
  */
-
-#include "includes.h"
-
-#include "common.h"
 #include "radiotap_iter.h"
-
-#define le16_to_cpu		le_to_host16
-#define le32_to_cpu		le_to_host32
-#define __le32			uint32_t
-#define ulong			unsigned long
-#define unlikely(cond)		(cond)
-#define get_unaligned(p)					\
-({								\
-	struct packed_dummy_struct {				\
-		typeof(*(p)) __val;				\
-	} __attribute__((packed)) *__ptr = (void *) (p);	\
-								\
-	__ptr->__val;						\
-})
+#include "platform.h"
 
 /* function prototypes and related defs are in radiotap_iter.h */
 
+static const struct radiotap_align_size rtap_namespace_sizes[] = {
+	[IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
+	[IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
+	[IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
+	[IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
+	/*
+	 * add more here as they are defined in radiotap.h
+	 */
+};
+
+static const struct ieee80211_radiotap_namespace radiotap_ns = {
+	.n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
+	.align_size = rtap_namespace_sizes,
+};
+
 /**
  * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
  * @iterator: radiotap_iterator to initialize
@@ -73,38 +84,52 @@
  * get_unaligned((type *)iterator.this_arg) to dereference
  * iterator.this_arg for type "type" safely on all arches.
  *
- * Example code:
- * See Documentation/networking/radiotap-headers.txt
+ * Example code: parse.c
  */
 
 int ieee80211_radiotap_iterator_init(
-    struct ieee80211_radiotap_iterator *iterator,
-    struct ieee80211_radiotap_header *radiotap_header,
-    int max_length)
+	struct ieee80211_radiotap_iterator *iterator,
+	struct ieee80211_radiotap_header *radiotap_header,
+	int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
 {
+	/* must at least have the radiotap header */
+	if (max_length < (int)sizeof(struct ieee80211_radiotap_header))
+		return -EINVAL;
+
 	/* Linux only supports version 0 radiotap format */
 	if (radiotap_header->it_version)
 		return -EINVAL;
 
 	/* sanity check for allowed length and radiotap length field */
-	if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len)))
+	if (max_length < get_unaligned_le16(&radiotap_header->it_len))
 		return -EINVAL;
 
-	iterator->rtheader = radiotap_header;
-	iterator->max_length = le16_to_cpu(get_unaligned(
-						&radiotap_header->it_len));
-	iterator->arg_index = 0;
-	iterator->bitmap_shifter = le32_to_cpu(get_unaligned(
-						&radiotap_header->it_present));
-	iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
-	iterator->this_arg = NULL;
+	iterator->_rtheader = radiotap_header;
+	iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
+	iterator->_arg_index = 0;
+	iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
+	iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
+	iterator->_reset_on_ext = 0;
+	iterator->_next_bitmap = &radiotap_header->it_present;
+	iterator->_next_bitmap++;
+	iterator->_vns = vns;
+	iterator->current_namespace = &radiotap_ns;
+	iterator->is_radiotap_ns = 1;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+	iterator->n_overrides = 0;
+	iterator->overrides = NULL;
+#endif
 
 	/* find payload start allowing for extended bitmap(s) */
 
-	if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
-		while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) &
-				   (1<<IEEE80211_RADIOTAP_EXT)) {
-			iterator->arg += sizeof(u32);
+	if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+		if ((unsigned long)iterator->_arg -
+		    (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
+		    (unsigned long)iterator->_max_length)
+			return -EINVAL;
+		while (get_unaligned_le32(iterator->_arg) &
+					(1 << IEEE80211_RADIOTAP_EXT)) {
+			iterator->_arg += sizeof(uint32_t);
 
 			/*
 			 * check for insanity where the present bitmaps
@@ -112,12 +137,14 @@
 			 * stated radiotap header length
 			 */
 
-			if (((ulong)iterator->arg - (ulong)iterator->rtheader)
-			    > (ulong)iterator->max_length)
+			if ((unsigned long)iterator->_arg -
+			    (unsigned long)iterator->_rtheader +
+			    sizeof(uint32_t) >
+			    (unsigned long)iterator->_max_length)
 				return -EINVAL;
 		}
 
-		iterator->arg += sizeof(u32);
+		iterator->_arg += sizeof(uint32_t);
 
 		/*
 		 * no need to check again for blowing past stated radiotap
@@ -126,11 +153,57 @@
 		 */
 	}
 
+	iterator->this_arg = iterator->_arg;
+
 	/* we are all initialized happily */
 
 	return 0;
 }
 
+static void find_ns(struct ieee80211_radiotap_iterator *iterator,
+		    uint32_t oui, uint8_t subns)
+{
+	int i;
+
+	iterator->current_namespace = NULL;
+
+	if (!iterator->_vns)
+		return;
+
+	for (i = 0; i < iterator->_vns->n_ns; i++) {
+		if (iterator->_vns->ns[i].oui != oui)
+			continue;
+		if (iterator->_vns->ns[i].subns != subns)
+			continue;
+
+		iterator->current_namespace = &iterator->_vns->ns[i];
+		break;
+	}
+}
+
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+static int find_override(struct ieee80211_radiotap_iterator *iterator,
+			 int *align, int *size)
+{
+	int i;
+
+	if (!iterator->overrides)
+		return 0;
+
+	for (i = 0; i < iterator->n_overrides; i++) {
+		if (iterator->_arg_index == iterator->overrides[i].field) {
+			*align = iterator->overrides[i].align;
+			*size = iterator->overrides[i].size;
+			if (!*align) /* erroneous override */
+				return 0;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+#endif
+
 
 /**
  * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
@@ -156,99 +229,106 @@
  */
 
 int ieee80211_radiotap_iterator_next(
-    struct ieee80211_radiotap_iterator *iterator)
+	struct ieee80211_radiotap_iterator *iterator)
 {
-
-	/*
-	 * small length lookup table for all radiotap types we heard of
-	 * starting from b0 in the bitmap, so we can walk the payload
-	 * area of the radiotap header
-	 *
-	 * There is a requirement to pad args, so that args
-	 * of a given length must begin at a boundary of that length
-	 * -- but note that compound args are allowed (eg, 2 x u16
-	 * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
-	 * a reliable indicator of alignment requirement.
-	 *
-	 * upper nybble: content alignment for arg
-	 * lower nybble: content length for arg
-	 */
-
-	static const u8 rt_sizes[] = {
-		[IEEE80211_RADIOTAP_TSFT] = 0x88,
-		[IEEE80211_RADIOTAP_FLAGS] = 0x11,
-		[IEEE80211_RADIOTAP_RATE] = 0x11,
-		[IEEE80211_RADIOTAP_CHANNEL] = 0x24,
-		[IEEE80211_RADIOTAP_FHSS] = 0x22,
-		[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
-		[IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
-		[IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
-		[IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
-		[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
-		[IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
-		[IEEE80211_RADIOTAP_ANTENNA] = 0x11,
-		[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
-		[IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11,
-		[IEEE80211_RADIOTAP_RX_FLAGS] = 0x22,
-		[IEEE80211_RADIOTAP_TX_FLAGS] = 0x22,
-		[IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11,
-		[IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11,
-		/*
-		 * add more here as they are defined in
-		 * include/net/ieee80211_radiotap.h
-		 */
-	};
-
-	/*
-	 * for every radiotap entry we can at
-	 * least skip (by knowing the length)...
-	 */
-
-	while (iterator->arg_index < (int) sizeof(rt_sizes)) {
+	while (1) {
 		int hit = 0;
-		int pad;
+		int pad, align, size, subns;
+		uint32_t oui;
 
-		if (!(iterator->bitmap_shifter & 1))
+		/* if no more EXT bits, that's it */
+		if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
+		    !(iterator->_bitmap_shifter & 1))
+			return -ENOENT;
+
+		if (!(iterator->_bitmap_shifter & 1))
 			goto next_entry; /* arg not present */
 
+		/* get alignment/size of data */
+		switch (iterator->_arg_index % 32) {
+		case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+		case IEEE80211_RADIOTAP_EXT:
+			align = 1;
+			size = 0;
+			break;
+		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+			align = 2;
+			size = 6;
+			break;
+		default:
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+			if (find_override(iterator, &align, &size)) {
+				/* all set */
+			} else
+#endif
+			if (!iterator->current_namespace ||
+			    iterator->_arg_index >= iterator->current_namespace->n_bits) {
+				if (iterator->current_namespace == &radiotap_ns)
+					return -ENOENT;
+				align = 0;
+			} else {
+				align = iterator->current_namespace->align_size[iterator->_arg_index].align;
+				size = iterator->current_namespace->align_size[iterator->_arg_index].size;
+			}
+			if (!align) {
+				/* skip all subsequent data */
+				iterator->_arg = iterator->_next_ns_data;
+				/* give up on this namespace */
+				iterator->current_namespace = NULL;
+				goto next_entry;
+			}
+			break;
+		}
+
 		/*
 		 * arg is present, account for alignment padding
-		 *  8-bit args can be at any alignment
-		 * 16-bit args must start on 16-bit boundary
-		 * 32-bit args must start on 32-bit boundary
-		 * 64-bit args must start on 64-bit boundary
 		 *
-		 * note that total arg size can differ from alignment of
-		 * elements inside arg, so we use upper nybble of length
-		 * table to base alignment on
-		 *
-		 * also note: these alignments are ** relative to the
-		 * start of the radiotap header **.  There is no guarantee
+		 * Note that these alignments are relative to the start
+		 * of the radiotap header.  There is no guarantee
 		 * that the radiotap header itself is aligned on any
 		 * kind of boundary.
 		 *
-		 * the above is why get_unaligned() is used to dereference
-		 * multibyte elements from the radiotap area
+		 * The above is why get_unaligned() is used to dereference
+		 * multibyte elements from the radiotap area.
 		 */
 
-		pad = (((ulong)iterator->arg) -
-			((ulong)iterator->rtheader)) &
-			((rt_sizes[iterator->arg_index] >> 4) - 1);
+		pad = ((unsigned long)iterator->_arg -
+		       (unsigned long)iterator->_rtheader) & (align - 1);
 
 		if (pad)
-			iterator->arg +=
-				(rt_sizes[iterator->arg_index] >> 4) - pad;
+			iterator->_arg += align - pad;
+
+		if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
+			int vnslen;
+
+			if ((unsigned long)iterator->_arg + size -
+			    (unsigned long)iterator->_rtheader >
+			    (unsigned long)iterator->_max_length)
+				return -EINVAL;
+
+			oui = (*iterator->_arg << 16) |
+				(*(iterator->_arg + 1) << 8) |
+				*(iterator->_arg + 2);
+			subns = *(iterator->_arg + 3);
+
+			find_ns(iterator, oui, subns);
+
+			vnslen = get_unaligned_le16(iterator->_arg + 4);
+			iterator->_next_ns_data = iterator->_arg + size + vnslen;
+			if (!iterator->current_namespace)
+				size += vnslen;
+		}
 
 		/*
 		 * this is what we will return to user, but we need to
 		 * move on first so next call has something fresh to test
 		 */
-		iterator->this_arg_index = iterator->arg_index;
-		iterator->this_arg = iterator->arg;
-		hit = 1;
+		iterator->this_arg_index = iterator->_arg_index;
+		iterator->this_arg = iterator->_arg;
+		iterator->this_arg_size = size;
 
 		/* internally move on the size of this arg */
-		iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+		iterator->_arg += size;
 
 		/*
 		 * check for insanity where we are given a bitmap that
@@ -257,31 +337,57 @@
 		 * max_length on the last arg, never exceeding it.
 		 */
 
-		if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
-		    (ulong) iterator->max_length)
+		if ((unsigned long)iterator->_arg -
+		    (unsigned long)iterator->_rtheader >
+		    (unsigned long)iterator->_max_length)
 			return -EINVAL;
 
-	next_entry:
-		iterator->arg_index++;
-		if (unlikely((iterator->arg_index & 31) == 0)) {
-			/* completed current u32 bitmap */
-			if (iterator->bitmap_shifter & 1) {
-				/* b31 was set, there is more */
-				/* move to next u32 bitmap */
-				iterator->bitmap_shifter = le32_to_cpu(
-					get_unaligned(iterator->next_bitmap));
-				iterator->next_bitmap++;
-			} else
-				/* no more bitmaps: end */
-				iterator->arg_index = sizeof(rt_sizes);
-		} else /* just try the next bit */
-			iterator->bitmap_shifter >>= 1;
+		/* these special ones are valid in each bitmap word */
+		switch (iterator->_arg_index % 32) {
+		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+			iterator->_reset_on_ext = 1;
+
+			iterator->is_radiotap_ns = 0;
+			/*
+			 * If parser didn't register this vendor
+			 * namespace with us, allow it to show it
+			 * as 'raw. Do do that, set argument index
+			 * to vendor namespace.
+			 */
+			iterator->this_arg_index =
+				IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
+			if (!iterator->current_namespace)
+				hit = 1;
+			goto next_entry;
+		case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+			iterator->_reset_on_ext = 1;
+			iterator->current_namespace = &radiotap_ns;
+			iterator->is_radiotap_ns = 1;
+			goto next_entry;
+		case IEEE80211_RADIOTAP_EXT:
+			/*
+			 * bit 31 was set, there is more
+			 * -- move to next u32 bitmap
+			 */
+			iterator->_bitmap_shifter =
+				get_unaligned_le32(iterator->_next_bitmap);
+			iterator->_next_bitmap++;
+			if (iterator->_reset_on_ext)
+				iterator->_arg_index = 0;
+			else
+				iterator->_arg_index++;
+			iterator->_reset_on_ext = 0;
+			break;
+		default:
+			/* we've got a hit! */
+			hit = 1;
+ next_entry:
+			iterator->_bitmap_shifter >>= 1;
+			iterator->_arg_index++;
+		}
 
 		/* if we found a valid arg earlier, return it now */
 		if (hit)
 			return 0;
 	}
-
-	/* we don't know how to handle any more args, we're done */
-	return -ENOENT;
 }
diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h
index 137288f..0572e7c 100644
--- a/src/utils/radiotap.h
+++ b/src/utils/radiotap.h
@@ -1,6 +1,3 @@
-/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */
-/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */
-
 /*-
  * Copyright (c) 2003, 2004 David Young.  All rights reserved.
  *
@@ -178,6 +175,14 @@
  *
  *     Number of unicast retries a transmitted frame used.
  *
+ * IEEE80211_RADIOTAP_MCS	u8, u8, u8		unitless
+ *
+ *     Contains a bitmap of known fields/flags, the flags, and
+ *     the MCS index.
+ *
+ * IEEE80211_RADIOTAP_AMPDU_STATUS	u32, u16, u8, u8	unitlesss
+ *
+ *	Contains the AMPDU information for the subframe.
  */
 enum ieee80211_radiotap_type {
 	IEEE80211_RADIOTAP_TSFT = 0,
@@ -198,6 +203,13 @@
 	IEEE80211_RADIOTAP_TX_FLAGS = 15,
 	IEEE80211_RADIOTAP_RTS_RETRIES = 16,
 	IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+
+	IEEE80211_RADIOTAP_MCS = 19,
+	IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+
+	/* valid in every it_present bitmap, even vendor namespaces */
+	IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+	IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
 	IEEE80211_RADIOTAP_EXT = 31
 };
 
@@ -230,8 +242,10 @@
 						 * 802.11 header and payload
 						 * (to 32-bit boundary)
 						 */
+#define IEEE80211_RADIOTAP_F_BADFCS	0x40	/* frame failed FCS check */
+
 /* For IEEE80211_RADIOTAP_RX_FLAGS */
-#define IEEE80211_RADIOTAP_F_RX_BADFCS	0x0001	/* frame failed crc check */
+#define IEEE80211_RADIOTAP_F_RX_BADPLCP	0x0002 /* bad PLCP */
 
 /* For IEEE80211_RADIOTAP_TX_FLAGS */
 #define IEEE80211_RADIOTAP_F_TX_FAIL	0x0001	/* failed due to excessive
@@ -240,4 +254,38 @@
 #define IEEE80211_RADIOTAP_F_TX_RTS	0x0004	/* used rts/cts handshake */
 #define IEEE80211_RADIOTAP_F_TX_NOACK	0x0008	/* don't expect an ACK */
 
+/* For IEEE80211_RADIOTAP_AMPDU_STATUS */
+#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN		0x0001
+#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN		0x0002
+#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN		0x0004
+#define IEEE80211_RADIOTAP_AMPDU_IS_LAST		0x0008
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR		0x0010
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN	0x0020
+
+/* For IEEE80211_RADIOTAP_MCS */
+#define IEEE80211_RADIOTAP_MCS_HAVE_BW		0x01
+#define IEEE80211_RADIOTAP_MCS_HAVE_MCS		0x02
+#define IEEE80211_RADIOTAP_MCS_HAVE_GI		0x04
+#define IEEE80211_RADIOTAP_MCS_HAVE_FMT		0x08
+#define IEEE80211_RADIOTAP_MCS_HAVE_FEC		0x10
+#define IEEE80211_RADIOTAP_MCS_HAVE_STBC	0x20
+#define IEEE80211_RADIOTAP_MCS_HAVE_NESS	0x40
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT1	0x80
+
+
+#define IEEE80211_RADIOTAP_MCS_BW_MASK		0x03
+#define		IEEE80211_RADIOTAP_MCS_BW_20	0
+#define		IEEE80211_RADIOTAP_MCS_BW_40	1
+#define		IEEE80211_RADIOTAP_MCS_BW_20L	2
+#define		IEEE80211_RADIOTAP_MCS_BW_20U	3
+#define IEEE80211_RADIOTAP_MCS_SGI		0x04
+#define IEEE80211_RADIOTAP_MCS_FMT_GF		0x08
+#define IEEE80211_RADIOTAP_MCS_FEC_LDPC		0x10
+#define IEEE80211_RADIOTAP_MCS_STBC_MASK	0x60
+#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT	5
+#define		IEEE80211_RADIOTAP_MCS_STBC_1	1
+#define		IEEE80211_RADIOTAP_MCS_STBC_2	2
+#define		IEEE80211_RADIOTAP_MCS_STBC_3	3
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT0	0x80
+
 #endif				/* IEEE80211_RADIOTAP_H */
diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h
index 2e0e872..b768c85 100644
--- a/src/utils/radiotap_iter.h
+++ b/src/utils/radiotap_iter.h
@@ -1,56 +1,96 @@
-/*
- * Radiotap parser
- *
- * Copyright 2007		Andy Green <andy@warmcat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
 #ifndef __RADIOTAP_ITER_H
 #define __RADIOTAP_ITER_H
 
+#include <stdint.h>
 #include "radiotap.h"
 
 /* Radiotap header iteration
  *   implemented in radiotap.c
  */
+
+struct radiotap_override {
+	uint8_t field;
+	uint8_t align:4, size:4;
+};
+
+struct radiotap_align_size {
+	uint8_t align:4, size:4;
+};
+
+struct ieee80211_radiotap_namespace {
+	const struct radiotap_align_size *align_size;
+	int n_bits;
+	uint32_t oui;
+	uint8_t subns;
+};
+
+struct ieee80211_radiotap_vendor_namespaces {
+	const struct ieee80211_radiotap_namespace *ns;
+	int n_ns;
+};
+
 /**
  * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
- * @rtheader: pointer to the radiotap header we are walking through
- * @max_length: length of radiotap header in cpu byte ordering
- * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg
- * @this_arg: pointer to current radiotap arg
- * @arg_index: internal next argument index
- * @arg: internal next argument pointer
- * @next_bitmap: internal pointer to next present u32
- * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @this_arg_index: index of current arg, valid after each successful call
+ *	to ieee80211_radiotap_iterator_next()
+ * @this_arg: pointer to current radiotap arg; it is valid after each
+ *	call to ieee80211_radiotap_iterator_next() but also after
+ *	ieee80211_radiotap_iterator_init() where it will point to
+ *	the beginning of the actual data portion
+ * @this_arg_size: length of the current arg, for convenience
+ * @current_namespace: pointer to the current namespace definition
+ *	(or internally %NULL if the current namespace is unknown)
+ * @is_radiotap_ns: indicates whether the current namespace is the default
+ *	radiotap namespace or not
+ *
+ * @overrides: override standard radiotap fields
+ * @n_overrides: number of overrides
+ *
+ * @_rtheader: pointer to the radiotap header we are walking through
+ * @_max_length: length of radiotap header in cpu byte ordering
+ * @_arg_index: next argument index
+ * @_arg: next argument pointer
+ * @_next_bitmap: internal pointer to next present u32
+ * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @_vns: vendor namespace definitions
+ * @_next_ns_data: beginning of the next namespace's data
+ * @_reset_on_ext: internal; reset the arg index to 0 when going to the
+ *	next bitmap word
+ *
+ * Describes the radiotap parser state. Fields prefixed with an underscore
+ * must not be used by users of the parser, only by the parser internally.
  */
 
 struct ieee80211_radiotap_iterator {
-	struct ieee80211_radiotap_header *rtheader;
-	int max_length;
-	int this_arg_index;
-	unsigned char *this_arg;
+	struct ieee80211_radiotap_header *_rtheader;
+	const struct ieee80211_radiotap_vendor_namespaces *_vns;
+	const struct ieee80211_radiotap_namespace *current_namespace;
 
-	int arg_index;
-	unsigned char *arg;
-	uint32_t *next_bitmap;
-	uint32_t bitmap_shifter;
+	unsigned char *_arg, *_next_ns_data;
+	uint32_t *_next_bitmap;
+
+	unsigned char *this_arg;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+	const struct radiotap_override *overrides;
+	int n_overrides;
+#endif
+	int this_arg_index;
+	int this_arg_size;
+
+	int is_radiotap_ns;
+
+	int _max_length;
+	int _arg_index;
+	uint32_t _bitmap_shifter;
+	int _reset_on_ext;
 };
 
 extern int ieee80211_radiotap_iterator_init(
-   struct ieee80211_radiotap_iterator *iterator,
-   struct ieee80211_radiotap_header *radiotap_header,
-   int max_length);
+	struct ieee80211_radiotap_iterator *iterator,
+	struct ieee80211_radiotap_header *radiotap_header,
+	int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns);
 
 extern int ieee80211_radiotap_iterator_next(
-   struct ieee80211_radiotap_iterator *iterator);
+	struct ieee80211_radiotap_iterator *iterator);
 
 #endif /* __RADIOTAP_ITER_H */
diff --git a/src/utils/trace.c b/src/utils/trace.c
index 6795d41..6044f5f 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -18,11 +18,9 @@
 
 #ifdef WPA_TRACE_BFD
 #include <bfd.h>
-#ifdef __linux__
-#include <demangle.h>
-#else /* __linux__ */
-#include <libiberty/demangle.h>
-#endif /* __linux__ */
+
+#define DMGL_PARAMS      (1 << 0)
+#define DMGL_ANSI        (1 << 1)
 
 static char *prg_fname = NULL;
 static bfd *cached_abfd = NULL;
@@ -187,6 +185,7 @@
 		wpa_printf(MSG_INFO, "     %s() %s:%u",
 			   name, filename, data.line);
 		free(aname);
+		aname = NULL;
 
 		data.found = bfd_find_inliner_info(abfd, &data.filename,
 						   &data.function, &data.line);
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
new file mode 100644
index 0000000..9a9ec40
--- /dev/null
+++ b/src/utils/utils_module_tests.c
@@ -0,0 +1,266 @@
+/*
+ * utils module tests
+ * Copyright (c) 2014, 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/bitfield.h"
+#include "utils/ext_password.h"
+#include "utils/trace.h"
+
+
+struct printf_test_data {
+	u8 *data;
+	size_t len;
+	char *encoded;
+};
+
+static const struct printf_test_data printf_tests[] = {
+	{ (u8 *) "abcde", 5, "abcde" },
+	{ (u8 *) "a\0b\nc\ed\re\tf\"\\", 13, "a\\0b\\nc\\ed\\re\\tf\\\"\\\\" },
+	{ (u8 *) "\x00\x31\x00\x32\x00\x39", 6, "\\x001\\0002\\09" },
+	{ (u8 *) "\n\n\n", 3, "\n\12\x0a" },
+	{ (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+	  "\\xc3\\xa5\xc3\\xa4\\xc3\\xb6\\xc3\\x85\\xc3\\x84\\xc3\\x96" },
+	{ (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+	  "\\303\\245\\303\\244\\303\\266\\303\\205\\303\\204\\303\\226" },
+	{ (u8 *) "\xe5\xe4\xf6\xc5\xc4\xd6", 6,
+	  "\\xe5\\xe4\\xf6\\xc5\\xc4\\xd6" },
+	{ NULL, 0, NULL }
+};
+
+
+static int printf_encode_decode_tests(void)
+{
+	int i;
+	size_t binlen;
+	char buf[100];
+	u8 bin[100];
+	int errors = 0;
+
+	wpa_printf(MSG_INFO, "printf encode/decode tests");
+
+	for (i = 0; printf_tests[i].data; i++) {
+		const struct printf_test_data *test = &printf_tests[i];
+		printf_encode(buf, sizeof(buf), test->data, test->len);
+		wpa_printf(MSG_INFO, "%d: -> \"%s\"", i, buf);
+
+		binlen = printf_decode(bin, sizeof(bin), buf);
+		if (binlen != test->len ||
+		    os_memcmp(bin, test->data, binlen) != 0) {
+			wpa_hexdump(MSG_ERROR, "Error in decoding#1",
+				    bin, binlen);
+			errors++;
+		}
+
+		binlen = printf_decode(bin, sizeof(bin), test->encoded);
+		if (binlen != test->len ||
+		    os_memcmp(bin, test->data, binlen) != 0) {
+			wpa_hexdump(MSG_ERROR, "Error in decoding#2",
+				    bin, binlen);
+			errors++;
+		}
+	}
+
+	buf[5] = 'A';
+	printf_encode(buf, 5, (const u8 *) "abcde", 5);
+	if (buf[5] != 'A') {
+		wpa_printf(MSG_ERROR, "Error in bounds checking#1");
+		errors++;
+	}
+
+	for (i = 5; i < 10; i++) {
+		buf[i] = 'A';
+		printf_encode(buf, i, (const u8 *) "\xdd\xdd\xdd\xdd\xdd", 5);
+		if (buf[i] != 'A') {
+			wpa_printf(MSG_ERROR, "Error in bounds checking#2(%d)",
+				   i);
+			errors++;
+		}
+	}
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int bitfield_tests(void)
+{
+	struct bitfield *bf;
+	int i;
+	int errors = 0;
+
+	wpa_printf(MSG_INFO, "bitfield tests");
+
+	bf = bitfield_alloc(123);
+	if (bf == NULL)
+		return -1;
+
+	for (i = 0; i < 123; i++) {
+		if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+			errors++;
+		if (i > 0 && bitfield_is_set(bf, i - 1))
+			errors++;
+		bitfield_set(bf, i);
+		if (!bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 123; i < 200; i++) {
+		if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+			errors++;
+		if (i > 0 && bitfield_is_set(bf, i - 1))
+			errors++;
+		bitfield_set(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 0; i < 123; i++) {
+		if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+			errors++;
+		bitfield_set(bf, i);
+		if (!bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 0; i < 123; i++) {
+		if (!bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 0; i < 123; i++) {
+		if (bitfield_get_first_zero(bf) != i)
+			errors++;
+		bitfield_set(bf, i);
+	}
+	if (bitfield_get_first_zero(bf) != -1)
+		errors++;
+	for (i = 0; i < 123; i++) {
+		if (!bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_get_first_zero(bf) != i)
+			errors++;
+		bitfield_set(bf, i);
+	}
+	if (bitfield_get_first_zero(bf) != -1)
+		errors++;
+
+	bitfield_free(bf);
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d bitfield test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int int_array_tests(void)
+{
+	int test1[] = { 1, 2, 3, 4, 5, 6, 0 };
+	int test2[] = { 1, -1, 0 };
+	int test3[] = { 1, 1, 1, -1, 2, 3, 4, 1, 2, 0 };
+	int test3_res[] = { -1, 1, 2, 3, 4, 0 };
+	int errors = 0;
+	int len;
+
+	wpa_printf(MSG_INFO, "int_array tests");
+
+	if (int_array_len(test1) != 6 ||
+	    int_array_len(test2) != 2)
+		errors++;
+
+	int_array_sort_unique(test3);
+	len = int_array_len(test3_res);
+	if (int_array_len(test3) != len)
+		errors++;
+	else if (os_memcmp(test3, test3_res, len * sizeof(int)) != 0)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d int_array test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ext_password_tests(void)
+{
+	struct ext_password_data *data;
+	int ret = 0;
+	struct wpabuf *pw;
+
+	wpa_printf(MSG_INFO, "ext_password tests");
+
+	data = ext_password_init("unknown", "foo");
+	if (data != NULL)
+		return -1;
+
+	data = ext_password_init("test", NULL);
+	if (data == NULL)
+		return -1;
+	pw = ext_password_get(data, "foo");
+	if (pw != NULL)
+		ret = -1;
+	ext_password_free(pw);
+
+	ext_password_deinit(data);
+
+	pw = ext_password_get(NULL, "foo");
+	if (pw != NULL)
+		ret = -1;
+	ext_password_free(pw);
+
+	return ret;
+}
+
+
+static int trace_tests(void)
+{
+	wpa_printf(MSG_INFO, "trace tests");
+
+	wpa_trace_show("test backtrace");
+	wpa_trace_dump_funcname("test funcname", trace_tests);
+
+	return 0;
+}
+
+
+int utils_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "utils module tests");
+
+	if (printf_encode_decode_tests() < 0 ||
+	    ext_password_tests() < 0 ||
+	    trace_tests() < 0 ||
+	    bitfield_tests() < 0 ||
+	    int_array_tests() < 0)
+		ret = -1;
+
+	return ret;
+}
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 38ea8aa..68cbace 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -375,19 +375,19 @@
 #endif /* CONFIG_ANDROID_LOG */
 }
 
-void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
+void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
 {
 	_wpa_hexdump(level, title, buf, len, 1);
 }
 
 
-void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
+void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
 {
 	_wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
 }
 
 
-static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
 			       size_t len, int show)
 {
 	size_t i, llen;
@@ -407,7 +407,7 @@
 			/* can do ascii processing in userspace */
 			for (i = 0; i < len; i++)
 				fprintf(wpa_debug_tracing_file,
-					" %02x", buf[i]);
+					" %02x", pos[i]);
 		}
 		fflush(wpa_debug_tracing_file);
 	}
@@ -495,13 +495,14 @@
 }
 
 
-void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+		       size_t len)
 {
 	_wpa_hexdump_ascii(level, title, buf, len, 1);
 }
 
 
-void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
 			   size_t len)
 {
 	_wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
@@ -595,10 +596,14 @@
 {
 	va_list ap;
 	char *buf;
-	const int buflen = 2048;
+	int buflen;
 	int len;
 	char prefix[130];
 
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
 	buf = os_malloc(buflen);
 	if (buf == NULL) {
 		wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
@@ -629,12 +634,16 @@
 {
 	va_list ap;
 	char *buf;
-	const int buflen = 2048;
+	int buflen;
 	int len;
 
 	if (!wpa_msg_cb)
 		return;
 
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
 	buf = os_malloc(buflen);
 	if (buf == NULL) {
 		wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
@@ -653,9 +662,13 @@
 {
 	va_list ap;
 	char *buf;
-	const int buflen = 2048;
+	int buflen;
 	int len;
 
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
 	buf = os_malloc(buflen);
 	if (buf == NULL) {
 		wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate "
@@ -672,13 +685,45 @@
 }
 
 
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+
+	if (!wpa_msg_cb)
+		return;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "wpa_msg_global_ctrl: Failed to allocate message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_msg_cb(ctx, level, 1, buf, len);
+	os_free(buf);
+}
+
+
 void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
 {
 	va_list ap;
 	char *buf;
-	const int buflen = 2048;
+	int buflen;
 	int len;
 
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
 	buf = os_malloc(buflen);
 	if (buf == NULL) {
 		wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate "
@@ -711,9 +756,13 @@
 {
 	va_list ap;
 	char *buf;
-	const int buflen = 2048;
+	int buflen;
 	int len;
 
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
 	buf = os_malloc(buflen);
 	if (buf == NULL) {
 		wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 2ed1bd8..391f197 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -11,6 +11,10 @@
 
 #include "wpabuf.h"
 
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+
 /* Debugging function - conditional printf and hex dump. Driver wrappers can
  * use these for debugging purposes. */
 
@@ -77,7 +81,7 @@
  * output may be directed to stdout, stderr, and/or syslog based on
  * configuration. The contents of buf is printed out has hex dump.
  */
-void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len);
+void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
 
 static inline void wpa_hexdump_buf(int level, const char *title,
 				   const struct wpabuf *buf)
@@ -99,7 +103,7 @@
  * like wpa_hexdump(), but by default, does not include secret keys (passwords,
  * etc.) in debug output.
  */
-void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len);
+void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
 
 static inline void wpa_hexdump_buf_key(int level, const char *title,
 				       const struct wpabuf *buf)
@@ -121,7 +125,7 @@
  * the hex numbers and ASCII characters (for printable range) are shown. 16
  * bytes per line will be shown.
  */
-void wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
 		       size_t len);
 
 /**
@@ -138,7 +142,7 @@
  * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
  * default, does not include secret keys (passwords, etc.) in debug output.
  */
-void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
 			   size_t len);
 
 /*
@@ -156,6 +160,7 @@
 #define wpa_msg(args...) do { } while (0)
 #define wpa_msg_ctrl(args...) do { } while (0)
 #define wpa_msg_global(args...) do { } while (0)
+#define wpa_msg_global_ctrl(args...) do { } while (0)
 #define wpa_msg_no_global(args...) do { } while (0)
 #define wpa_msg_register_cb(f) do { } while (0)
 #define wpa_msg_register_ifname_cb(f) do { } while (0)
@@ -208,6 +213,21 @@
 PRINTF_FORMAT(3, 4);
 
 /**
+ * wpa_msg_global_ctrl - Conditional global printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg_global(), but it sends the output only to the
+ * attached global ctrl_iface monitors. In other words, it can be used for
+ * frequent events that do not need to be sent to syslog.
+ */
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
  * wpa_msg_no_global - Conditional printf for ctrl_iface monitors
  * @ctx: Pointer to context data; this is the ctx variable registered
  *	with struct wpa_driver_ops::init()
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
new file mode 100644
index 0000000..4916d29
--- /dev/null
+++ b/src/utils/xml-utils.c
@@ -0,0 +1,471 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "xml-utils.h"
+
+
+static xml_node_t * get_node_uri_iter(struct xml_node_ctx *ctx,
+				      xml_node_t *root, char *uri)
+{
+	char *end;
+	xml_node_t *node;
+	const char *name;
+
+	end = strchr(uri, '/');
+	if (end)
+		*end++ = '\0';
+
+	node = root;
+	xml_node_for_each_sibling(ctx, node) {
+		xml_node_for_each_check(ctx, node);
+		name = xml_node_get_localname(ctx, node);
+		if (strcasecmp(name, uri) == 0)
+			break;
+	}
+
+	if (node == NULL)
+		return NULL;
+
+	if (end) {
+		return get_node_uri_iter(ctx, xml_node_first_child(ctx, node),
+					 end);
+	}
+
+	return node;
+}
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+			  const char *uri)
+{
+	char *search;
+	xml_node_t *node;
+
+	search = os_strdup(uri);
+	if (search == NULL)
+		return NULL;
+
+	node = get_node_uri_iter(ctx, root, search);
+
+	os_free(search);
+	return node;
+}
+
+
+static xml_node_t * get_node_iter(struct xml_node_ctx *ctx,
+				  xml_node_t *root, const char *path)
+{
+	char *end;
+	xml_node_t *node;
+	const char *name;
+
+	end = os_strchr(path, '/');
+	if (end)
+		*end++ = '\0';
+
+	xml_node_for_each_child(ctx, node, root) {
+		xml_node_for_each_check(ctx, node);
+		name = xml_node_get_localname(ctx, node);
+		if (os_strcasecmp(name, path) == 0)
+			break;
+	}
+
+	if (node == NULL)
+		return NULL;
+	if (end)
+		return get_node_iter(ctx, node, end);
+	return node;
+}
+
+
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+		      const char *path)
+{
+	char *search;
+	xml_node_t *node;
+
+	search = os_strdup(path);
+	if (search == NULL)
+		return NULL;
+
+	node = get_node_iter(ctx, root, search);
+
+	os_free(search);
+	return node;
+}
+
+
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+			    const char *path)
+{
+	xml_node_t *node;
+	xml_node_t *match;
+
+	xml_node_for_each_child(ctx, node, root) {
+		xml_node_for_each_check(ctx, node);
+		match = get_node(ctx, node, path);
+		if (match)
+			return match;
+	}
+
+	return NULL;
+}
+
+
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name)
+{
+	xml_node_t *node;
+	char *buf, *buf2, *start;
+	size_t len;
+
+	buf = os_readfile(name, &len);
+	if (buf == NULL)
+		return NULL;
+	buf2 = os_realloc(buf, len + 1);
+	if (buf2 == NULL) {
+		os_free(buf);
+		return NULL;
+	}
+	buf = buf2;
+	buf[len] = '\0';
+
+	start = os_strstr(buf, "<!DOCTYPE ");
+	if (start) {
+		char *pos = start + 1;
+		int count = 1;
+		while (*pos) {
+			if (*pos == '<')
+				count++;
+			else if (*pos == '>') {
+				count--;
+				if (count == 0) {
+					pos++;
+					break;
+				}
+			}
+			pos++;
+		}
+		if (count == 0) {
+			/* Remove DOCTYPE to allow the file to be parsed */
+			os_memset(start, ' ', pos - start);
+		}
+	}
+
+	node = xml_node_from_buf(ctx, buf);
+	os_free(buf);
+
+	return node;
+}
+
+
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node)
+{
+	FILE *f;
+	char *str;
+
+	str = xml_node_to_str(ctx, node);
+	if (str == NULL)
+		return -1;
+
+	f = fopen(fname, "w");
+	if (!f) {
+		os_free(str);
+		return -1;
+	}
+
+	fprintf(f, "%s\n", str);
+	os_free(str);
+	fclose(f);
+
+	return 0;
+}
+
+
+static char * get_val(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	char *val, *pos;
+
+	val = xml_node_get_text(ctx, node);
+	if (val == NULL)
+		return NULL;
+	pos = val;
+	while (*pos) {
+		if (*pos != ' ' && *pos != '\t' && *pos != '\r' && *pos != '\n')
+			return val;
+		pos++;
+	}
+
+	return NULL;
+}
+
+
+static char * add_path(const char *prev, const char *leaf)
+{
+	size_t len;
+	char *new_uri;
+
+	if (prev == NULL)
+		return NULL;
+
+	len = os_strlen(prev) + 1 + os_strlen(leaf) + 1;
+	new_uri = os_malloc(len);
+	if (new_uri)
+		os_snprintf(new_uri, len, "%s/%s", prev, leaf);
+
+	return new_uri;
+}
+
+
+static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out,
+			 xml_node_t *in, const char *uri)
+{
+	xml_node_t *node;
+	xml_node_t *tnds;
+	const char *name;
+	char *val;
+	char *new_uri;
+
+	xml_node_for_each_child(ctx, node, in) {
+		xml_node_for_each_check(ctx, node);
+		name = xml_node_get_localname(ctx, node);
+
+		tnds = xml_node_create(ctx, out, NULL, "Node");
+		if (tnds == NULL)
+			return;
+		xml_node_create_text(ctx, tnds, NULL, "NodeName", name);
+
+		if (uri)
+			xml_node_create_text(ctx, tnds, NULL, "Path", uri);
+
+		val = get_val(ctx, node);
+		if (val) {
+			xml_node_create_text(ctx, tnds, NULL, "Value", val);
+			xml_node_get_text_free(ctx, val);
+		}
+
+		new_uri = add_path(uri, name);
+		node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
+		os_free(new_uri);
+	}
+}
+
+
+static int add_ddfname(struct xml_node_ctx *ctx, xml_node_t *parent,
+		       const char *urn)
+{
+	xml_node_t *node;
+
+	node = xml_node_create(ctx, parent, NULL, "RTProperties");
+	if (node == NULL)
+		return -1;
+	node = xml_node_create(ctx, node, NULL, "Type");
+	if (node == NULL)
+		return -1;
+	xml_node_create_text(ctx, node, NULL, "DDFName", urn);
+	return 0;
+}
+
+
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+			int use_path, const char *urn, const char *ns_uri)
+{
+	xml_node_t *root;
+	xml_node_t *node;
+	const char *name;
+
+	root = xml_node_create_root(ctx, ns_uri, NULL, NULL, "MgmtTree");
+	if (root == NULL)
+		return NULL;
+
+	xml_node_create_text(ctx, root, NULL, "VerDTD", "1.2");
+
+	name = xml_node_get_localname(ctx, mo);
+
+	node = xml_node_create(ctx, root, NULL, "Node");
+	if (node == NULL)
+		goto fail;
+	xml_node_create_text(ctx, node, NULL, "NodeName", name);
+	if (urn)
+		add_ddfname(ctx, node, urn);
+
+	node_to_tnds(ctx, use_path ? root : node, mo, use_path ? name : NULL);
+
+	return root;
+
+fail:
+	xml_node_free(ctx, root);
+	return NULL;
+}
+
+
+static xml_node_t * get_first_child_node(struct xml_node_ctx *ctx,
+					 xml_node_t *node,
+					 const char *name)
+{
+	const char *lname;
+	xml_node_t *child;
+
+	xml_node_for_each_child(ctx, child, node) {
+		xml_node_for_each_check(ctx, child);
+		lname = xml_node_get_localname(ctx, child);
+		if (os_strcasecmp(lname, name) == 0)
+			return child;
+	}
+
+	return NULL;
+}
+
+
+static char * get_node_text(struct xml_node_ctx *ctx, xml_node_t *node,
+			    const char *node_name)
+{
+	node = get_first_child_node(ctx, node, node_name);
+	if (node == NULL)
+		return NULL;
+	return xml_node_get_text(ctx, node);
+}
+
+
+static xml_node_t * add_mo_node(struct xml_node_ctx *ctx, xml_node_t *root,
+				xml_node_t *node, const char *uri)
+{
+	char *nodename, *value, *path;
+	xml_node_t *parent;
+
+	nodename = get_node_text(ctx, node, "NodeName");
+	if (nodename == NULL)
+		return NULL;
+	value = get_node_text(ctx, node, "Value");
+
+	if (root == NULL) {
+		root = xml_node_create_root(ctx, NULL, NULL, NULL,
+					    nodename);
+		if (root && value)
+			xml_node_set_text(ctx, root, value);
+	} else {
+		if (uri == NULL) {
+			xml_node_get_text_free(ctx, nodename);
+			xml_node_get_text_free(ctx, value);
+			return NULL;
+		}
+		path = get_node_text(ctx, node, "Path");
+		if (path)
+			uri = path;
+		parent = get_node_uri(ctx, root, uri);
+		xml_node_get_text_free(ctx, path);
+		if (parent == NULL) {
+			printf("Could not find URI '%s'\n", uri);
+			xml_node_get_text_free(ctx, nodename);
+			xml_node_get_text_free(ctx, value);
+			return NULL;
+		}
+		if (value)
+			xml_node_create_text(ctx, parent, NULL, nodename,
+					     value);
+		else
+			xml_node_create(ctx, parent, NULL, nodename);
+	}
+
+	xml_node_get_text_free(ctx, nodename);
+	xml_node_get_text_free(ctx, value);
+
+	return root;
+}
+
+
+static xml_node_t * tnds_to_mo_iter(struct xml_node_ctx *ctx, xml_node_t *root,
+				    xml_node_t *node, const char *uri)
+{
+	xml_node_t *child;
+	const char *name;
+	char *nodename;
+
+	xml_node_for_each_sibling(ctx, node) {
+		xml_node_for_each_check(ctx, node);
+
+		nodename = get_node_text(ctx, node, "NodeName");
+		if (nodename == NULL)
+			return NULL;
+
+		name = xml_node_get_localname(ctx, node);
+		if (strcmp(name, "Node") == 0) {
+			if (root && !uri) {
+				printf("Invalid TNDS tree structure - "
+				       "multiple top level nodes\n");
+				xml_node_get_text_free(ctx, nodename);
+				return NULL;
+			}
+			root = add_mo_node(ctx, root, node, uri);
+		}
+
+		child = get_first_child_node(ctx, node, "Node");
+		if (child) {
+			if (uri == NULL)
+				tnds_to_mo_iter(ctx, root, child, nodename);
+			else {
+				char *new_uri;
+				new_uri = add_path(uri, nodename);
+				tnds_to_mo_iter(ctx, root, child, new_uri);
+				os_free(new_uri);
+			}
+		}
+		xml_node_get_text_free(ctx, nodename);
+	}
+
+	return root;
+}
+
+
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds)
+{
+	const char *name;
+	xml_node_t *node;
+
+	name = xml_node_get_localname(ctx, tnds);
+	if (name == NULL || os_strcmp(name, "MgmtTree") != 0)
+		return NULL;
+
+	node = get_first_child_node(ctx, tnds, "Node");
+	if (!node)
+		return NULL;
+	return tnds_to_mo_iter(ctx, NULL, node, NULL);
+}
+
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xml_node_t *envelope, *body;
+	xml_namespace_t *ns;
+
+	envelope = xml_node_create_root(
+		ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns,
+		"Envelope");
+	if (envelope == NULL)
+		return NULL;
+	body = xml_node_create(ctx, envelope, ns, "Body");
+	xml_node_add_child(ctx, body, node);
+	return envelope;
+}
+
+
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap)
+{
+	xml_node_t *body, *child;
+
+	body = get_node_uri(ctx, soap, "Envelope/Body");
+	if (body == NULL)
+		return NULL;
+	xml_node_for_each_child(ctx, child, body) {
+		xml_node_for_each_check(ctx, child);
+		return child;
+	}
+	return NULL;
+}
diff --git a/src/utils/xml-utils.h b/src/utils/xml-utils.h
new file mode 100644
index 0000000..fb6208c
--- /dev/null
+++ b/src/utils/xml-utils.h
@@ -0,0 +1,97 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef XML_UTILS_H
+#define XML_UTILS_H
+
+struct xml_node_ctx;
+typedef struct xml_node xml_node_t;
+typedef struct xml_namespace_foo xml_namespace_t;
+
+/* XML library wrappers */
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+		 const char *xml_schema_fname, char **ret_err);
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+		     const char *dtd_fname, char **ret_err);
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf);
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+				    xml_node_t *node);
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+			xml_node_t *child);
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+				  const char *ns_prefix,
+				  xml_namespace_t **ret_ns, const char *name);
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+			     xml_namespace_t *ns, const char *name);
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+				  xml_node_t *parent, xml_namespace_t *ns,
+				  const char *name, const char *value);
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+				     xml_node_t *parent, const char *ns_uri,
+				     const char *name, const char *value);
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+		       const char *value);
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+		      xml_namespace_t *ns, const char *name, const char *value);
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+			       char *name);
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+				  const char *ns_uri, char *name);
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val);
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+				  xml_node_t *parent);
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+				   xml_node_t *node);
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node);
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val);
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+				int *ret_len);
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node);
+
+#define xml_node_for_each_child(ctx, child, parent) \
+for (child = xml_node_first_child(ctx, parent); \
+     child; \
+     child = xml_node_next_sibling(ctx, child))
+
+#define xml_node_for_each_sibling(ctx, node) \
+for (; \
+     node; \
+     node = xml_node_next_sibling(ctx, node))
+
+#define xml_node_for_each_check(ctx, child) \
+if (!xml_node_is_element(ctx, child)) \
+	continue
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+					const void *env);
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx);
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+			  const char *uri);
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+		      const char *path);
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+			    const char *path);
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name);
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node);
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+			int use_path, const char *urn, const char *ns_uri);
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds);
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap);
+
+#endif /* XML_UTILS_H */
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
new file mode 100644
index 0000000..c928394
--- /dev/null
+++ b/src/utils/xml_libxml2.c
@@ -0,0 +1,457 @@
+/*
+ * XML wrapper for libxml2
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#define LIBXML_VALID_ENABLED
+#include <libxml/tree.h>
+#include <libxml/xmlschemastypes.h>
+
+#include "common.h"
+#include "base64.h"
+#include "xml-utils.h"
+
+
+struct xml_node_ctx {
+	void *ctx;
+};
+
+
+struct str_buf {
+	char *buf;
+	size_t len;
+};
+
+#define MAX_STR 1000
+
+static void add_str(void *ctx_ptr, const char *fmt, ...)
+{
+	struct str_buf *str = ctx_ptr;
+	va_list ap;
+	char *n;
+	int len;
+
+	n = os_realloc(str->buf, str->len + MAX_STR + 2);
+	if (n == NULL)
+		return;
+	str->buf = n;
+
+	va_start(ap, fmt);
+	len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap);
+	va_end(ap);
+	if (len >= MAX_STR)
+		len = MAX_STR - 1;
+	str->len += len;
+	str->buf[str->len] = '\0';
+}
+
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+		 const char *xml_schema_fname, char **ret_err)
+{
+	xmlDocPtr doc;
+	xmlNodePtr n;
+	xmlSchemaParserCtxtPtr pctx;
+	xmlSchemaValidCtxtPtr vctx;
+	xmlSchemaPtr schema;
+	int ret;
+	struct str_buf errors;
+
+	if (ret_err)
+		*ret_err = NULL;
+
+	doc = xmlNewDoc((xmlChar *) "1.0");
+	if (doc == NULL)
+		return -1;
+	n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+	if (n == NULL) {
+		xmlFreeDoc(doc);
+		return -1;
+	}
+	xmlDocSetRootElement(doc, n);
+
+	os_memset(&errors, 0, sizeof(errors));
+
+	pctx = xmlSchemaNewParserCtxt(xml_schema_fname);
+	xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str,
+				 (xmlSchemaValidityWarningFunc) add_str,
+				 &errors);
+	schema = xmlSchemaParse(pctx);
+	xmlSchemaFreeParserCtxt(pctx);
+
+	vctx = xmlSchemaNewValidCtxt(schema);
+	xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str,
+				(xmlSchemaValidityWarningFunc) add_str,
+				&errors);
+
+	ret = xmlSchemaValidateDoc(vctx, doc);
+	xmlSchemaFreeValidCtxt(vctx);
+	xmlFreeDoc(doc);
+	xmlSchemaFree(schema);
+
+	if (ret == 0) {
+		os_free(errors.buf);
+		return 0;
+	} else if (ret > 0) {
+		if (ret_err)
+			*ret_err = errors.buf;
+		else
+			os_free(errors.buf);
+		return -1;
+	} else {
+		if (ret_err)
+			*ret_err = errors.buf;
+		else
+			os_free(errors.buf);
+		return -1;
+	}
+}
+
+
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+		     const char *dtd_fname, char **ret_err)
+{
+	xmlDocPtr doc;
+	xmlNodePtr n;
+	xmlValidCtxt vctx;
+	xmlDtdPtr dtd;
+	int ret;
+	struct str_buf errors;
+
+	if (ret_err)
+		*ret_err = NULL;
+
+	doc = xmlNewDoc((xmlChar *) "1.0");
+	if (doc == NULL)
+		return -1;
+	n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+	if (n == NULL) {
+		xmlFreeDoc(doc);
+		return -1;
+	}
+	xmlDocSetRootElement(doc, n);
+
+	os_memset(&errors, 0, sizeof(errors));
+
+	dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname);
+	if (dtd == NULL) {
+		xmlFreeDoc(doc);
+		return -1;
+	}
+
+	os_memset(&vctx, 0, sizeof(vctx));
+	vctx.userData = &errors;
+	vctx.error = add_str;
+	vctx.warning = add_str;
+	ret = xmlValidateDtd(&vctx, doc, dtd);
+	xmlFreeDoc(doc);
+	xmlFreeDtd(dtd);
+
+	if (ret == 1) {
+		os_free(errors.buf);
+		return 0;
+	} else {
+		if (ret_err)
+			*ret_err = errors.buf;
+		else
+			os_free(errors.buf);
+		return -1;
+	}
+}
+
+
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xmlFreeNode((xmlNodePtr) node);
+}
+
+
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	return (xml_node_t *) ((xmlNodePtr) node)->parent;
+}
+
+
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf)
+{
+	xmlDocPtr doc;
+	xmlNodePtr node;
+
+	doc = xmlParseMemory(buf, strlen(buf));
+	if (doc == NULL)
+		return NULL;
+	node = xmlDocGetRootElement(doc);
+	node = xmlCopyNode(node, 1);
+	xmlFreeDoc(doc);
+
+	return (xml_node_t *) node;
+}
+
+
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+				    xml_node_t *node)
+{
+	return (const char *) ((xmlNodePtr) node)->name;
+}
+
+
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xmlChar *buf;
+	int bufsiz;
+	char *ret, *pos;
+	xmlNodePtr n = (xmlNodePtr) node;
+	xmlDocPtr doc;
+
+	doc = xmlNewDoc((xmlChar *) "1.0");
+	n = xmlDocCopyNode(n, doc, 1);
+	xmlDocSetRootElement(doc, n);
+	xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0);
+	xmlFreeDoc(doc);
+	pos = (char *) buf;
+	if (strncmp(pos, "<?xml", 5) == 0) {
+		pos = strchr(pos, '>');
+		if (pos)
+			pos++;
+		while (pos && (*pos == '\r' || *pos == '\n'))
+			pos++;
+	}
+	if (pos)
+		ret = os_strdup(pos);
+	else
+		ret = NULL;
+	xmlFree(buf);
+
+	if (ret) {
+		pos = ret;
+		if (pos[0]) {
+			while (pos[1])
+				pos++;
+		}
+		while (pos >= ret && *pos == '\n')
+			*pos-- = '\0';
+	}
+
+	return ret;
+}
+
+
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xmlUnlinkNode((xmlNodePtr) node);
+}
+
+
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+			xml_node_t *child)
+{
+	xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child);
+}
+
+
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+				  const char *ns_prefix,
+				  xml_namespace_t **ret_ns, const char *name)
+{
+	xmlNodePtr node;
+	xmlNsPtr ns = NULL;
+
+	node = xmlNewNode(NULL, (const xmlChar *) name);
+	if (node == NULL)
+		return NULL;
+	if (ns_uri) {
+		ns = xmlNewNs(node, (const xmlChar *) ns_uri,
+			      (const xmlChar *) ns_prefix);
+		xmlSetNs(node, ns);
+	}
+
+	if (ret_ns)
+		*ret_ns = (xml_namespace_t *) ns;
+
+	return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+			     xml_namespace_t *ns, const char *name)
+{
+	xmlNodePtr node;
+	node = xmlNewChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+			   (const xmlChar *) name, NULL);
+	return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+				  xml_node_t *parent, xml_namespace_t *ns,
+				  const char *name, const char *value)
+{
+	xmlNodePtr node;
+	node = xmlNewTextChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+			       (const xmlChar *) name, (const xmlChar *) value);
+	return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+				     xml_node_t *parent, const char *ns_uri,
+				     const char *name, const char *value)
+{
+	xmlNodePtr node;
+	xmlNsPtr ns;
+
+	node = xmlNewTextChild((xmlNodePtr) parent, NULL,
+			       (const xmlChar *) name, (const xmlChar *) value);
+	ns = xmlNewNs(node, (const xmlChar *) ns_uri, NULL);
+	xmlSetNs(node, ns);
+	return (xml_node_t *) node;
+}
+
+
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+		       const char *value)
+{
+	/* TODO: escape XML special chars in value */
+	xmlNodeSetContent((xmlNodePtr) node, (xmlChar *) value);
+}
+
+
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+		      xml_namespace_t *ns, const char *name, const char *value)
+{
+	xmlAttrPtr attr;
+
+	if (ns) {
+		attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns,
+				    (const xmlChar *) name,
+				    (const xmlChar *) value);
+	} else {
+		attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name,
+				  (const xmlChar *) value);
+	}
+
+	return attr ? 0 : -1;
+}
+
+
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+			       char *name)
+{
+	return (char *) xmlGetNoNsProp((xmlNodePtr) node,
+				       (const xmlChar *) name);
+}
+
+
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+				  const char *ns_uri, char *name)
+{
+	return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name,
+				     (const xmlChar *) ns_uri);
+}
+
+
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val)
+{
+	if (val)
+		xmlFree((xmlChar *) val);
+}
+
+
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+				  xml_node_t *parent)
+{
+	return (xml_node_t *) ((xmlNodePtr) parent)->children;
+}
+
+
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+				   xml_node_t *node)
+{
+	return (xml_node_t *) ((xmlNodePtr) node)->next;
+}
+
+
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	return ((xmlNodePtr) node)->type == XML_ELEMENT_NODE;
+}
+
+
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	if (xmlChildElementCount((xmlNodePtr) node) > 0)
+		return NULL;
+	return (char *) xmlNodeGetContent((xmlNodePtr) node);
+}
+
+
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val)
+{
+	if (val)
+		xmlFree((xmlChar *) val);
+}
+
+
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+				int *ret_len)
+{
+	char *txt;
+	unsigned char *ret;
+	size_t len;
+
+	txt = xml_node_get_text(ctx, node);
+	if (txt == NULL)
+		return NULL;
+
+	ret = base64_decode((unsigned char *) txt, strlen(txt), &len);
+	if (ret_len)
+		*ret_len = len;
+	xml_node_get_text_free(ctx, txt);
+	if (ret == NULL)
+		return NULL;
+	txt = os_malloc(len + 1);
+	if (txt == NULL) {
+		os_free(ret);
+		return NULL;
+	}
+	os_memcpy(txt, ret, len);
+	txt[len] = '\0';
+	return txt;
+}
+
+
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	if (node == NULL)
+		return NULL;
+	return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1);
+}
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+					const void *env)
+{
+	struct xml_node_ctx *xctx;
+
+	xctx = os_zalloc(sizeof(*xctx));
+	if (xctx == NULL)
+		return NULL;
+	xctx->ctx = upper_ctx;
+
+	LIBXML_TEST_VERSION
+
+	return xctx;
+}
+
+
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx)
+{
+	xmlSchemaCleanupTypes();
+	xmlCleanupParser();
+	xmlMemoryDump();
+	os_free(ctx);
+}
diff --git a/src/wps/Makefile b/src/wps/Makefile
index 9c41962..adfd3df 100644
--- a/src/wps/Makefile
+++ b/src/wps/Makefile
@@ -2,7 +2,7 @@
 	@echo Nothing to be made.
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
 	@echo Nothing to be made.
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
index 6ca3214..ac088c4 100644
--- a/src/wps/http_server.c
+++ b/src/wps/http_server.c
@@ -232,6 +232,7 @@
 {
 	struct sockaddr_in sin;
 	struct http_server *srv;
+	int on = 1;
 
 	srv = os_zalloc(sizeof(*srv));
 	if (srv == NULL)
@@ -242,6 +243,15 @@
 	srv->fd = socket(AF_INET, SOCK_STREAM, 0);
 	if (srv->fd < 0)
 		goto fail;
+
+	if (setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "HTTP: setsockopt(SO_REUSEADDR) failed: %s",
+			   strerror(errno));
+		/* try to continue anyway */
+	}
+
 	if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
 		goto fail;
 	if (port < 0)
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
index ad4f4a1..2f08f37 100644
--- a/src/wps/httpread.c
+++ b/src/wps/httpread.c
@@ -67,8 +67,6 @@
 	int timeout_seconds;            /* 0 or total duration timeout period */
 
 	/* dynamically used information follows */
-	int sd_registered;      /* nonzero if we need to unregister socket */
-	int to_registered;      /* nonzero if we need to unregister timeout */
 
 	int got_hdr;            /* nonzero when header is finalized */
 	char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
@@ -129,19 +127,6 @@
 }
 
 
-/* convert hex to binary
- * Requires that c have been previously tested true with isxdigit().
- */
-static int hex_value(int c)
-{
-	if (isdigit(c))
-		return c - '0';
-	if (islower(c))
-		return 10 + c - 'a';
-	return 10 + c - 'A';
-}
-
-
 static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
 
 /* httpread_destroy -- if h is non-NULL, clean up
@@ -156,12 +141,8 @@
 	if (!h)
 		return;
 
-	if (h->to_registered)
-		eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
-	h->to_registered = 0;
-	if (h->sd_registered)
-		eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
-	h->sd_registered = 0;
+	eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+	eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
 	os_free(h->body);
 	os_free(h->uri);
 	os_memset(h, 0, sizeof(*h));  /* aid debugging */
@@ -176,7 +157,6 @@
 {
 	struct httpread *h = user_ctx;
 	wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
-	h->to_registered = 0;   /* is self-cancelling */
 	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
 }
 
@@ -295,8 +275,7 @@
 			int c = *rawuri;
 			if (c == '%' &&
 			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
-				*uri++ = (hex_value(rawuri[1]) << 4) |
-					hex_value(rawuri[2]);
+				*uri++ = hex2byte(rawuri + 1);
 				rawuri += 3;
 			} else {
 				*uri++ = c;
@@ -434,8 +413,8 @@
 		 */
 		if (httpread_debug >= 10)
 			wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
-			h->got_body = 1;
-			goto got_file;
+		h->got_body = 1;
+		goto got_file;
 	}
 	rbp = readbuf;
 
@@ -638,7 +617,6 @@
 		 * We do NOT support trailers except to skip them --
 		 * this is supported (generally) by the http spec.
 		 */
-		bbp = h->body + h->body_nbytes;
 		for (;;) {
 			int c;
 			if (nread <= 0)
@@ -703,15 +681,11 @@
 	 * and just in case somehow we don't get destroyed right away,
 	 * unregister now.
 	 */
-	if (h->sd_registered)
-		eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
-	h->sd_registered = 0;
+	eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
 	/* The application can destroy us whenever they feel like...
 	 * cancel timeout.
 	 */
-	if (h->to_registered)
-		eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
-	h->to_registered = 0;
+	eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
 	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
 }
 
@@ -749,21 +723,17 @@
 	h->max_bytes = max_bytes;
 	h->timeout_seconds = timeout_seconds;
 
-	if (timeout_seconds > 0) {
-		if (eloop_register_timeout(timeout_seconds, 0,
-					   httpread_timeout_handler,
-					   NULL, h)) {
-			/* No way to recover (from malloc failure) */
-			goto fail;
-		}
-		h->to_registered = 1;
+	if (timeout_seconds > 0 &&
+	    eloop_register_timeout(timeout_seconds, 0,
+				   httpread_timeout_handler, NULL, h)) {
+		/* No way to recover (from malloc failure) */
+		goto fail;
 	}
 	if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
 				NULL, h)) {
 		/* No way to recover (from malloc failure) */
 		goto fail;
 	}
-	h->sd_registered = 1;
 	return h;
 
 fail:
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 96685d2..d45dfc8 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -30,6 +30,7 @@
 };
 
 static char wifi_handover_type[] = "application/vnd.wfa.wsc";
+static char p2p_handover_type[] = "application/vnd.wfa.p2p";
 
 static int ndef_parse_record(const u8 *data, u32 size,
 			     struct ndef_record *record)
@@ -147,7 +148,8 @@
 
 static int wifi_filter(struct ndef_record *record)
 {
-	if (record->type_length != os_strlen(wifi_handover_type))
+	if (record->type == NULL ||
+	    record->type_length != os_strlen(wifi_handover_type))
 		return 0;
 	if (os_memcmp(record->type, wifi_handover_type,
 		      os_strlen(wifi_handover_type)) != 0)
@@ -170,85 +172,27 @@
 }
 
 
-struct wpabuf * ndef_build_wifi_hc(int begin)
+static int p2p_filter(struct ndef_record *record)
 {
-	struct wpabuf *hc, *carrier;
-
-	carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
-	if (carrier == NULL)
-		return NULL;
-	wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
-	wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
-	wpabuf_put_str(carrier, wifi_handover_type);
-
-	hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
-			       FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
-			       "0", 1, carrier);
-	wpabuf_free(carrier);
-
-	return hc;
+	if (record->type == NULL ||
+	    record->type_length != os_strlen(p2p_handover_type))
+		return 0;
+	if (os_memcmp(record->type, p2p_handover_type,
+		      os_strlen(p2p_handover_type)) != 0)
+		return 0;
+	return 1;
 }
 
 
-struct wpabuf * ndef_build_wifi_hr(void)
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
 {
-	struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
-	struct wpabuf *hc;
+	return ndef_parse_records(buf, p2p_filter);
+}
 
-	rn = wpabuf_alloc(2);
-	if (rn == NULL)
-		return NULL;
-	wpabuf_put_be16(rn, os_random() & 0xffff);
 
-	cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
-			       NULL, 0, rn);
-	wpabuf_free(rn);
-
-	if (cr == NULL)
-		return NULL;
-
-	ac_payload = wpabuf_alloc(4);
-	if (ac_payload == NULL) {
-		wpabuf_free(cr);
-		return NULL;
-	}
-	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
-	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
-	wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
-	wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
-
-	ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
-			       NULL, 0, ac_payload);
-	wpabuf_free(ac_payload);
-	if (ac == NULL) {
-		wpabuf_free(cr);
-		return NULL;
-	}
-
-	hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
-	if (hr_payload == NULL) {
-		wpabuf_free(cr);
-		wpabuf_free(ac);
-		return NULL;
-	}
-
-	wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
-	wpabuf_put_buf(hr_payload, cr);
-	wpabuf_put_buf(hr_payload, ac);
-	wpabuf_free(cr);
-	wpabuf_free(ac);
-
-	hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
-			       NULL, 0, hr_payload);
-	wpabuf_free(hr_payload);
-	if (hr == NULL)
-		return NULL;
-
-	hc = ndef_build_wifi_hc(0);
-	if (hc == NULL) {
-		wpabuf_free(hr);
-		return NULL;
-	}
-
-	return wpabuf_concat(hr, hc);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
+{
+	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+				 FLAG_TNF_RFC2046, p2p_handover_type,
+				 os_strlen(p2p_handover_type), NULL, 0, buf);
 }
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 22d7eea..b0f6887 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -18,6 +18,7 @@
 #ifdef CONFIG_WPS_TESTING
 int wps_version_number = 0x20;
 int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
 #endif /* CONFIG_WPS_TESTING */
 
 
@@ -58,6 +59,10 @@
 	}
 
 #ifdef CONFIG_WPS_NFC
+	if (cfg->pin == NULL &&
+	    cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+		data->dev_pw_id = cfg->dev_pw_id;
+
 	if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
 		/* Keep AP PIN as alternative Device Password */
 		data->alt_dev_pw_id = data->dev_pw_id;
@@ -84,7 +89,7 @@
 	if (cfg->pbc) {
 		/* Use special PIN '00000000' for PBC */
 		data->dev_pw_id = DEV_PW_PUSHBUTTON;
-		os_free(data->dev_password);
+		bin_clear_free(data->dev_password, data->dev_password_len);
 		data->dev_password = (u8 *) os_strdup("00000000");
 		if (data->dev_password == NULL) {
 			os_free(data);
@@ -117,7 +122,8 @@
 		data->new_ap_settings =
 			os_malloc(sizeof(*data->new_ap_settings));
 		if (data->new_ap_settings == NULL) {
-			os_free(data->dev_password);
+			bin_clear_free(data->dev_password,
+				       data->dev_password_len);
 			os_free(data);
 			return NULL;
 		}
@@ -133,6 +139,12 @@
 	data->use_psk_key = cfg->use_psk_key;
 	data->pbc_in_m1 = cfg->pbc_in_m1;
 
+	if (cfg->peer_pubkey_hash) {
+		os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+			  WPS_OOB_PUBKEY_HASH_LEN);
+		data->peer_pubkey_hash_set = 1;
+	}
+
 	return data;
 }
 
@@ -162,13 +174,12 @@
 	wpabuf_free(data->dh_pubkey_e);
 	wpabuf_free(data->dh_pubkey_r);
 	wpabuf_free(data->last_msg);
-	os_free(data->dev_password);
-	os_free(data->alt_dev_password);
-	os_free(data->new_psk);
+	bin_clear_free(data->dev_password, data->dev_password_len);
+	bin_clear_free(data->alt_dev_password, data->alt_dev_password_len);
+	bin_clear_free(data->new_psk, data->new_psk_len);
 	wps_device_data_free(&data->peer_dev);
-	os_free(data->new_ap_settings);
+	bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings));
 	dh5_free(data->dh_ctx);
-	os_free(data->nfc_pw_token);
 	os_free(data);
 }
 
@@ -501,13 +512,11 @@
 	    wps_build_assoc_state(NULL, ie) ||
 	    wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
 	    wps_build_dev_password_id(ie, pw_id) ||
-#ifdef CONFIG_WPS2
 	    wps_build_manufacturer(dev, ie) ||
 	    wps_build_model_name(dev, ie) ||
 	    wps_build_model_number(dev, ie) ||
 	    wps_build_dev_name(dev, ie) ||
 	    wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
-#endif /* CONFIG_WPS2 */
 	    wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
 	    ||
 	    wps_build_secondary_dev_type(dev, ie)
@@ -516,13 +525,6 @@
 		return NULL;
 	}
 
-#ifndef CONFIG_WPS2
-	if (dev->p2p && wps_build_dev_name(dev, ie)) {
-		wpabuf_free(ie);
-		return NULL;
-	}
-#endif /* CONFIG_WPS2 */
-
 	return wps_ie_encapsulate(ie);
 }
 
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 15137a8..192d283 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -42,7 +42,6 @@
  * @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
  *	this may be %NULL, if not used
  * @cred_attr_len: Length of cred_attr in octets
- * @ap_channel: AP channel
  */
 struct wps_credential {
 	u8 ssid[32];
@@ -55,7 +54,6 @@
 	u8 mac_addr[ETH_ALEN];
 	const u8 *cred_attr;
 	size_t cred_attr_len;
-	u16 ap_channel;
 };
 
 #define WPS_DEV_TYPE_LEN 8
@@ -183,6 +181,11 @@
 	 * PBC with the AP.
 	 */
 	int pbc_in_m1;
+
+	/**
+	 * peer_pubkey_hash - Peer public key hash or %NULL if not known
+	 */
+	const u8 *peer_pubkey_hash;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
@@ -665,6 +668,16 @@
 	u16 auth_types;
 
 	/**
+	 * encr_types - Current AP encryption type (WPS_ENCR_*)
+	 */
+	u16 ap_encr_type;
+
+	/**
+	 * ap_auth_type - Current AP authentication types (WPS_AUTH_*)
+	 */
+	u16 ap_auth_type;
+
+	/**
 	 * network_key - The current Network Key (PSK) or %NULL to generate new
 	 *
 	 * If %NULL, Registrar will generate per-device PSK. In addition, AP
@@ -801,7 +814,8 @@
 			    struct wps_credential *cred);
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
 				   const u8 *pubkey_hash, u16 pw_id,
-				   const u8 *dev_pw, size_t dev_pw_len);
+				   const u8 *dev_pw, size_t dev_pw_len,
+				   int pk_hash_provided_oob);
 int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
 					 const u8 *oob_dev_pw,
 					 size_t oob_dev_pw_len);
@@ -815,7 +829,8 @@
 int wps_pin_str_valid(const char *pin);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+				 int channel);
 int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
 const char * wps_ei_str(enum wps_error_indication ei);
@@ -839,6 +854,9 @@
 					      struct wps_credential *cred);
 struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
 					const u8 *addr);
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+					struct wps_context *wps, const u8 *uuid,
+					const u8 *addr, struct wpabuf *pubkey);
 
 int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
 char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
@@ -850,15 +868,27 @@
 				       const struct wpabuf *dev_pw);
 struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
 				    struct wpabuf *dev_pw);
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey);
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
 				  struct wpabuf **privkey,
 				  struct wpabuf **dev_pw);
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey,
+					   const u8 *bssid, int freq);
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+					       struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+					       int nfc_dev_pw_id,
+					       struct wpabuf *nfc_dh_pubkey,
+					       struct wpabuf *nfc_dev_pw);
 
 /* ndef.c */
 struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
 struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi_hc(int begin);
-struct wpabuf * ndef_build_wifi_hr(void);
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf);
 
 #ifdef CONFIG_WPS_STRICT
 int wps_validate_beacon(const struct wpabuf *wps_ie);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index ac9bb1e..b689357 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -38,8 +38,15 @@
 		wps->wps->dh_ctx = NULL;
 		pubkey = wpabuf_dup(wps->wps->dh_pubkey);
 #ifdef CONFIG_WPS_NFC
-	} else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
-		   wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+	} else if ((wps->dev_pw_id >= 0x10 ||
+		    wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) &&
+		   (wps->wps->ap ||
+		    (wps->wps->ap_nfc_dh_pubkey &&
+		     wps->wps->ap_nfc_dev_pw_id ==
+		     DEV_PW_NFC_CONNECTION_HANDOVER &&
+		     wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) &&
+		   (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id ||
+		    wps->wps->ap_nfc_dh_pubkey)) {
 		wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
 		if (wps->wps->ap_nfc_dh_privkey == NULL) {
 			wpa_printf(MSG_DEBUG,
@@ -118,6 +125,8 @@
 
 int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
 {
+	if (wpabuf_tailroom(msg) < 4 + WPS_UUID_LEN)
+		return -1;
 	wpa_printf(MSG_DEBUG, "WPS:  * UUID-E");
 	wpabuf_put_be16(msg, ATTR_UUID_E);
 	wpabuf_put_be16(msg, WPS_UUID_LEN);
@@ -183,6 +192,8 @@
 	 * backwards compatibility reasons. The real version negotiation is
 	 * done with Version2.
 	 */
+	if (wpabuf_tailroom(msg) < 5)
+		return -1;
 	wpa_printf(MSG_DEBUG, "WPS:  * Version (hardcoded 0x10)");
 	wpabuf_put_be16(msg, ATTR_VERSION);
 	wpabuf_put_be16(msg, 1);
@@ -194,9 +205,17 @@
 int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
 		      const u8 *auth_macs, size_t auth_macs_count)
 {
-#ifdef CONFIG_WPS2
 	u8 *len;
 
+#ifdef CONFIG_WPS_TESTING
+	if (WPS_VERSION == 0x10)
+		return 0;
+#endif /* CONFIG_WPS_TESTING */
+
+	if (wpabuf_tailroom(msg) <
+	    7 + 3 + (req_to_enroll ? 3 : 0) +
+	    (auth_macs ? 2 + auth_macs_count * ETH_ALEN : 0))
+		return -1;
 	wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
 	len = wpabuf_put(msg, 2); /* to be filled */
 	wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
@@ -226,10 +245,11 @@
 	}
 
 	WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
-#endif /* CONFIG_WPS2 */
 
 #ifdef CONFIG_WPS_TESTING
 	if (WPS_VERSION > 0x20) {
+		if (wpabuf_tailroom(msg) < 5)
+			return -1;
 		wpa_printf(MSG_DEBUG, "WPS:  * Extensibility Testing - extra "
 			   "attribute");
 		wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
@@ -274,9 +294,10 @@
 int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
 {
 	u16 auth_types = WPS_AUTH_TYPES;
-#ifdef CONFIG_WPS2
+	/* WPA/WPA2-Enterprise enrollment not supported through WPS */
+	auth_types &= ~WPS_AUTH_WPA;
+	auth_types &= ~WPS_AUTH_WPA2;
 	auth_types &= ~WPS_AUTH_SHARED;
-#endif /* CONFIG_WPS2 */
 	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
 	wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
 	wpabuf_put_be16(msg, 2);
@@ -288,9 +309,7 @@
 int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
 {
 	u16 encr_types = WPS_ENCR_TYPES;
-#ifdef CONFIG_WPS2
 	encr_types &= ~WPS_ENCR_WEP;
-#endif /* CONFIG_WPS2 */
 	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
 	wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
 	wpabuf_put_be16(msg, 2);
@@ -372,15 +391,31 @@
 	const u8 *addr[1];
 	u8 pubkey_hash[WPS_HASH_LEN];
 
+	wpa_printf(MSG_DEBUG, "WPS:  * OOB Device Password (dev_pw_id=%u)",
+		   dev_pw_id);
 	addr[0] = wpabuf_head(pubkey);
 	hash_len = wpabuf_len(pubkey);
 	sha256_vector(1, addr, &hash_len, pubkey_hash);
+#ifdef CONFIG_WPS_TESTING
+	if (wps_corrupt_pkhash) {
+		wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
+			    pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+		wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash");
+		pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++;
+	}
+#endif /* CONFIG_WPS_TESTING */
 
 	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
 	wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+	wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+		    pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
 	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
 	wpabuf_put_be16(msg, dev_pw_id);
-	wpabuf_put_data(msg, dev_pw, dev_pw_len);
+	if (dev_pw) {
+		wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
+				dev_pw, dev_pw_len);
+		wpabuf_put_data(msg, dev_pw, dev_pw_len);
+	}
 
 	return 0;
 }
@@ -428,3 +463,23 @@
 	wpabuf_put_data(msg, addr, ETH_ALEN);
 	return 0;
 }
+
+
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", rf_bands);
+	wpabuf_put_be16(msg, ATTR_RF_BANDS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, rf_bands);
+	return 0;
+}
+
+
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * AP Channel (%u)", ap_channel);
+	wpabuf_put_be16(msg, ATTR_AP_CHANNEL);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, ap_channel);
+	return 0;
+}
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 3999b1b..40bc1ad 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -59,6 +59,14 @@
 		}
 		attr->settings_delay_time = pos;
 		break;
+	case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
+				   len);
+			return -1;
+		}
+		attr->registrar_configuration_methods = pos;
+		break;
 	default:
 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
 			   "Extension subelement %u", id);
@@ -75,7 +83,7 @@
 	const u8 *end = pos + len;
 	u8 id, elen;
 
-	while (pos + 2 < end) {
+	while (pos + 2 <= end) {
 		id = *pos++;
 		elen = *pos++;
 		if (pos + elen > end)
@@ -263,10 +271,13 @@
 		attr->dev_password_id = pos;
 		break;
 	case ATTR_OOB_DEVICE_PASSWORD:
-		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
-		    WPS_OOB_DEVICE_PASSWORD_LEN) {
+		    WPS_OOB_DEVICE_PASSWORD_LEN ||
+		    (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		     WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+		     WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+		     DEV_PW_NFC_CONNECTION_HANDOVER)) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
 				   "Password length %u", len);
 			return -1;
@@ -410,22 +421,6 @@
 		}
 		attr->mac_addr = pos;
 		break;
-	case ATTR_KEY_PROVIDED_AUTO:
-		if (len != 1) {
-			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
-				   "Automatically length %u", len);
-			return -1;
-		}
-		attr->key_prov_auto = pos;
-		break;
-	case ATTR_802_1X_ENABLED:
-		if (len != 1) {
-			wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
-				   "length %u", len);
-			return -1;
-		}
-		attr->dot1x_enabled = pos;
-		break;
 	case ATTR_SELECTED_REGISTRAR:
 		if (len != 1) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
@@ -497,14 +492,6 @@
 		attr->network_key = pos;
 		attr->network_key_len = len;
 		break;
-	case ATTR_EAP_TYPE:
-		attr->eap_type = pos;
-		attr->eap_type_len = len;
-		break;
-	case ATTR_EAP_IDENTITY:
-		attr->eap_identity = pos;
-		attr->eap_identity_len = len;
-		break;
 	case ATTR_AP_SETUP_LOCKED:
 		if (len != 1) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index 88e51a4..82c4739 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -47,8 +47,6 @@
 	const u8 *network_idx; /* 1 octet */
 	const u8 *network_key_idx; /* 1 octet */
 	const u8 *mac_addr; /* ETH_ALEN (6) octets */
-	const u8 *key_prov_auto; /* 1 octet (Bool) */
-	const u8 *dot1x_enabled; /* 1 octet (Bool) */
 	const u8 *selected_registrar; /* 1 octet (Bool) */
 	const u8 *request_type; /* 1 octet */
 	const u8 *response_type; /* 1 octet */
@@ -57,6 +55,7 @@
 	const u8 *network_key_shareable; /* 1 octet (Bool) */
 	const u8 *request_to_enroll; /* 1 octet (Bool) */
 	const u8 *ap_channel; /* 2 octets */
+	const u8 *registrar_configuration_methods; /* 2 octets */
 
 	/* variable length fields */
 	const u8 *manufacturer;
@@ -77,10 +76,6 @@
 	size_t ssid_len;
 	const u8 *network_key; /* <= 64 octets */
 	size_t network_key_len;
-	const u8 *eap_type; /* <= 8 octets */
-	size_t eap_type_len;
-	const u8 *eap_identity; /* <= 64 octets */
-	size_t eap_identity_len;
 	const u8 *authorized_macs; /* <= 30 octets */
 	size_t authorized_macs_len;
 	const u8 *sec_dev_type_list; /* <= 128 octets */
diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c
index b81f106..eadb22f 100644
--- a/src/wps/wps_attr_process.c
+++ b/src/wps/wps_attr_process.c
@@ -41,7 +41,7 @@
 	len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
 
-	if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+	if (os_memcmp_const(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
 		return -1;
 	}
@@ -71,7 +71,7 @@
 	}
 
 	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
-	if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+	if (os_memcmp_const(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
 		return -1;
 	}
@@ -207,70 +207,6 @@
 }
 
 
-static int wps_process_cred_eap_type(struct wps_credential *cred,
-				     const u8 *eap_type, size_t eap_type_len)
-{
-	if (eap_type == NULL)
-		return 0; /* optional attribute */
-
-	wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
-
-	return 0;
-}
-
-
-static int wps_process_cred_eap_identity(struct wps_credential *cred,
-					 const u8 *identity,
-					 size_t identity_len)
-{
-	if (identity == NULL)
-		return 0; /* optional attribute */
-
-	wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
-			  identity, identity_len);
-
-	return 0;
-}
-
-
-static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
-					  const u8 *key_prov_auto)
-{
-	if (key_prov_auto == NULL)
-		return 0; /* optional attribute */
-
-	wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
-		   *key_prov_auto);
-
-	return 0;
-}
-
-
-static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
-					   const u8 *dot1x_enabled)
-{
-	if (dot1x_enabled == NULL)
-		return 0; /* optional attribute */
-
-	wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
-
-	return 0;
-}
-
-
-static int wps_process_cred_ap_channel(struct wps_credential *cred,
-				       const u8 *ap_channel)
-{
-	if (ap_channel == NULL)
-		return 0; /* optional attribute */
-
-	cred->ap_channel = WPA_GET_BE16(ap_channel);
-	wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel);
-
-	return 0;
-}
-
-
 static int wps_workaround_cred_key(struct wps_credential *cred)
 {
 	if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
@@ -310,14 +246,7 @@
 	    wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
 	    wps_process_cred_network_key(cred, attr->network_key,
 					 attr->network_key_len) ||
-	    wps_process_cred_mac_addr(cred, attr->mac_addr) ||
-	    wps_process_cred_eap_type(cred, attr->eap_type,
-				      attr->eap_type_len) ||
-	    wps_process_cred_eap_identity(cred, attr->eap_identity,
-					  attr->eap_identity_len) ||
-	    wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
-	    wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) ||
-	    wps_process_cred_ap_channel(cred, attr->ap_channel))
+	    wps_process_cred_mac_addr(cred, attr->mac_addr))
 		return -1;
 
 	return wps_workaround_cred_key(cred);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 4b431ad..a282348 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -9,6 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "crypto/dh_group5.h"
@@ -16,6 +18,7 @@
 #include "crypto/sha256.h"
 #include "crypto/random.h"
 #include "wps_i.h"
+#include "wps_dev_attr.h"
 
 
 void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
@@ -349,7 +352,8 @@
 
 #ifdef CONFIG_WPS_OOB
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+				 int channel)
 {
 	struct wps_data data;
 	struct wpabuf *plain;
@@ -365,8 +369,10 @@
 	data.wps = wps;
 	data.auth_type = wps->auth_types;
 	data.encr_type = wps->encr_types;
-	if (wps_build_version(plain) ||
-	    wps_build_cred(&data, plain) ||
+	if (wps_build_cred(&data, plain) ||
+	    (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
+	    (channel && wps_build_ap_channel(plain, channel)) ||
+	    wps_build_mac_addr(plain, wps->dev.mac_addr) ||
 	    wps_build_wfa_ext(plain, 0, NULL, 0)) {
 		os_free(data.new_psk);
 		wpabuf_free(plain);
@@ -412,8 +418,7 @@
 	if (data == NULL)
 		return NULL;
 
-	if (wps_build_version(data) ||
-	    wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+	if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
 				 wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
 	    wps_build_wfa_ext(data, 0, NULL, 0)) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
@@ -526,9 +531,7 @@
 	if (str == NULL) {
 		/* Default to enabling methods based on build configuration */
 		methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS2
 		methods |= WPS_CONFIG_VIRT_DISPLAY;
-#endif /* CONFIG_WPS2 */
 #ifdef CONFIG_WPS_NFC
 		methods |= WPS_CONFIG_NFC_INTERFACE;
 #endif /* CONFIG_WPS_NFC */
@@ -549,7 +552,6 @@
 			methods |= WPS_CONFIG_PUSHBUTTON;
 		if (os_strstr(str, "keypad"))
 			methods |= WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS2
 		if (os_strstr(str, "virtual_display"))
 			methods |= WPS_CONFIG_VIRT_DISPLAY;
 		if (os_strstr(str, "physical_display"))
@@ -558,7 +560,6 @@
 			methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 		if (os_strstr(str, "physical_push_button"))
 			methods |= WPS_CONFIG_PHY_PUSHBUTTON;
-#endif /* CONFIG_WPS2 */
 	}
 
 	return methods;
@@ -636,12 +637,36 @@
 }
 
 
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
+{
+	struct wpabuf *priv = NULL, *pub = NULL;
+	void *dh_ctx;
+
+	dh_ctx = dh5_init(&priv, &pub);
+	if (dh_ctx == NULL)
+		return -1;
+	pub = wpabuf_zeropad(pub, 192);
+	if (pub == NULL) {
+		wpabuf_free(priv);
+		return -1;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
+	dh5_free(dh_ctx);
+
+	wpabuf_free(*pubkey);
+	*pubkey = pub;
+	wpabuf_free(*privkey);
+	*privkey = priv;
+
+	return 0;
+}
+
+
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
 				  struct wpabuf **privkey,
 				  struct wpabuf **dev_pw)
 {
-	struct wpabuf *priv = NULL, *pub = NULL, *pw;
-	void *dh_ctx;
+	struct wpabuf *pw;
 	u16 val;
 
 	pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
@@ -655,22 +680,223 @@
 		return NULL;
 	}
 
-	dh_ctx = dh5_init(&priv, &pub);
-	if (dh_ctx == NULL) {
+	if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
 		wpabuf_free(pw);
 		return NULL;
 	}
-	dh5_free(dh_ctx);
 
 	*id = 0x10 + val % 0xfff0;
-	wpabuf_free(*pubkey);
-	*pubkey = pub;
-	wpabuf_free(*privkey);
-	*privkey = priv;
 	wpabuf_free(*dev_pw);
 	*dev_pw = pw;
 
 	return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
 }
 
+
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey)
+{
+	struct wpabuf *msg;
+	void *len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover request");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+	len = wpabuf_put(msg, 2);
+
+	if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+	return msg;
+}
+
+
+static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select",
+			  wps->ssid, wps->ssid_len);
+	wpabuf_put_be16(msg, ATTR_SSID);
+	wpabuf_put_be16(msg, wps->ssid_len);
+	wpabuf_put_data(msg, wps->ssid, wps->ssid_len);
+	return 0;
+}
+
+
+static int wps_build_ap_freq(struct wpabuf *msg, int freq)
+{
+	enum hostapd_hw_mode mode;
+	u8 channel, rf_band;
+	u16 ap_channel;
+
+	if (freq <= 0)
+		return 0;
+
+	mode = ieee80211_freq_to_chan(freq, &channel);
+	if (mode == NUM_HOSTAPD_MODES)
+		return 0; /* Unknown channel */
+
+	if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B)
+		rf_band = WPS_RF_24GHZ;
+	else if (mode == HOSTAPD_MODE_IEEE80211A)
+		rf_band = WPS_RF_50GHZ;
+	else
+		return 0; /* Unknown band */
+	ap_channel = channel;
+
+	if (wps_build_rf_bands_attr(msg, rf_band) ||
+	    wps_build_ap_channel(msg, ap_channel))
+		return -1;
+
+	return 0;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey,
+					   const u8 *bssid, int freq)
+{
+	struct wpabuf *msg;
+	void *len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover select");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+	len = wpabuf_put(msg, 2);
+
+	if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_ssid(msg, ctx) ||
+	    wps_build_ap_freq(msg, freq) ||
+	    (bssid && wps_build_mac_addr(msg, bssid)) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+	return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+					       struct wpabuf *nfc_dh_pubkey)
+{
+	struct wpabuf *msg;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover request (P2P)");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+
+	if (wps_build_manufacturer(&ctx->dev, msg) ||
+	    wps_build_model_name(&ctx->dev, msg) ||
+	    wps_build_model_number(&ctx->dev, msg) ||
+	    wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
+	    wps_build_serial_number(&ctx->dev, msg) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+					       int nfc_dev_pw_id,
+					       struct wpabuf *nfc_dh_pubkey,
+					       struct wpabuf *nfc_dev_pw)
+{
+	struct wpabuf *msg;
+	const u8 *dev_pw;
+	size_t dev_pw_len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover select (P2P)");
+
+	if (nfc_dh_pubkey == NULL ||
+	    (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+	     nfc_dev_pw == NULL)) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+
+	if (nfc_dev_pw) {
+		dev_pw = wpabuf_head(nfc_dev_pw);
+		dev_pw_len = wpabuf_len(nfc_dev_pw);
+	} else {
+		dev_pw = NULL;
+		dev_pw_len = 0;
+	}
+
+	if (wps_build_manufacturer(&ctx->dev, msg) ||
+	    wps_build_model_name(&ctx->dev, msg) ||
+	    wps_build_model_number(&ctx->dev, msg) ||
+	    wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey,
+				 dev_pw, dev_pw_len) ||
+	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
+	    wps_build_serial_number(&ctx->dev, msg) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 3421ac5..f483e2e 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -13,15 +13,12 @@
 
 extern int wps_version_number;
 extern int wps_testing_dummy_cred;
+extern int wps_corrupt_pkhash;
 #define WPS_VERSION wps_version_number
 
 #else /* CONFIG_WPS_TESTING */
 
-#ifdef CONFIG_WPS2
 #define WPS_VERSION 0x20
-#else /* CONFIG_WPS2 */
-#define WPS_VERSION 0x10
-#endif /* CONFIG_WPS2 */
 
 #endif /* CONFIG_WPS_TESTING */
 
@@ -145,7 +142,8 @@
 	WFA_ELEM_AUTHORIZEDMACS = 0x01,
 	WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
 	WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
-	WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
+	WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
+	WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
 };
 
 /* Device Password ID */
@@ -155,7 +153,8 @@
 	DEV_PW_MACHINE_SPECIFIED = 0x0002,
 	DEV_PW_REKEY = 0x0003,
 	DEV_PW_PUSHBUTTON = 0x0004,
-	DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+	DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
+	DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007
 };
 
 /* Message Type */
@@ -180,7 +179,7 @@
 /* Authentication Type Flags */
 #define WPS_AUTH_OPEN 0x0001
 #define WPS_AUTH_WPAPSK 0x0002
-#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_SHARED 0x0004 /* deprecated */
 #define WPS_AUTH_WPA 0x0008
 #define WPS_AUTH_WPA2 0x0010
 #define WPS_AUTH_WPA2PSK 0x0020
@@ -189,7 +188,7 @@
 
 /* Encryption Type Flags */
 #define WPS_ENCR_NONE 0x0001
-#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_WEP 0x0002 /* deprecated */
 #define WPS_ENCR_TKIP 0x0004
 #define WPS_ENCR_AES 0x0008
 #define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
@@ -215,7 +214,9 @@
 	WPS_CFG_SETUP_LOCKED = 15,
 	WPS_CFG_MSG_TIMEOUT = 16,
 	WPS_CFG_REG_SESS_TIMEOUT = 17,
-	WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+	WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18,
+	WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
+	WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
 };
 
 /* Vendor specific Error Indication for WPS event messages */
@@ -241,12 +242,10 @@
 #define WPS_CONFIG_NFC_INTERFACE 0x0040
 #define WPS_CONFIG_PUSHBUTTON 0x0080
 #define WPS_CONFIG_KEYPAD 0x0100
-#ifdef CONFIG_WPS2
 #define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
 #define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
 #define WPS_CONFIG_VIRT_DISPLAY 0x2008
 #define WPS_CONFIG_PHY_DISPLAY 0x4008
-#endif /* CONFIG_WPS2 */
 
 /* Connection Type Flags */
 #define WPS_CONN_ESS 0x01
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index 1b12b5a..0d01211 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -85,8 +85,7 @@
 }
 
 
-static int wps_build_serial_number(struct wps_device_data *dev,
-				   struct wpabuf *msg)
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
 {
 	size_t len;
 	wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
@@ -220,11 +219,7 @@
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
 		       u8 rf_band)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", dev->rf_bands);
-	wpabuf_put_be16(msg, ATTR_RF_BANDS);
-	wpabuf_put_be16(msg, 1);
-	wpabuf_put_u8(msg, rf_band ? rf_band : dev->rf_bands);
-	return 0;
+	return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
 }
 
 
@@ -409,25 +404,6 @@
 }
 
 
-void wps_device_data_dup(struct wps_device_data *dst,
-			 const struct wps_device_data *src)
-{
-	if (src->device_name)
-		dst->device_name = os_strdup(src->device_name);
-	if (src->manufacturer)
-		dst->manufacturer = os_strdup(src->manufacturer);
-	if (src->model_name)
-		dst->model_name = os_strdup(src->model_name);
-	if (src->model_number)
-		dst->model_number = os_strdup(src->model_number);
-	if (src->serial_number)
-		dst->serial_number = os_strdup(src->serial_number);
-	os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
-	dst->os_version = src->os_version;
-	dst->rf_bands = src->rf_bands;
-}
-
-
 void wps_device_data_free(struct wps_device_data *dev)
 {
 	os_free(dev->device_name);
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index 0158cdc..c9034ad 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -14,6 +14,7 @@
 int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
@@ -29,8 +30,6 @@
 			     struct wps_parse_attr *attr);
 int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
 int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
-void wps_device_data_dup(struct wps_device_data *dst,
-			 const struct wps_device_data *src);
 void wps_device_data_free(struct wps_device_data *dev);
 int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index d02ba30..f7d41b4 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -130,10 +130,8 @@
 		 * workaround.
 		 */
 		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
 		config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 				    WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
 	}
 
 	if (wps_build_version(msg) ||
@@ -243,54 +241,53 @@
 
 static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
 {
-	u16 auth_type = wps->wps->auth_types;
-
-	/* Select the best authentication type */
-	if (auth_type & WPS_AUTH_WPA2PSK)
-		auth_type = WPS_AUTH_WPA2PSK;
-	else if (auth_type & WPS_AUTH_WPAPSK)
-		auth_type = WPS_AUTH_WPAPSK;
-	else if (auth_type & WPS_AUTH_OPEN)
-		auth_type = WPS_AUTH_OPEN;
-	else if (auth_type & WPS_AUTH_SHARED)
-		auth_type = WPS_AUTH_SHARED;
-
-	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)",
+		   wps->wps->ap_auth_type);
 	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
 	wpabuf_put_be16(msg, 2);
-	wpabuf_put_be16(msg, auth_type);
+	wpabuf_put_be16(msg, wps->wps->ap_auth_type);
 	return 0;
 }
 
 
 static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
 {
-	u16 encr_type = wps->wps->encr_types;
-
-	/* Select the best encryption type */
-	if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
-		if (encr_type & WPS_ENCR_AES)
-			encr_type = WPS_ENCR_AES;
-		else if (encr_type & WPS_ENCR_TKIP)
-			encr_type = WPS_ENCR_TKIP;
-	} else {
-		if (encr_type & WPS_ENCR_WEP)
-			encr_type = WPS_ENCR_WEP;
-		else if (encr_type & WPS_ENCR_NONE)
-			encr_type = WPS_ENCR_NONE;
-	}
-
-	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)",
+		   wps->wps->ap_encr_type);
 	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
 	wpabuf_put_be16(msg, 2);
-	wpabuf_put_be16(msg, encr_type);
+	wpabuf_put_be16(msg, wps->wps->ap_encr_type);
 	return 0;
 }
 
 
 static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * Network Key");
+	if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
+	    wps->wps->network_key_len == 0) {
+		char hex[65];
+		u8 psk[32];
+		/* Generate a random per-device PSK */
+		if (random_get_bytes(psk, sizeof(psk)) < 0)
+			return -1;
+		wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+				psk, sizeof(psk));
+		wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
+			   (unsigned int) wps->new_psk_len * 2);
+		wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
+		wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+		wpabuf_put_be16(msg, sizeof(psk) * 2);
+		wpabuf_put_data(msg, hex, sizeof(psk) * 2);
+		if (wps->wps->registrar) {
+			wps_cb_new_psk(wps->wps->registrar,
+				       wps->peer_dev.mac_addr,
+				       wps->p2p_dev_addr, psk, sizeof(psk));
+		}
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
+		   (unsigned int) wps->wps->network_key_len);
 	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
 	wpabuf_put_be16(msg, wps->wps->network_key_len);
 	wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
@@ -310,6 +307,9 @@
 
 static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
 {
+	const u8 *start, *end;
+	int ret;
+
 	if (wps->wps->ap_settings) {
 		wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
 		wpabuf_put_data(plain, wps->wps->ap_settings,
@@ -317,11 +317,19 @@
 		return 0;
 	}
 
-	return wps_build_cred_ssid(wps, plain) ||
+	wpa_printf(MSG_DEBUG, "WPS:  * AP Settings based on current configuration");
+	start = wpabuf_put(plain, 0);
+	ret = wps_build_cred_ssid(wps, plain) ||
 		wps_build_cred_mac_addr(wps, plain) ||
 		wps_build_cred_auth_type(wps, plain) ||
 		wps_build_cred_encr_type(wps, plain) ||
 		wps_build_cred_network_key(wps, plain);
+	end = wpabuf_put(plain, 0);
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
+			start, end - start);
+
+	return ret;
 }
 
 
@@ -514,6 +522,24 @@
 		return -1;
 	}
 
+	if (wps->peer_pubkey_hash_set) {
+		u8 hash[WPS_HASH_LEN];
+		sha256_vector(1, &pk, &pk_len, hash);
+		if (os_memcmp_const(hash, wps->peer_pubkey_hash,
+				    WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+			wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+			wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+				    pk, pk_len);
+			wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+				    "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+			wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+				    wps->peer_pubkey_hash,
+				    WPS_OOB_PUBKEY_HASH_LEN);
+			wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+			return -1;
+		}
+	}
+
 	wpabuf_free(wps->dh_pubkey_r);
 	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
 	if (wps->dh_pubkey_r == NULL)
@@ -579,7 +605,7 @@
 	len[3] = wpabuf_len(wps->dh_pubkey_r);
 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-	if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+	if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
 			   "not match with the pre-committed value");
 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
@@ -619,7 +645,7 @@
 	len[3] = wpabuf_len(wps->dh_pubkey_r);
 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-	if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+	if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
 			   "not match with the pre-committed value");
 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
@@ -670,7 +696,6 @@
 #endif /* CONFIG_WPS_STRICT */
 	}
 
-#ifdef CONFIG_WPS2
 	if (!(wps->cred.encr_type &
 	      (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
 		if (wps->cred.encr_type & WPS_ENCR_WEP) {
@@ -684,7 +709,6 @@
 			   "invalid encr_type 0x%x", wps->cred.encr_type);
 		return -1;
 	}
-#endif /* CONFIG_WPS2 */
 
 	if (wps->wps->cred_cb) {
 		wps->cred.cred_attr = cred - 4;
@@ -771,7 +795,6 @@
 #endif /* CONFIG_WPS_STRICT */
 	}
 
-#ifdef CONFIG_WPS2
 	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
 	{
 		if (cred.encr_type & WPS_ENCR_WEP) {
@@ -785,7 +808,6 @@
 			   "invalid encr_type 0x%x", cred.encr_type);
 		return -1;
 	}
-#endif /* CONFIG_WPS2 */
 
 #ifdef CONFIG_WPS_STRICT
 	if (wps2) {
@@ -802,7 +824,6 @@
 	}
 #endif /* CONFIG_WPS_STRICT */
 
-#ifdef CONFIG_WPS2
 	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
 	{
 		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
@@ -816,7 +837,6 @@
 			   "WPAPSK+WPA2PSK");
 		cred.auth_type |= WPS_AUTH_WPA2PSK;
 	}
-#endif /* CONFIG_WPS2 */
 
 	if (wps->wps->cred_cb) {
 		cred.cred_attr = wpabuf_head(attrs);
@@ -864,9 +884,15 @@
 	wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
 		   "ID from %u to %u", wps->dev_pw_id, id);
 
+	if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Workaround - ignore PBC-to-PIN change");
+		return 0;
+	}
+
 	if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
 		wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
-		os_free(wps->dev_password);
+		bin_clear_free(wps->dev_password, wps->dev_password_len);
 		wps->dev_pw_id = wps->alt_dev_pw_id;
 		wps->dev_password = wps->alt_dev_password;
 		wps->dev_password_len = wps->alt_dev_password_len;
@@ -923,6 +949,38 @@
 		return WPS_CONTINUE;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (wps->peer_pubkey_hash_set) {
+		struct wpabuf *decrypted;
+		struct wps_parse_attr eattr;
+
+		decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+						      attr->encr_settings_len);
+		if (decrypted == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
+				   "Encrypted Settings attribute");
+			wps->state = SEND_WSC_NACK;
+			return WPS_CONTINUE;
+		}
+
+		wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
+			   "Settings attribute");
+		if (wps_parse_msg(decrypted, &eattr) < 0 ||
+		    wps_process_key_wrap_auth(wps, decrypted,
+					      eattr.key_wrap_auth) ||
+		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
+				      eattr.num_cred, attr->version2 != NULL)) {
+			wpabuf_free(decrypted);
+			wps->state = SEND_WSC_NACK;
+			return WPS_CONTINUE;
+		}
+		wpabuf_free(decrypted);
+
+		wps->state = WPS_MSG_DONE;
+		return WPS_CONTINUE;
+	}
+#endif /* CONFIG_WPS_NFC */
+
 	wps->state = SEND_M3;
 	return WPS_CONTINUE;
 }
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 5694997..078ff72 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -185,10 +185,8 @@
 	dl_list_del(&ap->list);
 	wps_er_ap_free(ap);
 
-	if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) {
-		eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
+	if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing))
 		wps_er_deinit_finish(er, NULL);
-	}
 }
 
 
@@ -581,12 +579,15 @@
 	wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
 
 	ap->udn = xml_get_first_item(data, "UDN");
-	wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
-	pos = os_strstr(ap->udn, "uuid:");
-	if (pos) {
-		pos += 5;
-		if (uuid_str2bin(pos, ap->uuid) < 0)
-			wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN");
+	if (ap->udn) {
+		wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
+		pos = os_strstr(ap->udn, "uuid:");
+		if (pos) {
+			pos += 5;
+			if (uuid_str2bin(pos, ap->uuid) < 0)
+				wpa_printf(MSG_DEBUG,
+					   "WPS ER: Invalid UUID in UDN");
+		}
 	}
 
 	ap->upc = xml_get_first_item(data, "UPC");
@@ -1347,9 +1348,19 @@
 	struct wps_er *er = eloop_data;
 	void (*deinit_done_cb)(void *ctx);
 	void *deinit_done_ctx;
+	struct wps_er_ap *ap, *tmp;
 
 	wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit");
 
+	dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap,
+			      list) {
+		wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it",
+			   inet_ntoa(ap->addr), ap->location);
+		dl_list_del(&ap->list);
+		wps_er_ap_free(ap);
+	}
+
+	eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
 	deinit_done_cb = er->deinit_done_cb;
 	deinit_done_ctx = er->deinit_done_ctx;
 	os_free(er->ip_addr_text);
@@ -1482,11 +1493,9 @@
 
 static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
 {
-#ifdef CONFIG_WPS2
 	wpabuf_put_be16(msg, ATTR_UUID_R);
 	wpabuf_put_be16(msg, WPS_UUID_LEN);
 	wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
-#endif /* CONFIG_WPS2 */
 	return 0;
 }
 
@@ -1498,9 +1507,7 @@
 	struct wps_er_ap *ap;
 	struct wps_registrar *reg = er->wps->registrar;
 	const u8 *auth_macs;
-#ifdef CONFIG_WPS2
 	u8 bcast[ETH_ALEN];
-#endif /* CONFIG_WPS2 */
 	size_t count;
 	union wps_event_data data;
 
@@ -1514,13 +1521,11 @@
 		return;
 
 	auth_macs = wps_authorized_macs(reg, &count);
-#ifdef CONFIG_WPS2
 	if (count == 0) {
 		os_memset(bcast, 0xff, ETH_ALEN);
 		auth_macs = bcast;
 		count = 1;
 	}
-#endif /* CONFIG_WPS2 */
 
 	if (wps_build_version(msg) ||
 	    wps_er_build_selected_registrar(msg, sel_reg) ||
@@ -2032,8 +2037,7 @@
 	os_memset(&data, 0, sizeof(data));
 	data.wps = wps;
 	data.use_cred = cred;
-	if (wps_build_version(ret) ||
-	    wps_build_cred(&data, ret) ||
+	if (wps_build_cred(&data, ret) ||
 	    wps_build_wfa_ext(ret, 0, NULL, 0)) {
 		wpabuf_free(ret);
 		return NULL;
@@ -2063,4 +2067,29 @@
 	return wps_er_config_token_from_cred(er->wps, ap->ap_settings);
 }
 
+
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+					struct wps_context *wps, const u8 *uuid,
+					const u8 *addr, struct wpabuf *pubkey)
+{
+	struct wps_er_ap *ap;
+
+	if (er == NULL)
+		return NULL;
+
+	ap = wps_er_ap_get(er, NULL, uuid, addr);
+	if (ap == NULL)
+		return NULL;
+	if (ap->ap_settings == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+			   "selected AP");
+		return NULL;
+	}
+
+	os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len);
+	wps->ssid_len = ap->ap_settings->ssid_len;
+
+	return wps_build_nfc_handover_sel(wps, pubkey, addr, 0);
+}
+
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index dac1250..f7154f8 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -75,6 +75,9 @@
 	size_t alt_dev_password_len;
 	u16 alt_dev_pw_id;
 
+	u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	int peer_pubkey_hash_set;
+
 	/**
 	 * request_type - Request Type attribute from (Re)AssocReq
 	 */
@@ -173,6 +176,8 @@
 			 size_t dev_pw_len);
 struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
 int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr);
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands);
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel);
 
 /* wps_attr_process.c */
 int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
@@ -207,5 +212,7 @@
 			      const u8 *addr, const u8 *uuid_e);
 void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
 				       struct wps_nfc_pw_token *token);
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+		   const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len);
 
 #endif /* WPS_I_H */
diff --git a/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c
new file mode 100644
index 0000000..6800e86
--- /dev/null
+++ b/src/wps/wps_module_tests.c
@@ -0,0 +1,337 @@
+/*
+ * WPS module tests
+ * Copyright (c) 2014, 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 "wps_attr_parse.h"
+
+struct wps_attr_parse_test {
+	const char *data;
+	int result;
+	int extra;
+};
+
+struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
+	/* Empty message */
+	{ "", 0, 0 },
+	/* Truncated attribute header */
+	{ "10", -1, 0 },
+	{ "1010", -1, 0 },
+	{ "101000", -1, 0 },
+	/* Attribute overflow */
+	{ "10100001", -1, 0 },
+#ifdef CONFIG_WPS_STRICT
+	{ "10270000001057000101", -1, 0 },
+	{ "1027000010570001010000000000", -1, 0 },
+#else /* CONFIG_WPS_STRICT */
+	/* Network Key workaround */
+	{ "10270000001057000101", 0, 1 },
+	{ "10230000001057000101", -1, 0 },
+	{ "10270000101057000101", -1, 0 },
+	/* Mac OS X 10.6 padding workaround */
+	{ "1027000010570001010000000000", 0, 1 },
+	{ "1027000010570001010000000000000001000000", -1, 0 },
+#endif /* CONFIG_WPS_STRICT */
+	/* Version */
+	{ "104a000110", 0, 0 },
+	{ "104a0000", -1, 0 },
+	/* Message Type */
+	{ "1022000101", 0, 0 },
+	{ "10220000", -1, 0 },
+	/* Enrollee Nonce */
+	{ "101a001000112233445566778899aabbccddeeff", 0, 0 },
+	{ "101a00111122334455667788990011223344556677", -1, 0 },
+	/* Registrar Nonce */
+	{ "1039001000112233445566778899aabbccddeeff", 0, 0 },
+	{ "103900111122334455667788990011223344556677", -1, 0 },
+	/* UUID-E */
+	{ "1047001000112233445566778899aabbccddeeff", 0, 0 },
+	{ "10470000", -1, 0 },
+	{ "104700111122334455667788990011223344556677", -1, 0 },
+	/* UUID-R */
+	{ "1048001000112233445566778899aabbccddeeff", 0, 0 },
+	{ "10480000", -1, 0 },
+	{ "104800111122334455667788990011223344556677", -1, 0 },
+	/* Auth Type Flags */
+	{ "100400021122", 0, 0 },
+	{ "10040001ff", -1, 0 },
+	/* Encr Type Flags */
+	{ "101000021122", 0, 0 },
+	{ "10100001ff", -1, 0 },
+	/* Connection Type Flags */
+	{ "100d0001ff", 0, 0 },
+	{ "100d0002ffff", -1, 0 },
+	/* Config Methods */
+	{ "10080002ffff", 0, 0 },
+	{ "10080001ff", -1, 0 },
+	/* Selected Registrar Config Methods */
+	{ "10530002ffff", 0, 0 },
+	{ "10530001ff", -1, 0 },
+	/* Primary Device Type */
+	{ "105400081122334455667788", 0, 0 },
+	{ "105400111122334455667788990011223344556677", -1, 0 },
+	/* RF Bands */
+	{ "103c0001ff", 0, 0 },
+	{ "103c0002ffff", -1, 0 },
+	/* Association State */
+	{ "10020002ffff", 0, 0 },
+	{ "10020001ff", -1, 0 },
+	/* Config Error */
+	{ "100900020001", 0, 0 },
+	{ "10090001ff", -1, 0 },
+	/* Device Password ID */
+	{ "101200020004", 0, 0 },
+	{ "10120001ff", -1, 0 },
+	/* OOB Device Password */
+	{ "102c001611223344556677889900112233445566778899000007", 0, 0 },
+	{ "102c0036112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344", 0, 0 },
+	{ "102c0001ff", -1, 0 },
+	{ "102c003711223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455", -1, 0 },
+	{ "102c002511223344556677889900112233445566778899001122334455667788990011223344556677", -1, 0 },
+	/* OS Version */
+	{ "102d000411223344", 0, 0 },
+	{ "102d00111122334455667788990011223344556677", -1, 0 },
+	/* WPS State */
+	{ "1044000101", 0, 0 },
+	{ "10440002ffff", -1, 0 },
+	/* Authenticator */
+	{ "100500081122334455667788", 0, 0 },
+	{ "10050000", -1, 0 },
+	{ "100500111122334455667788990011223344556677", -1, 0 },
+	/* R-Hash1 */
+	{ "103d00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+	{ "103d0000", -1, 0 },
+	{ "103d0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+	/* R-Hash2 */
+	{ "103e00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+	{ "103e0000", -1, 0 },
+	{ "103e0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+	/* E-Hash1 */
+	{ "101400201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+	{ "10140000", -1, 0 },
+	{ "10140021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+	/* E-Hash2 */
+	{ "101500201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+	{ "10150000", -1, 0 },
+	{ "10150021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+	/* R-SNonce1 */
+	{ "103f001011223344556677889900112233445566", 0, 0 },
+	{ "103f0000", -1, 0 },
+	{ "103f00111122334455667788990011223344556677", -1, 0 },
+	/* R-SNonce2 */
+	{ "1040001011223344556677889900112233445566", 0, 0 },
+	{ "10400000", -1, 0 },
+	{ "104000111122334455667788990011223344556677", -1, 0 },
+	/* E-SNonce1 */
+	{ "1016001011223344556677889900112233445566", 0, 0 },
+	{ "10160000", -1, 0 },
+	{ "101600111122334455667788990011223344556677", -1, 0 },
+	/* E-SNonce2 */
+	{ "1017001011223344556677889900112233445566", 0, 0 },
+	{ "10170000", -1, 0 },
+	{ "101700111122334455667788990011223344556677", -1, 0 },
+	/* Key Wrap Authenticator */
+	{ "101e00081122334455667788", 0, 0 },
+	{ "101e0000", -1, 0 },
+	{ "101e0009112233445566778899", -1, 0 },
+	/* Authentication Type */
+	{ "100300020001", 0, 0 },
+	{ "10030001ff", -1, 0 },
+	/* Encryption Type */
+	{ "100f00020001", 0, 0 },
+	{ "100f0001ff", -1, 0 },
+	/* Network Index */
+	{ "1026000101", 0, 0 },
+	{ "10260002ffff", -1, 0 },
+	/* Network Key Index */
+	{ "1028000101", 0, 3 },
+	{ "10280002ffff", -1, 0 },
+	/* MAC Address */
+	{ "10200006112233445566", 0, 0 },
+	{ "10200000", -1, 0 },
+	{ "1020000711223344556677", -1, 0 },
+	/* Selected Registrar */
+	{ "1041000101", 0, 0 },
+	{ "10410002ffff", -1, 0 },
+	/* Request Type */
+	{ "103a000101", 0, 0 },
+	{ "103a0002ffff", -1, 0 },
+	/* Response Type */
+	{ "103b000101", 0, 0 },
+	{ "103b0002ffff", -1, 0 },
+	/* Manufacturer */
+	{ "10210000", 0, 0 },
+	/* Model Name */
+	{ "10230000", 0, 0 },
+	/* Model Number */
+	{ "10240000", 0, 0 },
+	/* Serial Number */
+	{ "10420000", 0, 0 },
+	/* Device Name */
+	{ "10110000", 0, 0 },
+	/* Public Key */
+	{ "10320000", 0, 0 },
+	/* Enc Settings */
+	{ "10180000", 0, 0 },
+	/* SSID */
+	{ "10450000", 0, 0 },
+	/* AP Setup Locked */
+	{ "1057000101", 0, 0 },
+	{ "10570002ffff", -1, 0 },
+	/* Requested Device Type */
+	{ "106a00081122334455667788", 0, 0 },
+	{ "106a0000", -1, 0 },
+	{ "106a0009112233445566778899", -1, 0 },
+	/* More than maximum Requested Device Type attributes */
+	{ "106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788", 0, 4 },
+	/* Secondary Device Type List */
+	{ "105500081122334455667788", 0, 0 },
+	{ "1055000711223344556677", -1, 0 },
+	{ "1055008811223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566", -1, 0 },
+	/* AP Channel */
+	{ "100100020001", 0, 0 },
+	{ "1001000101", -1, 0 },
+	/* Skip invalid Vendor Extension */
+	{ "10490000", 0, 0 },
+	{ "1049000100", 0, 0 },
+	{ "104900020000", 0, 0 },
+	/* Too long unknown vendor extension */
+	{ "10490401"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "112233445566778899001122334455667788990011223344556677889900"
+	  "1122334455", -1, 0 },
+	/* Maximum unknown vendor extensions */
+	{ "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA", 0, 5 },
+	/* More than maximum unknown vendor extensions */
+	{ "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA10490003BBBBBB", -1, 0 },
+	/* WFA vendor extensions */
+	{ "1049000300372a", 0, 0 },
+	{ "1049000400372a00", 0, 0 },
+	{ "1049000500372a0001", 0, 0 },
+	{ "1049001600372a0001ff0100020101030101040101ff00fe0101", 0, 6 },
+	/* Invalid Version2 length */
+	{ "1049000500372a0000", -1, 0 },
+	/* Invalid Network Key Shareable length */
+	{ "1049000500372a0200", -1, 0 },
+	/* Invalid Requedt To Enroll length */
+	{ "1049000500372a0300", -1, 0 },
+	/* Invalid Settings Delay Time length */
+	{ "1049000500372a0400", -1, 0 },
+	/* More than maximum Credential attributes */
+	{ "100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000", 0, 2 },
+};
+
+
+static int wps_attr_parse_tests(void)
+{
+	struct wps_parse_attr attr;
+	unsigned int i;
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "WPS attribute parsing tests");
+
+	for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) {
+		struct wpabuf *buf;
+		size_t len;
+		struct wps_attr_parse_test *test =
+			&wps_attr_parse_test_cases[i];
+
+		len = os_strlen(test->data) / 2;
+		buf = wpabuf_alloc(len);
+		if (buf == NULL)
+			return -1;
+		if (hexstr2bin(test->data, wpabuf_put(buf, len), len) < 0) {
+			wpabuf_free(buf);
+			return -1;
+		}
+		if (wps_parse_msg(buf, &attr) != test->result) {
+			wpa_printf(MSG_ERROR, "WPS attribute parsing test %u failed: %s",
+				   i, test->data);
+			ret = -1;
+		}
+		switch (test->extra) {
+		case 1:
+			if (!attr.network_key || !attr.ap_setup_locked)
+				ret = -1;
+			break;
+		case 2:
+			if (attr.num_cred != MAX_CRED_COUNT)
+				ret = -1;
+			break;
+		case 3:
+			if (!attr.network_key_idx)
+				ret = -1;
+			break;
+		case 4:
+			if (attr.num_req_dev_type != MAX_REQ_DEV_TYPE_COUNT)
+				ret = -1;
+			break;
+		case 5:
+			if (attr.num_vendor_ext != MAX_WPS_PARSE_VENDOR_EXT)
+				ret = -1;
+			break;
+		case 6:
+			if (!attr.version2 ||
+			    !attr.authorized_macs ||
+			    !attr.network_key_shareable ||
+			    !attr.request_to_enroll ||
+			    !attr.settings_delay_time)
+				ret = -1;
+			break;
+		}
+		wpabuf_free(buf);
+	}
+
+	return ret;
+}
+
+
+int wps_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "WPS module tests");
+
+	if (wps_attr_parse_tests() < 0)
+		ret = -1;
+
+	return ret;
+}
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index b7fcd9c..00c8299 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -31,16 +31,18 @@
 struct wps_nfc_pw_token {
 	struct dl_list list;
 	u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	unsigned int peer_pk_hash_known:1;
 	u16 pw_id;
 	u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
 	size_t dev_pw_len;
+	int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
 };
 
 
 static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
 {
 	dl_list_del(&token->list);
-	os_free(token);
+	bin_clear_free(token, sizeof(*token));
 }
 
 
@@ -82,14 +84,14 @@
 #define PIN_LOCKED BIT(0)
 #define PIN_EXPIRES BIT(1)
 	int flags;
-	struct os_time expiration;
+	struct os_reltime expiration;
 	u8 enrollee_addr[ETH_ALEN];
 };
 
 
 static void wps_free_pin(struct wps_uuid_pin *pin)
 {
-	os_free(pin->pin);
+	bin_clear_free(pin->pin, pin->pin_len);
 	os_free(pin);
 }
 
@@ -113,7 +115,7 @@
 	struct wps_pbc_session *next;
 	u8 addr[ETH_ALEN];
 	u8 uuid_e[WPS_UUID_LEN];
-	struct os_time timestamp;
+	struct os_reltime timestamp;
 };
 
 
@@ -183,7 +185,9 @@
 	u8 p2p_dev_addr[ETH_ALEN];
 
 	u8 pbc_ignore_uuid[WPS_UUID_LEN];
-	struct os_time pbc_ignore_start;
+#ifdef WPS_WORKAROUNDS
+	struct os_reltime pbc_ignore_start;
+#endif /* WPS_WORKAROUNDS */
 };
 
 
@@ -311,9 +315,9 @@
 					  const u8 *addr, const u8 *uuid_e)
 {
 	struct wps_pbc_session *pbc, *prev = NULL;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 
 	pbc = reg->pbc_sessions;
 	while (pbc) {
@@ -347,7 +351,8 @@
 	pbc = pbc->next;
 
 	while (pbc) {
-		if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+		if (os_reltime_expired(&now, &pbc->timestamp,
+				       WPS_PBC_WALK_TIME)) {
 			prev->next = NULL;
 			wps_free_pbc_sessions(pbc);
 			break;
@@ -367,13 +372,8 @@
 	pbc = reg->pbc_sessions;
 	while (pbc) {
 		if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
-#ifdef ANDROID_P2P
-		    (p2p_dev_addr && !is_zero_ether_addr(pbc->addr) &&
-		     os_memcmp(pbc->addr, p2p_dev_addr, ETH_ALEN) ==
-#else
 		    (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
 		     os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
-#endif
 		     0)) {
 			if (prev)
 				prev->next = pbc->next;
@@ -400,9 +400,9 @@
 	int count = 0;
 	struct wps_pbc_session *pbc;
 	struct wps_pbc_session *first = NULL;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 
 	wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
 
@@ -418,9 +418,9 @@
 			   MAC2STR(pbc->addr));
 		wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
 			    pbc->uuid_e, WPS_UUID_LEN);
-		if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
-			wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
-				   "expired");
+		if (os_reltime_expired(&now, &pbc->timestamp,
+				       WPS_PBC_WALK_TIME)) {
+			wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired");
 			break;
 		}
 		if (first &&
@@ -538,7 +538,6 @@
 static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
 {
 	*methods |= WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
 	if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
 	    WPS_CONFIG_VIRT_PUSHBUTTON)
 		*methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
@@ -556,7 +555,6 @@
 		 */
 		*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
 	}
-#endif /* CONFIG_WPS2 */
 }
 
 
@@ -568,10 +566,8 @@
 		return 0;
 	methods = reg->wps->config_methods;
 	methods &= ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
 	methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 		     WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
 	if (reg->pbc)
 		wps_set_pushbutton(&methods, reg->wps->config_methods);
 	if (reg->sel_reg_config_methods_override >= 0)
@@ -594,10 +590,8 @@
 	 * external Registrars.
 	 */
 	methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
 	methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 		     WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
 	wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
 	wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
 	wpabuf_put_be16(msg, 2);
@@ -617,13 +611,11 @@
 {
 	*count = 0;
 
-#ifdef CONFIG_WPS2
 	while (*count < WPS_MAX_AUTHORIZED_MACS) {
 		if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
 			break;
 		(*count)++;
 	}
-#endif /* CONFIG_WPS2 */
 
 	return (const u8 *) reg->authorized_macs_union;
 }
@@ -753,7 +745,7 @@
 
 	if (timeout) {
 		p->flags |= PIN_EXPIRES;
-		os_get_time(&p->expiration);
+		os_get_reltime(&p->expiration);
 		p->expiration.sec += timeout;
 	}
 
@@ -802,13 +794,13 @@
 static void wps_registrar_expire_pins(struct wps_registrar *reg)
 {
 	struct wps_uuid_pin *pin, *prev;
-	struct os_time now;
+	struct os_reltime now;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
 	{
 		if ((pin->flags & PIN_EXPIRES) &&
-		    os_time_before(&pin->expiration, &now)) {
+		    os_reltime_before(&pin->expiration, &now)) {
 			wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
 				    pin->uuid, WPS_UUID_LEN);
 			wps_registrar_remove_pin(reg, pin);
@@ -834,7 +826,7 @@
 	{
 		if (dev_pw && pin->pin &&
 		    (dev_pw_len != pin->pin_len ||
-		     os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+		     os_memcmp_const(dev_pw, pin->pin, dev_pw_len) != 0))
 			continue; /* different PIN */
 		if (pin->wildcard_uuid) {
 			wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
@@ -1042,7 +1034,9 @@
 		wps_registrar_remove_pbc_session(registrar,
 						 uuid_e, NULL);
 		wps_registrar_pbc_completed(registrar);
-		os_get_time(&registrar->pbc_ignore_start);
+#ifdef WPS_WORKAROUNDS
+		os_get_reltime(&registrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
 		os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
 	} else {
 		wps_registrar_pin_completed(registrar);
@@ -1145,9 +1139,9 @@
 #ifdef WPS_WORKAROUNDS
 	if (reg->pbc_ignore_start.sec &&
 	    os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
-		struct os_time now, dur;
-		os_get_time(&now);
-		os_time_sub(&now, &reg->pbc_ignore_start, &dur);
+		struct os_reltime now, dur;
+		os_get_reltime(&now);
+		os_reltime_sub(&now, &reg->pbc_ignore_start, &dur);
 		if (dur.sec >= 0 && dur.sec < 5) {
 			wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation "
 				   "based on Probe Request from the Enrollee "
@@ -1168,8 +1162,8 @@
 }
 
 
-static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
-			  const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len)
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+		   const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len)
 {
 	if (reg->new_psk_cb == NULL)
 		return 0;
@@ -1215,10 +1209,8 @@
 
 	if (reg->selected_registrar) {
 		methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
 		methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 			     WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
 		if (reg->pbc)
 			wps_set_pushbutton(&methods, reg->wps->config_methods);
 	}
@@ -1351,7 +1343,7 @@
 	const u8 *pin;
 	size_t pin_len = 0;
 
-	os_free(wps->dev_password);
+	bin_clear_free(wps->dev_password, wps->dev_password_len);
 	wps->dev_password = NULL;
 
 	if (wps->pbc) {
@@ -1360,10 +1352,23 @@
 		pin_len = 8;
 #ifdef CONFIG_WPS_NFC
 	} else if (wps->nfc_pw_token) {
+		if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+		{
+			wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
+				   "handover and abbreviated WPS handshake "
+				   "without Device Password");
+			return 0;
+		}
 		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
 			   "Password Token");
 		pin = wps->nfc_pw_token->dev_pw;
 		pin_len = wps->nfc_pw_token->dev_pw_len;
+	} else if (wps->dev_pw_id >= 0x10 &&
+		   wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+		   wps->wps->ap_nfc_dev_pw) {
+		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
+		pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
+		pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
 #endif /* CONFIG_WPS_NFC */
 	} else {
 		pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
@@ -1379,7 +1384,8 @@
 	}
 	if (pin == NULL) {
 		wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
-			   "the Enrollee");
+			   "the Enrollee (context %p registrar %p)",
+			   wps->wps, wps->wps->registrar);
 		wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
 				  &wps->peer_dev);
 		return -1;
@@ -1593,8 +1599,6 @@
 		wps->auth_type = WPS_AUTH_WPAPSK;
 	else if (wps->auth_type & WPS_AUTH_OPEN)
 		wps->auth_type = WPS_AUTH_OPEN;
-	else if (wps->auth_type & WPS_AUTH_SHARED)
-		wps->auth_type = WPS_AUTH_SHARED;
 	else {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
 			   wps->auth_type);
@@ -1614,10 +1618,12 @@
 			return -1;
 		}
 	} else {
-		if (wps->encr_type & WPS_ENCR_WEP)
-			wps->encr_type = WPS_ENCR_WEP;
-		else if (wps->encr_type & WPS_ENCR_NONE)
+		if (wps->encr_type & WPS_ENCR_NONE)
 			wps->encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
+		else if (wps->encr_type & WPS_ENCR_WEP)
+			wps->encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
 		else {
 			wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
 				   "type for non-WPA/WPA2 mode");
@@ -1776,6 +1782,7 @@
 static struct wpabuf * wps_build_m2(struct wps_data *wps)
 {
 	struct wpabuf *msg;
+	int config_in_m2 = 0;
 
 	if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
 		return NULL;
@@ -1806,14 +1813,41 @@
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
-	    wps_build_authenticator(wps, msg)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+#ifdef CONFIG_WPS_NFC
+	if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+	    wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+		/*
+		 * Use abbreviated handshake since public key hash allowed
+		 * Enrollee to validate our public key similarly to how Enrollee
+		 * public key was validated. There is no need to validate Device
+		 * Password in this case.
+		 */
+		struct wpabuf *plain = wpabuf_alloc(500);
+		if (plain == NULL ||
+		    wps_build_cred(wps, plain) ||
+		    wps_build_key_wrap_auth(wps, plain) ||
+		    wps_build_encr_settings(wps, msg, plain)) {
+			wpabuf_free(msg);
+			wpabuf_free(plain);
+			return NULL;
+		}
+		wpabuf_free(plain);
+		config_in_m2 = 1;
+	}
+#endif /* CONFIG_WPS_NFC */
+
+	if (wps_build_authenticator(wps, msg)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
 
 	wps->int_reg = 1;
-	wps->state = RECV_M3;
+	wps->state = config_in_m2 ? RECV_DONE : RECV_M3;
 	return msg;
 }
 
@@ -2177,7 +2211,7 @@
 	len[3] = wpabuf_len(wps->dh_pubkey_r);
 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-	if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+	if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
 			   "not match with the pre-committed value");
 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
@@ -2217,7 +2251,7 @@
 	len[3] = wpabuf_len(wps->dh_pubkey_r);
 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-	if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+	if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
 			   "not match with the pre-committed value");
 		wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
@@ -2527,6 +2561,9 @@
 	    wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+#ifdef CONFIG_WPS_NFC
+	    wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+#endif /* CONFIG_WPS_NFC */
 	    (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
 	     !wps->wps->registrar->pbc)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
@@ -2536,14 +2573,17 @@
 	}
 
 #ifdef CONFIG_WPS_NFC
-	if (wps->dev_pw_id >= 0x10) {
+	if (wps->dev_pw_id >= 0x10 ||
+	    wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
 		struct wps_nfc_pw_token *token;
 		const u8 *addr[1];
 		u8 hash[WPS_HASH_LEN];
 
+		wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)",
+			   wps->dev_pw_id, wps->wps, wps->wps->registrar);
 		token = wps_get_nfc_pw_token(
 			&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
-		if (token) {
+		if (token && token->peer_pk_hash_known) {
 			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
 				   "Password Token");
 			dl_list_del(&token->list);
@@ -2551,12 +2591,24 @@
 
 			addr[0] = attr->public_key;
 			sha256_vector(1, addr, &attr->public_key_len, hash);
-			if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
-				      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+			if (os_memcmp_const(hash,
+					    wps->nfc_pw_token->pubkey_hash,
+					    WPS_OOB_PUBKEY_HASH_LEN) != 0) {
 				wpa_printf(MSG_ERROR, "WPS: Public Key hash "
 					   "mismatch");
-				return WPS_FAILURE;
+				wps->state = SEND_M2D;
+				wps->config_error =
+					WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+				return WPS_CONTINUE;
 			}
+		} else if (token) {
+			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+				   "Password Token (no peer PK hash)");
+			wps->nfc_pw_token = token;
+		} else if (wps->dev_pw_id >= 0x10 &&
+			   wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+			   wps->wps->ap_nfc_dev_pw) {
+			wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
 		}
 	}
 #endif /* CONFIG_WPS_NFC */
@@ -3191,7 +3243,9 @@
 						 wps->uuid_e,
 						 wps->p2p_dev_addr);
 		wps_registrar_pbc_completed(wps->wps->registrar);
-		os_get_time(&wps->wps->registrar->pbc_ignore_start);
+#ifdef WPS_WORKAROUNDS
+		os_get_reltime(&wps->wps->registrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
 		os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
 			  WPS_UUID_LEN);
 	} else {
@@ -3383,10 +3437,8 @@
 		u16 methods;
 
 		methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
 		methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 			     WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
 		if (reg->pbc) {
 			reg->sel_reg_dev_password_id_override =
 				DEV_PW_PUSHBUTTON;
@@ -3447,8 +3499,7 @@
 int wps_registrar_config_ap(struct wps_registrar *reg,
 			    struct wps_credential *cred)
 {
-#ifdef CONFIG_WPS2
-	printf("encr_type=0x%x\n", cred->encr_type);
+	wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", cred->encr_type);
 	if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
 				 WPS_ENCR_AES))) {
 		if (cred->encr_type & WPS_ENCR_WEP) {
@@ -3475,7 +3526,6 @@
 			   "WPAPSK+WPA2PSK");
 		cred->auth_type |= WPS_AUTH_WPA2PSK;
 	}
-#endif /* CONFIG_WPS2 */
 
 	if (reg->wps->cred_cb)
 		return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
@@ -3488,25 +3538,39 @@
 
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
 				   const u8 *pubkey_hash, u16 pw_id,
-				   const u8 *dev_pw, size_t dev_pw_len)
+				   const u8 *dev_pw, size_t dev_pw_len,
+				   int pk_hash_provided_oob)
 {
 	struct wps_nfc_pw_token *token;
 
 	if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
 		return -1;
 
+	if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+	    (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+			   "addition - missing public key hash");
+		return -1;
+	}
+
 	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
 
 	token = os_zalloc(sizeof(*token));
 	if (token == NULL)
 		return -1;
 
-	os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	token->peer_pk_hash_known = pubkey_hash != NULL;
+	if (pubkey_hash)
+		os_memcpy(token->pubkey_hash, pubkey_hash,
+			  WPS_OOB_PUBKEY_HASH_LEN);
 	token->pw_id = pw_id;
-	wpa_snprintf_hex_uppercase((char *) token->dev_pw,
-				   sizeof(token->dev_pw),
-				   dev_pw, dev_pw_len);
-	token->dev_pw_len = dev_pw_len * 2;
+	token->pk_hash_provided_oob = pk_hash_provided_oob;
+	if (dev_pw) {
+		wpa_snprintf_hex_uppercase((char *) token->dev_pw,
+					   sizeof(token->dev_pw),
+					   dev_pw, dev_pw_len);
+		token->dev_pw_len = dev_pw_len * 2;
+	}
 
 	dl_list_add(&reg->nfc_pw_tokens, &token->list);
 
@@ -3535,8 +3599,7 @@
 	u16 id;
 	size_t dev_pw_len;
 
-	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-	    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
 	    oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
 	    WPS_OOB_DEVICE_PASSWORD_LEN)
 		return -1;
@@ -3555,7 +3618,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
 
 	return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
-					      dev_pw_len);
+					      dev_pw_len, 0);
 }
 
 
@@ -3565,6 +3628,14 @@
 	wps_registrar_remove_authorized_mac(reg,
 					    (u8 *) "\xff\xff\xff\xff\xff\xff");
 	wps_registrar_selected_registrar_changed(reg, 0);
+
+	/*
+	 * Free the NFC password token if it was used only for a single protocol
+	 * run. The static handover case uses the same password token multiple
+	 * times, so do not free that case here.
+	 */
+	if (token->peer_pk_hash_known)
+		os_free(token);
 }
 
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index bea2b33..ae94a9f 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -227,6 +227,8 @@
 
 	t = time(NULL);
 	date = gmtime(&t);
+	if (date == NULL)
+		return;
 	wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
 		      &weekday_str[date->tm_wday * 4], date->tm_mday,
 		      &month_str[date->tm_mon * 4], date->tm_year + 1900,
@@ -432,23 +434,6 @@
 }
 
 
-int send_wpabuf(int fd, struct wpabuf *buf)
-{
-	wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
-		   (unsigned long) wpabuf_len(buf));
-	errno = 0;
-	if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
-	    (int) wpabuf_len(buf)) {
-		wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
-			   "errno=%d (%s)",
-			   errno, strerror(errno));
-		return -1;
-	}
-
-	return 0;
-}
-
-
 static void wpabuf_put_property(struct wpabuf *buf, const char *name,
 				const char *value)
 {
@@ -480,14 +465,14 @@
 		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
 		"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
 	const char *format_tail = "</e:propertyset>\n";
-	struct os_time now;
+	struct os_reltime now;
 
 	if (dl_list_empty(&sm->subscriptions)) {
 		/* optimize */
 		return;
 	}
 
-	if (os_get_time(&now) == 0) {
+	if (os_get_reltime(&now) == 0) {
 		if (now.sec != sm->last_event_sec) {
 			sm->last_event_sec = now.sec;
 			sm->num_events_in_sec = 1;
@@ -611,7 +596,10 @@
 	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
 	wpabuf_put_be16(msg, WPS_NONCE_LEN);
 	wpabuf_put(msg, WPS_NONCE_LEN);
-	wps_build_wfa_ext(msg, 0, NULL, 0);
+	if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
 	return msg;
 }
 
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index 4f1dd8f..2949f14 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -61,11 +61,9 @@
 			os_memcpy(s->authorized_macs, attr.authorized_macs,
 				  count * ETH_ALEN);
 		} else if (!attr.version2) {
-#ifdef CONFIG_WPS2
 			wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
 				   "AuthorizedMACs for WPS 1.0 ER");
 			os_memset(s->authorized_macs, 0xff, ETH_ALEN);
-#endif /* CONFIG_WPS2 */
 		}
 		eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
 				       upnp_er_set_selected_timeout, s, reg);
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index 5c39f7e..f289fe6 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -158,7 +158,6 @@
 struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
 					const u8 uuid[UUID_LEN]);
 void subscr_addr_delete(struct subscr_addr *a);
-int send_wpabuf(int fd, struct wpabuf *buf);
 int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
 		   u8 mac[ETH_ALEN]);
 
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 416961c..098571c 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -134,6 +134,8 @@
 	*islast = 0;
 	iface = dl_list_first(&sm->interfaces,
 			      struct upnp_wps_device_interface, list);
+	if (!iface)
+		return NULL;
 	uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
 	msg = wpabuf_alloc(800); /* more than big enough */
 	if (msg == NULL)
@@ -587,6 +589,8 @@
 					&sm->interfaces,
 					struct upnp_wps_device_interface,
 					list);
+				if (!iface)
+					continue;
 				data += os_strlen("uuid:");
 				uuid_bin2str(iface->wps->uuid, uuid_string,
 					     sizeof(uuid_string));
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
index 11386d8..b1cf571 100644
--- a/src/wps/wps_upnp_web.c
+++ b/src/wps/wps_upnp_web.c
@@ -179,15 +179,12 @@
 /* format_wps_device_xml -- produce content of "file" wps_device.xml
  * (UPNP_WPS_DEVICE_XML_FILE)
  */
-static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
+static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
+				  struct upnp_wps_device_sm *sm,
 				  struct wpabuf *buf)
 {
 	const char *s;
 	char uuid_string[80];
-	struct upnp_wps_device_interface *iface;
-
-	iface = dl_list_first(&sm->interfaces,
-			      struct upnp_wps_device_interface, list);
 
 	wpabuf_put_str(buf, wps_device_xml_prefix);
 
@@ -319,13 +316,15 @@
 
 	iface = dl_list_first(&sm->interfaces,
 			      struct upnp_wps_device_interface, list);
+	if (iface == NULL) {
+		http_request_deinit(hreq);
+		return;
+	}
 
 	/*
 	 * It is not required that filenames be case insensitive but it is
 	 * allowed and cannot hurt here.
 	 */
-	if (filename == NULL)
-		filename = "(null)"; /* just in case */
 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
 		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
 		req = GET_DEVICE_XML_FILE;
@@ -393,7 +392,7 @@
 
 	switch (req) {
 	case GET_DEVICE_XML_FILE:
-		format_wps_device_xml(sm, buf);
+		format_wps_device_xml(iface, sm, buf);
 		break;
 	case GET_SCPD_XML_FILE:
 		wpabuf_put_str(buf, wps_scpd_xml);
@@ -421,13 +420,14 @@
 
 	iface = dl_list_first(&sm->interfaces,
 			      struct upnp_wps_device_interface, list);
-	peer = &iface->peer;
 
 	wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
 
-	if (iface->ctx->ap_pin == NULL)
+	if (!iface || iface->ctx->ap_pin == NULL)
 		return HTTP_INTERNAL_SERVER_ERROR;
 
+	peer = &iface->peer;
+
 	/*
 	 * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
 	 * registration over UPnP with the AP acting as an Enrollee. It should
@@ -475,6 +475,8 @@
 
 	iface = dl_list_first(&sm->interfaces,
 			      struct upnp_wps_device_interface, list);
+	if (!iface)
+		return HTTP_INTERNAL_SERVER_ERROR;
 
 	/*
 	 * PutMessage is used by external UPnP-based Registrar to perform WPS
@@ -948,7 +950,7 @@
 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
 	end = os_strchr(h, '\n');
 
-	for (; end != NULL; h = end + 1) {
+	while (end) {
 		/* Option line by option line */
 		h = end + 1;
 		end = os_strchr(h, '\n');
@@ -1155,7 +1157,7 @@
 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
 	end = os_strchr(h, '\n');
 
-	for (; end != NULL; h = end + 1) {
+	while (end) {
 		/* Option line by option line */
 		h = end + 1;
 		end = os_strchr(h, '\n');
@@ -1173,7 +1175,6 @@
 			.....
 		}
 #endif
-		/* SID is only for renewal */
 		match = "SID:";
 		match_len = os_strlen(match);
 		if (os_strncasecmp(h, match, match_len) == 0) {
@@ -1196,6 +1197,20 @@
 			got_uuid = 1;
 			continue;
 		}
+
+		match = "NT:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			ret = HTTP_BAD_REQUEST;
+			goto send_msg;
+		}
+
+		match = "CALLBACK:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			ret = HTTP_BAD_REQUEST;
+			goto send_msg;
+		}
 	}
 
 	if (got_uuid) {
@@ -1209,6 +1224,10 @@
 				   sa->domain_and_port : "-null-");
 			dl_list_del(&s->list);
 			subscription_destroy(s);
+		} else {
+			wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
+			ret = HTTP_PRECONDITION_FAILED;
+			goto send_msg;
 		}
 	} else {
 		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
index e366256..1c6a14b 100644
--- a/src/wps/wps_validate.c
+++ b/src/wps/wps_validate.c
@@ -267,7 +267,7 @@
 		return 0;
 	}
 	val = WPA_GET_BE16(config_error);
-	if (val > 18) {
+	if (val > 20) {
 		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
 			   "attribute value 0x%04x", val);
 		return -1;
@@ -290,7 +290,7 @@
 		return 0;
 	}
 	val = WPA_GET_BE16(dev_password_id);
-	if (val >= 0x0006 && val <= 0x000f) {
+	if (val >= 0x0008 && val <= 0x000f) {
 		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
 			   "attribute value 0x%04x", val);
 		return -1;
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 8515b5a..43c3eed 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -24,24 +24,20 @@
 # Set Android log name
 L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\"
 
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
+
+# Set Android extended P2P functionality
+L_CFLAGS += -DANDROID_P2P
+ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_P2P_STUB
+endif
+
 # Disable roaming in wpa_supplicant
 ifdef CONFIG_NO_ROAMING
 L_CFLAGS += -DCONFIG_NO_ROAMING
 endif
 
-ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
-L_CFLAGS += -DANDROID_P2P
-L_CFLAGS += -DP2P_CONCURRENT_SEARCH_DELAY=0
-endif
-
-ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
-L_CFLAGS += -DANDROID_P2P
-endif
-
-ifeq ($(BOARD_WLAN_DEVICE), mrvl)
-L_CFLAGS += -DANDROID_P2P
-endif
-
 # Use Android specific directory for control interface sockets
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
 L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
@@ -51,9 +47,6 @@
 L_CFLAGS += -mabi=aapcs-linux
 endif
 
-# To allow non-ASCII characters in SSID
-L_CFLAGS += -DWPA_UNICODE_SSID
-
 INCLUDES = $(LOCAL_PATH)
 INCLUDES += $(LOCAL_PATH)/src
 INCLUDES += $(LOCAL_PATH)/src/common
@@ -71,10 +64,14 @@
 INCLUDES += $(LOCAL_PATH)/src/utils
 INCLUDES += $(LOCAL_PATH)/src/wps
 INCLUDES += external/openssl/include
-INCLUDES += system/security/keystore
+INCLUDES += system/security/keystore/include
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+INCLUDES += external/libnl/include
+else
 INCLUDES += external/libnl-headers
 endif
+endif
 
 ifdef CONFIG_FIPS
 CONFIG_NO_RANDOM_POOL=
@@ -139,6 +136,10 @@
 L_CFLAGS += -DCONFIG_ELOOP_POLL
 endif
 
+ifdef CONFIG_ELOOP_EPOLL
+L_CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
 ifdef CONFIG_EAPOL_TEST
 L_CFLAGS += -Werror -DEAPOL_TEST
 endif
@@ -258,6 +259,7 @@
 OBJS += src/p2p/p2p_dev_disc.c
 OBJS += src/p2p/p2p_group.c
 OBJS += src/ap/p2p_hostapd.c
+OBJS += src/utils/bitfield.c
 L_CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
@@ -278,6 +280,7 @@
 OBJS += hs20_supplicant.c
 L_CFLAGS += -DCONFIG_HS20
 CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_INTERWORKING
@@ -507,7 +510,7 @@
 ifdef CONFIG_EAP_PROXY
 L_CFLAGS += -DCONFIG_EAP_PROXY
 OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c
-include eap_proxy_$(CONFIG_EAP_PROXY).mk
+include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
@@ -614,10 +617,6 @@
 endif
 
 ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-L_CFLAGS += -DCONFIG_WPS2
-endif
-
 # EAP-WSC
 L_CFLAGS += -DCONFIG_WPS -DEAP_WSC
 OBJS += wps_supplicant.c
@@ -772,6 +771,9 @@
 OBJS += src/ap/eap_user_db.c
 ifdef CONFIG_IEEE80211N
 OBJS += src/ap/ieee802_11_ht.c
+ifdef CONFIG_IEEE80211AC
+OBJS += src/ap/ieee802_11_vht.c
+endif
 endif
 ifdef CONFIG_WNM
 OBJS += src/ap/wnm_ap.c
@@ -787,6 +789,9 @@
 
 ifdef CONFIG_IEEE80211N
 L_CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+L_CFLAGS += -DCONFIG_IEEE80211AC
+endif
 endif
 
 ifdef NEED_AP_MLME
@@ -794,6 +799,7 @@
 OBJS += src/ap/ap_list.c
 OBJS += src/ap/ieee802_11.c
 OBJS += src/ap/hw_features.c
+OBJS += src/ap/dfs.c
 L_CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_WPS
@@ -952,7 +958,8 @@
 OBJS += src/crypto/crypto_gnutls.c
 OBJS_p += src/crypto/crypto_gnutls.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_gnutls.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 LIBS += -lgcrypt
 LIBS_p += -lgcrypt
@@ -968,7 +975,8 @@
 OBJS += src/crypto/crypto_cryptoapi.c
 OBJS_p += src/crypto/crypto_cryptoapi.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_cryptoapi.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 CONFIG_INTERNAL_SHA256=y
 CONFIG_INTERNAL_RC4=y
@@ -983,7 +991,8 @@
 OBJS += src/crypto/crypto_nss.c
 OBJS_p += src/crypto/crypto_nss.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_nss.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 LIBS += -lnss3
 LIBS_p += -lnss3
@@ -1122,7 +1131,7 @@
 ifdef NEED_AES_OMAC1
 NEED_AES_ENC=y
 ifdef CONFIG_OPENSSL_CMAC
-CFLAGS += -DCONFIG_OPENSSL_CMAC
+L_CFLAGS += -DCONFIG_OPENSSL_CMAC
 else
 AESOBJS += src/crypto/aes-omac1.c
 endif
@@ -1171,7 +1180,10 @@
 endif
 endif
 
-MD5OBJS = src/crypto/md5.c
+MD5OBJS =
+ifndef CONFIG_FIPS
+MD5OBJS += src/crypto/md5.c
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += src/crypto/md5-internal.c
@@ -1425,7 +1437,7 @@
 endif
 
 ifdef CONFIG_AUTOSCAN_PERIODIC
-CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
+L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
 OBJS += autoscan_periodic.c
 NEED_AUTOSCAN=y
 endif
@@ -1552,12 +1564,20 @@
 LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)
 endif
 LOCAL_SHARED_LIBRARIES := libc libcutils liblog
+ifdef CONFIG_EAP_PROXY
+LOCAL_STATIC_LIBRARIES += $(LIB_STATIC_EAP_PROXY)
+LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY)
+endif
 ifeq ($(CONFIG_TLS), openssl)
 LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder
 endif
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+LOCAL_SHARED_LIBRARIES += libnl
+else
 LOCAL_STATIC_LIBRARIES += libnl_2
 endif
+endif
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS)
 LOCAL_C_INCLUDES := $(INCLUDES)
@@ -1597,4 +1617,5 @@
 LOCAL_SHARED_LIBRARIES := libcutils liblog
 LOCAL_COPY_HEADERS_TO := libwpa_client
 LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h
+LOCAL_COPY_HEADERS += src/common/qca-vendor.h
 include $(BUILD_SHARED_LIBRARY)
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index 3f10e11..5558a5e 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,8 +1,233 @@
 ChangeLog for wpa_supplicant
 
-????-??-?? - v2.1
-	* added support for simulataneous authentication of equals (SAE) for
+2014-06-04 - v2.2
+	* added DFS indicator to get_capability freq
+	* added/fixed nl80211 functionality
+	  - BSSID/frequency hint for driver-based BSS selection
+	  - fix tearing down WDS STA interfaces
+	  - support vendor specific driver command
+	    (VENDOR <vendor id> <sub command id> [<hex formatted data>])
+	  - GO interface teardown optimization
+	  - allow beacon interval to be configured for IBSS
+	  - add SHA256-based AKM suites to CONNECT/ASSOCIATE commands
+	* removed unused NFC_RX_HANDOVER_REQ and NFC_RX_HANDOVER_SEL control
+	  interface commands (the more generic NFC_REPORT_HANDOVER is now used)
+	* fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding;
+	  this fixes password with include UTF-8 characters that use
+	  three-byte encoding EAP methods that use NtPasswordHash
+	* fixed couple of sequencies where radio work items could get stuck,
+	  e.g., when rfkill blocking happens during scanning or when
+	  scan-for-auth workaround is used
+	* P2P enhancements/fixes
+	  - enable enable U-APSD on GO automatically if the driver indicates
+	    support for this
+	  - fixed some service discovery cases with broadcast queries not being
+	    sent to all stations
+	  - fixed Probe Request frame triggering invitation to trigger only a
+	    single invitation instance even if multiple Probe Request frames are
+	    received
+	  - fixed a potential NULL pointer dereference crash when processing an
+	    invalid Invitation Request frame
+	  - add optional configuration file for the P2P_DEVICE parameters
+	  - optimize scan for GO during persistent group invocation
+	  - fix possible segmentation fault when PBC overlap is detected while
+	    using a separate P2P group interface
+	  - improve GO Negotiation robustness by allowing GO Negotiation
+	    Confirmation to be retransmitted
+	  - do use freed memory on device found event when P2P NFC
+	* added phase1 network parameter options for disabling TLS v1.1 and v1.2
+	  to allow workarounds with misbehaving AAA servers
+	  (tls_disable_tlsv1_1=1 and tls_disable_tlsv1_2=1)
+	* added support for OCSP stapling to validate AAA server certificate
+	  during TLS exchange
+	* Interworking/Hotspot 2.0 enhancements
+	  - prefer the last added network in Interworking connection to make the
+	    behavior more consistent with likely user expectation
+	  - roaming partner configuration (roaming_partner within a cred block)
+	  - support Hotspot 2.0 Release 2
+	    * "hs20_anqp_get <BSSID> 8" to request OSU Providers list
+	    * "hs20_icon_request <BSSID> <icon filename>" to request icon files
+	    * "fetch_osu" and "cancel_osu_fetch" to start/stop full OSU provider
+	      search (all suitable APs in scan results)
+	    * OSEN network for online signup connection
+	    * min_{dl,ul}_bandwidth_{home,roaming} cred parameters
+	    * max_bss_load cred parameter
+	    * req_conn_capab cred parameter
+	    * sp_priority cred parameter
+	    * ocsp cred parameter
+	    * slow down automatic connection attempts on EAP failure to meet
+	      required behavior (no more than 10 retries within a 10-minute
+	      interval)
+	    * sample implementation of online signup client (both SPP and
+	      OMA-DM protocols) (hs20/client/*)
+	  - fixed GAS indication for additional comeback delay with status
+	    code 95
+	  - extend ANQP_GET to accept Hotspot 2.0 subtypes
+	    ANQP_GET <addr> <info id>[,<info id>]...
+	    [,hs20:<subtype>][...,hs20:<subtype>]
+	  - add control interface events CRED-ADDED <id>,
+	    CRED-MODIFIED <id> <field>, CRED-REMOVED <id>
+	  - add "GET_CRED <id> <field>" command
+	  - enable FT for the connection automatically if the AP advertises
+	    support for this
+	  - fix a case where auto_interworking=1 could end up stopping scanning
+	* fixed TDLS interoperability issues with supported operating class in
+	  some deployed stations
+	* internal TLS implementation enhancements/fixes
+	  - add SHA256-based cipher suites
+	  - add DHE-RSA cipher suites
+	  - fix X.509 validation of PKCS#1 signature to check for extra data
+	* fixed PTK derivation for CCMP-256 and GCMP-256
+	* added "reattach" command for fast reassociate-back-to-same-BSS
+	* allow PMF to be enabled for AP mode operation with the ieee80211w
+	  parameter
+	* added "get_capability tdls" command
+	* added option to set config blobs through control interface with
+	  "SET blob <name> <hexdump>"
+	* D-Bus interface extensions/fixes
+	  - make p2p_no_group_iface configurable
+	  - declare ServiceDiscoveryRequest method properly
+	  - export peer's device address as a property
+	  - make reassociate command behave like the control interface one,
+	    i.e., to allow connection from disconnected state
+	* added optional "freq=<channel ranges>" parameter to SET pno
+	* added optional "freq=<channel ranges>" parameter to SELECT_NETWORK
+	* fixed OBSS scan result processing for 20/40 MHz co-ex report
+	* remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled
+	  whenever CONFIG_WPS=y is set
+	* fixed regression in parsing of WNM Sleep Mode exit key data
+	* fixed potential segmentation fault and memory leaks in WNM neighbor
+	  report processing
+	* EAP-pwd fixes
+	  - fragmentation of PWD-Confirm-Resp
+	  - fix memory leak when fragmentation is used
+	  - fix possible segmentation fault on EAP method deinit if an invalid
+	    group is negotiated
+	* added MACsec/IEEE Std 802.1X-2010 PAE implementation (currently
+	  available only with the macsec_qca driver wrapper)
+	* fixed EAP-SIM counter-too-small message
+	* added 'dup_network <id_s> <id_d> <name>' command; this can be used to
+	  clone the psk field without having toextract it from wpa_supplicant
+	* fixed GSM authentication on USIM
+	* added support for usin epoll in eloop (CONFIG_ELOOP_EPOLL=y)
+	* fixed some concurrent virtual interface cases with dedicated P2P
+	  management interface to not catch events from removed interface (this
+	  could result in the management interface getting disabled)
+	* fixed a memory leak in SAE random number generation
+	* fixed off-by-one bounds checking in printf_encode()
+	  - this could result in some control interface ATTACH command cases
+	    terminating wpa_supplicant
+	* fixed EAPOL-Key exchange when GCMP is used with SHA256-based AKM
+	* various bug fixes
+
+2014-02-04 - v2.1
+	* added support for simultaneous authentication of equals (SAE) for
 	  stronger password-based authentication with WPA2-Personal
+	* improved P2P negotiation and group formation robustness
+	  - avoid unnecessary Dialog Token value changes during retries
+	  - avoid more concurrent scanning cases during full group formation
+	    sequence
+	  - do not use potentially obsolete scan result data from driver
+	    cache for peer discovery/updates
+	  - avoid undesired re-starting of GO negotiation based on Probe
+	    Request frames
+	  - increase GO Negotiation and Invitation timeouts to address busy
+	    environments and peers that take long time to react to messages,
+	    e.g., due to power saving
+	  - P2P Device interface type
+	* improved P2P channel selection (use more peer information and allow
+	  more local options)
+	* added support for optional per-device PSK assignment by P2P GO
+	  (wpa_cli p2p_set per_sta_psk <0/1>)
+	* added P2P_REMOVE_CLIENT for removing a client from P2P groups
+	  (including persistent groups); this can be used to securely remove
+	  a client from a group if per-device PSKs are used
+	* added more configuration flexibility for allowed P2P GO/client
+	  channels (p2p_no_go_freq list and p2p_add_cli_chan=0/1)
+	* added nl80211 functionality
+	  - VHT configuration for nl80211
+	  - MFP (IEEE 802.11w) information for nl80211 command API
+	  - support split wiphy dump
+	  - FT (IEEE 802.11r) with driver-based SME
+	  - use advertised number of supported concurrent channels
+	  - QoS Mapping configuration
+	* improved TDLS negotiation robustness
+	* added more TDLS peer parameters to be configured to the driver
+	* optimized connection time by allowing recently received scan results
+	  to be used instead of having to run through a new scan
+	* fixed ctrl_iface BSS command iteration with RANGE argument and no
+	  exact matches; also fixed argument parsing for some cases with
+	  multiple arguments
+	* added 'SCAN TYPE=ONLY' ctrl_iface command to request manual scan
+	  without executing roaming/network re-selection on scan results
+	* added Session-Id derivation for EAP peer methods
+	* added fully automated regression testing with mac80211_hwsim
+	* changed configuration parser to reject invalid integer values
+	* allow AP/Enrollee to be specified with BSSID instead of UUID for
+	  WPS ER operations
+	* disable network block temporarily on repeated connection failures
+	* changed the default driver interface from wext to nl80211 if both are
+	  included in the build
+	* remove duplicate networks if WPS provisioning is run multiple times
+	* remove duplicate networks when Interworking network selection uses the
+	  same network
+	* added global freq_list configuration to allow scan frequencies to be
+	  limited for all cases instead of just for a specific network block
+	* added support for BSS Transition Management
+	* added option to use "IFNAME=<ifname> " prefix to use the global
+	  control interface connection to perform per-interface commands;
+	  similarly, allow global control interface to be used as a monitor
+	  interface to receive events from all interfaces
+	* fixed OKC-based PMKSA cache entry clearing
+	* fixed TKIP group key configuration with FT
+	* added support for using OCSP stapling to validate server certificate
+	  (ocsp=1 as optional and ocsp=2 as mandatory)
+	* added EAP-EKE peer
+	* added peer restart detection for IBSS RSN
+	* added domain_suffix_match (and domain_suffix_match2 for Phase 2
+	  EAP-TLS) to specify additional constraint for the server certificate
+	  domain name
+	* added support for external SIM/USIM processing in EAP-SIM, EAP-AKA,
+	  and EAP-AKA' (CTRL-REQ-SIM and CTRL-RSP-SIM commands over control
+	  interface)
+	* added global bgscan configuration option as a default for all network
+	  blocks that do not specify their own bgscan parameters
+	* added D-Bus methods for TDLS
+	* added more control to scan requests
+	  - "SCAN freq=<freq list>" can be used to specify which channels are
+	    scanned (comma-separated frequency ranges in MHz)
+	  - "SCAN passive=1" can be used to request a passive scan (no Probe
+	    Request frames are sent)
+	  - "SCAN use_id" can be used to request a scan id to be returned and
+	    included in event messages related to this specific scan operation
+	  - "SCAN only_new=1" can be used to request the driver/cfg80211 to
+	    report only BSS entries that have been updated during this scan
+	    round
+	  - these optional arguments to the SCAN command can be combined with
+	    each other
+	* modified behavior on externally triggered scans
+	  - avoid concurrent operations requiring full control of the radio when
+	    an externally triggered scan is detected
+	  - do not use results for internal roaming decision
+	* added a new cred block parameter 'temporary' to allow credential
+	  blocks to be stored separately even if wpa_supplicant configuration
+	  file is used to maintain other network information
+	* added "radio work" framework to schedule exclusive radio operations
+	  for off-channel functionality
+	  - reduce issues with concurrent operations that try to control which
+	    channel is used
+	  - allow external programs to request exclusive radio control in a way
+	    that avoids conflicts with wpa_supplicant
+	* added support for using Protected Dual of Public Action frames for
+	  GAS/ANQP exchanges when associated with PMF
+	* added support for WPS+NFC updates and P2P+NFC
+	  - improved protocol for WPS
+	  - P2P group formation/join based on NFC connection handover
+	  - new IPv4 address assignment for P2P groups (ip_addr_* configuration
+	    parameters on the GO) to replace DHCP
+	  - option to fetch and report alternative carrier records for external
+	    NFC operations
+	* various bug fixes
 
 2013-01-12 - v2.0
 	* removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 5698619..8f7c23f 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -10,11 +10,17 @@
 export BINDIR ?= /usr/local/sbin/
 PKG_CONFIG ?= pkg-config
 
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
+CFLAGS += -I$(abspath ../src)
+CFLAGS += -I$(abspath ../src/utils)
 
 -include .config
 
+ifdef CONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CONFIG_WPS_TESTING=y
+CONFIG_TDLS_TESTING=y
+endif
+
 BINALL=wpa_supplicant wpa_cli
 
 ifndef CONFIG_NO_WPA_PASSPHRASE
@@ -100,10 +106,10 @@
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_p += -lbfd
-LIBS_c += -lbfd
+CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD
+LIBS += -lbfd -ldl -liberty -lz
+LIBS_p += -lbfd -ldl -liberty -lz
+LIBS_c += -lbfd -ldl -liberty -lz
 endif
 endif
 
@@ -113,15 +119,32 @@
 OBJS += ../src/utils/$(CONFIG_ELOOP).o
 OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
 
+ifeq ($(CONFIG_ELOOP), eloop)
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+LIBS_c += -lrt
+LIBS_p += -lrt
+endif
+
 ifdef CONFIG_ELOOP_POLL
 CFLAGS += -DCONFIG_ELOOP_POLL
 endif
 
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
 
 ifdef CONFIG_EAPOL_TEST
 CFLAGS += -Werror -DEAPOL_TEST
 endif
 
+ifdef CONFIG_CODE_COVERAGE
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+LIBS += -lgcov
+LIBS_c += -lgcov
+LIBS_p += -lgcov
+endif
+
 ifdef CONFIG_HT_OVERRIDES
 CFLAGS += -DCONFIG_HT_OVERRIDES
 endif
@@ -237,6 +260,7 @@
 OBJS += ../src/p2p/p2p_dev_disc.o
 OBJS += ../src/p2p/p2p_group.o
 OBJS += ../src/ap/p2p_hostapd.o
+OBJS += ../src/utils/bitfield.o
 CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
@@ -257,6 +281,7 @@
 OBJS += hs20_supplicant.o
 CFLAGS += -DCONFIG_HS20
 CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_INTERWORKING
@@ -485,7 +510,7 @@
 ifdef CONFIG_EAP_PROXY
 CFLAGS += -DCONFIG_EAP_PROXY
 OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o
-include eap_proxy_$(CONFIG_EAP_PROXY).mk
+include eap_proxy_$(CONFIG_EAP_PROXY).mak
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
@@ -592,10 +617,6 @@
 endif
 
 ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-CFLAGS += -DCONFIG_WPS2
-endif
-
 # EAP-WSC
 CFLAGS += -DCONFIG_WPS -DEAP_WSC
 OBJS += wps_supplicant.o
@@ -722,6 +743,19 @@
 endif
 endif
 
+ifdef CONFIG_MACSEC
+CFLAGS += -DCONFIG_MACSEC
+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
+endif
+
 ifdef CONFIG_AP
 NEED_80211_COMMON=y
 NEED_EAP_COMMON=y
@@ -750,6 +784,9 @@
 OBJS += ../src/ap/eap_user_db.o
 ifdef CONFIG_IEEE80211N
 OBJS += ../src/ap/ieee802_11_ht.o
+ifdef CONFIG_IEEE80211AC
+OBJS += ../src/ap/ieee802_11_vht.o
+endif
 endif
 ifdef CONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
@@ -765,6 +802,9 @@
 
 ifdef CONFIG_IEEE80211N
 CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+CFLAGS += -DCONFIG_IEEE80211AC
+endif
 endif
 
 ifdef NEED_AP_MLME
@@ -772,6 +812,7 @@
 OBJS += ../src/ap/ap_list.o
 OBJS += ../src/ap/ieee802_11.o
 OBJS += ../src/ap/hw_features.o
+OBJS += ../src/ap/dfs.o
 CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_WPS
@@ -930,7 +971,8 @@
 OBJS += ../src/crypto/crypto_gnutls.o
 OBJS_p += ../src/crypto/crypto_gnutls.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 LIBS += -lgcrypt
 LIBS_p += -lgcrypt
@@ -946,7 +988,8 @@
 OBJS += ../src/crypto/crypto_cryptoapi.o
 OBJS_p += ../src/crypto/crypto_cryptoapi.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_cryptoapi.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 CONFIG_INTERNAL_SHA256=y
 CONFIG_INTERNAL_RC4=y
@@ -961,7 +1004,8 @@
 OBJS += ../src/crypto/crypto_nss.o
 OBJS_p += ../src/crypto/crypto_nss.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 LIBS += -lnss3
 LIBS_p += -lnss3
@@ -1231,6 +1275,11 @@
 ifeq ($(CONFIG_CTRL_IFACE), udp)
 CFLAGS += -DCONFIG_CTRL_IFACE_UDP
 endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
 ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
 CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
 endif
@@ -1239,6 +1288,12 @@
 CFLAGS += -DCONFIG_CTRL_IFACE_UDP
 CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
 endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6-remote)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
 OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
 endif
 
@@ -1436,6 +1491,19 @@
 CFLAGS += -DCONFIG_OFFCHANNEL
 endif
 
+ifdef CONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_MODULE_TESTS
+OBJS += wpas_module_tests.o
+OBJS += ../src/utils/utils_module_tests.o
+OBJS += ../src/common/common_module_tests.o
+ifdef CONFIG_WPS
+OBJS += ../src/wps/wps_module_tests.o
+endif
+ifndef CONFIG_P2P
+OBJS += ../src/utils/bitfield.o
+endif
+endif
+
 OBJS += ../src/drivers/driver_common.o
 OBJS_priv += ../src/drivers/driver_common.o
 
@@ -1601,9 +1669,15 @@
 	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
 		-D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
 
+ifdef CONFIG_CODE_COVERAGE
+%.o: %.c
+	@$(E) "  CC " $<
+	$(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
+else
 %.o: %.c
 	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
 	@$(E) "  CC " $<
+endif
 
 %.service: %.service.in
 	sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
@@ -1655,11 +1729,18 @@
 fips:
 	$(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)"
 
+lcov-html: wpa_supplicant.gcda
+	lcov -c -d .. > lcov.info
+	genhtml lcov.info --output-directory lcov-html
+
 clean:
 	$(MAKE) -C ../src clean
 	$(MAKE) -C dbus clean
-	rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
+	rm -f core *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
 	rm -f wpa_priv
 	rm -f nfc_pw_token
+	rm -f lcov.info
+	rm -rf lcov-html
 
 -include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index 78df89e..653848e 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
 WPA Supplicant
 ==============
 
-Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
@@ -413,7 +413,7 @@
         [-G<group>] \
         -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \
         [-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
-        [-p<driver_param>] [-b<br_ifname>] ...]
+        [-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ...
 
 options:
   -b = optional bridge interface name
@@ -438,6 +438,7 @@
   -w = wait for interface to be added, if needed
   -W = wait for a control interface monitor before starting
   -N = start describing new interface
+  -m = Configuration file for the P2P Device
 
 drivers:
   nl80211 = Linux nl80211/cfg80211
@@ -949,3 +950,105 @@
 wpa_priv can control multiple interface with one process, but it is
 also possible to run multiple wpa_priv processes at the same time, if
 desired.
+
+
+Linux capabilities instead of privileged process
+------------------------------------------------
+
+wpa_supplicant performs operations that need special permissions, e.g.,
+to control the network connection. Traditionally this has been achieved
+by running wpa_supplicant as a privileged process with effective user id
+0 (root). Linux capabilities can be used to provide restricted set of
+capabilities to match the functions needed by wpa_supplicant. The
+minimum set of capabilities needed for the operations is CAP_NET_ADMIN
+and CAP_NET_RAW.
+
+setcap(8) can be used to set file capabilities. For example:
+
+sudo setcap cap_net_raw,cap_net_admin+ep wpa_supplicant
+
+Please note that this would give anyone being able to run that
+wpa_supplicant binary access to the additional capabilities. This can
+further be limited by file owner/group and mode bits. For example:
+
+sudo chown wpas wpa_supplicant
+sudo chmod 0100 wpa_supplicant
+
+This combination of setcap, chown, and chmod commands would allow wpas
+user to execute wpa_supplicant with additional network admin/raw
+capabilities.
+
+Common way style of creating a control interface socket in
+/var/run/wpa_supplicant could not be done by this user, but this
+directory could be created before starting the wpa_supplicant and set to
+suitable mode to allow wpa_supplicant to create sockets
+there. Alternatively, other directory or abstract socket namespace could
+be used for the control interface.
+
+
+External requests for radio control
+-----------------------------------
+
+External programs can request wpa_supplicant to not start offchannel
+operations during other tasks that may need exclusive control of the
+radio. The RADIO_WORK control interface command can be used for this.
+
+"RADIO_WORK add <name> [freq=<MHz>] [timeout=<seconds>]" command can be
+used to reserve a slot for radio access. If freq is specified, other
+radio work items on the same channel may be completed in
+parallel. Otherwise, all other radio work items are blocked during
+execution. Timeout is set to 10 seconds by default to avoid blocking
+wpa_supplicant operations for excessive time. If a longer (or shorter)
+safety timeout is needed, that can be specified with the optional
+timeout parameter. This command returns an identifier for the radio work
+item.
+
+Once the radio work item has been started, "EXT-RADIO-WORK-START <id>"
+event message is indicated that the external processing can start. Once
+the operation has been completed, "RADIO_WORK done <id>" is used to
+indicate that to wpa_supplicant. This allows other radio works to be
+performed. If this command is forgotten (e.g., due to the external
+program terminating), wpa_supplicant will time out the radio owrk item
+and send "EXT-RADIO-WORK-TIMEOUT <id>" event ot indicate that this has
+happened. "RADIO_WORK done <id>" can also be used to cancel items that
+have not yet been started.
+
+For example, in wpa_cli interactive mode:
+
+> radio_work add test
+1
+<3>EXT-RADIO-WORK-START 1
+> radio_work show
+ext:test@wlan0:0:1:2.487797
+> radio_work done 1
+OK
+> radio_work show
+
+
+> radio_work done 3
+OK
+> radio_work show
+ext:test freq=2412 timeout=30@wlan0:2412:1:28.583483
+<3>EXT-RADIO-WORK-TIMEOUT 2
+
+
+> radio_work add test2 freq=2412 timeout=60
+5
+<3>EXT-RADIO-WORK-START 5
+> radio_work add test3
+6
+> radio_work add test4
+7
+> radio_work show
+ext:test2 freq=2412 timeout=60@wlan0:2412:1:9.751844
+ext:test3@wlan0:0:0:5.071812
+ext:test4@wlan0:0:0:3.143870
+> radio_work done 6
+OK
+> radio_work show
+ext:test2 freq=2412 timeout=60@wlan0:2412:1:16.287869
+ext:test4@wlan0:0:0:9.679895
+> radio_work done 5
+OK
+<3>EXT-RADIO-WORK-START 7
+<3>EXT-RADIO-WORK-TIMEOUT 7
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index 5669c55..58c2475 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -109,6 +109,8 @@
 #
 # credential fields:
 #
+# temporary: Whether this credential is temporary and not to be saved
+#
 # priority: Priority group
 #	By default, all networks and credentials get the same priority group
 #	(0). This field can be used to give higher priority for credentials
@@ -166,9 +168,25 @@
 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
 #	format
 #
-# domain: Home service provider FQDN
+# domain_suffix_match: Constraint for server domain name
+#	If set, this FQDN is used as a suffix match requirement for the AAA
+#	server certificate in SubjectAltName dNSName element(s). If a
+#	matching dNSName is found, this constraint is met. If no dNSName
+#	values are present, this constraint is matched against SubjetName CN
+#	using same suffix match comparison. Suffix match here means that the
+#	host/domain name is compared one label at a time starting from the
+#	top-level domain and all the labels in @domain_suffix_match shall be
+#	included in the certificate. The certificate may include additional
+#	sub-level labels in addition to the required labels.
+#
+#	For example, domain_suffix_match=example.com would match
+#	test.example.com but would not match test-example.com.
+#
+# domain: Home service provider FQDN(s)
 #	This is used to compare against the Domain Name List to figure out
-#	whether the AP is operated by the Home SP.
+#	whether the AP is operated by the Home SP. Multiple domain entries can
+#	be used to configure alternative FQDNs that will be considered home
+#	networks.
 #
 # roaming_consortium: Roaming Consortium OI
 #	If roaming_consortium_len is non-zero, this field contains the
@@ -195,6 +213,65 @@
 #	matching with the network. Multiple entries can be used to specify more
 #	than one SSID.
 #
+# roaming_partner: Roaming partner information
+#	This optional field can be used to configure preferences between roaming
+#	partners. The field is a string in following format:
+#	<FQDN>,<0/1 exact match>,<priority>,<* or country code>
+#	(non-exact match means any subdomain matches the entry; priority is in
+#	0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+#	(Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+#	This optional field can be used to keep track of the SP that provisioned
+#	the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# sp_priority: Credential priority within a provisioning SP
+#	This is the priority of the credential among all credentials
+#	provisionined by the same SP (i.e., for entries that have identical
+#	provisioning_sp value). The range of this priority is 0-255 with 0
+#	being the highest and 255 the lower priority.
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+#	These fields can be used to specify minimum download/upload backhaul
+#	bandwidth that is preferred for the credential. This constraint is
+#	ignored if the AP does not advertise WAN Metrics information or if the
+#	limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+#	(PPS/<X+>/Policy/MaximumBSSLoadValue)
+#	This value is used as the maximum channel utilization for network
+#	selection purposes for home networks. If the AP does not advertise
+#	BSS Load or if the limit would prevent any connection, this constraint
+#	will be ignored.
+#
+# req_conn_capab: Required connection capability
+#	(PPS/<X+>/Policy/RequiredProtoPortTuple)
+#	This value is used to configure set of required protocol/port pairs that
+#	a roaming network shall support (include explicitly in Connection
+#	Capability ANQP element). This constraint is ignored if the AP does not
+#	advertise Connection Capability or if this constraint would prevent any
+#	network connection. This policy is not used in home networks.
+#	Format: <protocol>[:<comma-separated list of ports]
+#	Multiple entries can be used to list multiple requirements.
+#	For example, number of common TCP protocols:
+#	req_conn_capab=6:22,80,443
+#	For example, IPSec/IKE:
+#	req_conn_capab=17:500
+#	req_conn_capab=50
+#
+# 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
+#
+# sim_num: Identifier for which SIM to use in multi-SIM devices
+#
 # for example:
 #
 #cred={
@@ -203,6 +280,7 @@
 #	password="password"
 #	ca_cert="/etc/wpa_supplicant/ca.pem"
 #	domain="example.com"
+#	domain_suffix_match="example.com"
 #}
 #
 #cred={
@@ -252,6 +330,8 @@
 OK
 > set_cred 0 priority 1
 OK
+> set_cred 0 temporary 1
+OK
 
 Add a SIM credential using a simulated SIM/USIM card for testing:
 
@@ -267,6 +347,17 @@
 Note: the return value of add_cred is used as the first argument to
 the following set_cred commands.
 
+Add a SIM credential using a external SIM/USIM processing:
+
+> set external_sim 1
+OK
+> add_cred
+1
+> set_cred 1 imsi "23456-0000000000"
+OK
+> set_cred 1 eap SIM
+OK
+
 
 Add a WPA2-Enterprise network:
 
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index 76f8219..5c1e4f9 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -72,7 +72,8 @@
 Device Discovery
 
 p2p_find [timeout in seconds] [type=<social|progressive>] \
-	[dev_id=<addr>] [delay=<search delay in ms>]
+	[dev_id=<addr>] [dev_type=<device type>] \
+	[delay=<search delay in ms>]
 
 The default behavior is to run a single full scan in the beginning and
 then scan only social channels. type=social will scan only social
@@ -87,6 +88,10 @@
 delay to be used between search iterations (e.g., to free up radio
 resources for concurrent operations).
 
+The optional dev_type option can be used to specify a single device type
+(primary or secondary) to search for, e.g.,
+"p2p_find dev_type=1-0050F204-1".
+
 p2p_listen [timeout in seconds]
 
 Start Listen-only state (become discoverable without searching for
@@ -125,7 +130,7 @@
 
 p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
 	[persistent|persistent=<network id>] [join|auth]
-	[go_intent=<0..15>] [freq=<in MHz>] [ht40] [provdisc]
+	[go_intent=<0..15>] [freq=<in MHz>] [ht40] [vht] [provdisc]
 
 Start P2P group formation with a discovered P2P peer. This includes
 optional group owner negotiation, group interface setup, provisioning,
@@ -166,7 +171,8 @@
 P2P implementations that require this to allow the user to accept the
 connection.
 
-p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] [ht40]
+p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>]
+	[ht40] [vht]
 
 Set up a P2P group owner manually (i.e., without group owner
 negotiation with a specific peer). This is also known as autonomous
@@ -224,9 +230,8 @@
 peers (note: this can result in long response frames). The pending
 requests are sent during device discovery (see p2p_find).
 
-Only a single pending wildcard query is supported, but there can be
-multiple pending peer device specific queries (each will be sent in
-sequence whenever the peer is found).
+There can be multiple pending peer device specific queries (each will be
+sent in sequence whenever the peer is found).
 
 This command returns an identifier for the pending query (e.g.,
 "1f77628") that can be used to cancel the request. Directed requests
@@ -373,7 +378,8 @@
 Invitation
 
 p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
-	[go_dev_addr=address] [freq=<freq in MHz>] [ht40] [pref=<MHz>]
+	[go_dev_addr=address] [freq=<freq in MHz>] [ht40] [vht]
+	[pref=<MHz>]
 
 Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a
 persistent group (e.g., persistent=4). If the peer device is the GO of
@@ -416,9 +422,11 @@
 Send a P2P Presence Request to the GO (this is only available when
 acting as a P2P client). If no duration/interval pairs are given, the
 request indicates that this client has no special needs for GO
-presence. the first parameter pair gives the preferred duration and
+presence. The first parameter pair gives the preferred duration and
 interval values in microseconds. If the second pair is included, that
-indicates which value would be acceptable.
+indicates which value would be acceptable. This command returns OK
+immediately and the response from the GO is indicated in a
+P2P-PRESENCE-RESPONSE event message.
 
 Parameters
 
@@ -546,6 +554,13 @@
 
 Set country code (this is included in some P2P messages).
 
+set p2p_search_delay <delay>
+
+Set p2p_search_delay which adds extra delay in milliseconds between
+concurrent search iterations to make p2p_find friendlier to concurrent
+operations by avoiding it from taking 100% of radio resources. The
+default value is 500 ms.
+
 Status
 
 p2p_peers [discovered]
diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS
index 3d07109..b884f67 100644
--- a/wpa_supplicant/README-WPS
+++ b/wpa_supplicant/README-WPS
@@ -60,7 +60,6 @@
 
 CONFIG_DRIVER_NL80211=y
 CONFIG_WPS=y
-CONFIG_WPS2=y
 
 If you want to enable WPS external registrar (ER) functionality, you
 will also need to add following line:
@@ -366,11 +365,11 @@
 token is used to enable enrollment of a new station (that was the source
 of the NFC password token).
 
-"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the
-contents of a Handover Request Message for connection handover. The
-first argument selects the format of the output data and the second
-argument selects which type of connection handover is requested (WPS =
-Wi-Fi handover as specified in WSC 2.0).
+"nfc_get_handover_req <NDEF> <WPS-CR>" command can be used to build the
+WPS carrier record for a Handover Request Message for connection
+handover. The first argument selects the format of the output data and
+the second argument selects which type of connection handover is
+requested (WPS-CR = Wi-Fi handover as specified in WSC 2.0).
 
 "nfc_get_handover_sel <NDEF> <WPS> [UUID|BSSID]" command can be used to
 build the contents of a Handover Select Message for connection handover
@@ -382,17 +381,6 @@
 message for the specified AP when wpa_supplicant is operating as a WPS
 ER.
 
-"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt
-of NFC connection handover request. The payload may include multiple
-carriers the the applicable ones are matched based on the media
-type. The reply data is contents for the Handover Select Message
-(hexdump).
-
-"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt
-of NFC connection handover select. The payload may include multiple
-carriers the the applicable ones are matched based on the media
-type.
-
 "nfc_report_handover <INIT/RESP> WPS <carrier from handover request>
 <carrier from handover select>" can be used as an alternative way for
 reporting completed NFC connection handover. The first parameter
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 8b3d6b4..3ed734d 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -20,63 +20,6 @@
 # used to fix build issues on such systems (krb5.h not found).
 #CFLAGS += -I/usr/include/kerberos
 
-# Example configuration for various cross-compilation platforms
-
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
-
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-#	-I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
-
-
-# Driver interface for Host AP driver
-#CONFIG_DRIVER_HOSTAP=y
-
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-#CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
 # Driver interface for generic Linux wireless extensions
 # Note: WEXT is deprecated in the current Linux kernel version and no new
 # functionality is added to it. nl80211-based interface is the new
@@ -180,7 +123,7 @@
 
 # EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
 # This requires CONFIG_EAP_AKA to be enabled, too.
-#CONFIG_EAP_AKA_PRIME=y
+CONFIG_EAP_AKA_PRIME=y
 
 # Enable USIM simulator (Milenage) for EAP-AKA
 #CONFIG_USIM_SIMULATOR=y
@@ -198,8 +141,6 @@
 
 # Wi-Fi Protected Setup (WPS)
 CONFIG_WPS=y
-# Enable WSC 2.0 support
-CONFIG_WPS2=y
 # Enable WPS external registrar functionality
 CONFIG_WPS_ER=y
 # Disable credentials for an open network by default when acting as a WPS
@@ -296,7 +237,7 @@
 # main_none = Very basic example (development use only)
 #CONFIG_MAIN=main
 
-# Select wrapper for operatins system and C library specific functions
+# Select wrapper for operating system and C library specific functions
 # unix = UNIX/POSIX like systems (default)
 # win32 = Windows systems
 # none = Empty template
@@ -305,12 +246,14 @@
 # Select event loop implementation
 # eloop = select() loop (default)
 # eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
 CONFIG_ELOOP=eloop
 
 # Should we use poll instead of select? Select is used by default.
 #CONFIG_ELOOP_POLL=y
 
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -325,7 +268,7 @@
 
 # IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
-#CONFIG_IEEE80211W=y
+CONFIG_IEEE80211W=y
 
 # Select TLS implementation
 # openssl = OpenSSL (default)
@@ -403,7 +346,7 @@
 #CONFIG_DYNAMIC_EAP_METHODS=y
 
 # IEEE Std 802.11r-2008 (Fast BSS Transition)
-#CONFIG_IEEE80211R=y
+CONFIG_IEEE80211R=y
 
 # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
 #CONFIG_DEBUG_FILE=y
@@ -484,16 +427,16 @@
 
 # Wireless Network Management (IEEE Std 802.11v-2011)
 # Note: This is experimental and not complete implementation.
-#CONFIG_WNM=y
+CONFIG_WNM=y
 
 # Interworking (IEEE 802.11u)
 # This can be used to enable functionality to improve interworking with
 # external networks (GAS/ANQP to learn more about the networks and network
 # selection based on available credentials).
-#CONFIG_INTERWORKING=y
+CONFIG_INTERWORKING=y
 
 # Hotspot 2.0
-#CONFIG_HS20=y
+CONFIG_HS20=y
 
 # Disable roaming in wpa_supplicant
 CONFIG_NO_ROAMING=y
@@ -510,14 +453,17 @@
 # more information on P2P operations.
 CONFIG_P2P=y
 
+# Enable TDLS support
 CONFIG_TDLS=y
 
-#Enable Wifi Display
+# Wi-Fi Direct
+# This can be used to enable Wi-Fi Direct extensions for P2P using an external
+# program to control the additional information exchanges in the messages.
 CONFIG_WIFI_DISPLAY=y
 
 # Autoscan
 # This can be used to enable automatic scan support in wpa_supplicant.
-# See wpa_supplicant.conf for more information on autoscan usage.
+# See wpa_supplicant.conf for more information on autoscan usage.
 #
 # Enabling directly a module will enable autoscan support.
 # For exponential module:
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index fdbe248..f9aa807 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "eapol_supp/eapol_supp_sm.h"
+#include "crypto/dh_group5.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ap_drv_ops.h"
@@ -42,11 +43,43 @@
 #endif /* CONFIG_WPS */
 
 
+#ifdef CONFIG_IEEE80211N
+static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
+			     struct hostapd_config *conf,
+			     struct hostapd_hw_modes *mode)
+{
+#ifdef CONFIG_P2P
+	u8 center_chan = 0;
+	u8 channel = conf->channel;
+
+	if (!conf->secondary_channel)
+		goto no_vht;
+
+	center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+	if (!center_chan)
+		goto no_vht;
+
+	/* Use 80 MHz channel */
+	conf->vht_oper_chwidth = 1;
+	conf->vht_oper_centr_freq_seg0_idx = center_chan;
+	return;
+
+no_vht:
+	conf->vht_oper_centr_freq_seg0_idx =
+		channel + conf->secondary_channel * 2;
+#else /* CONFIG_P2P */
+	conf->vht_oper_centr_freq_seg0_idx =
+		conf->channel + conf->secondary_channel * 2;
+#endif /* CONFIG_P2P */
+}
+#endif /* CONFIG_IEEE80211N */
+
+
 static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid,
 				  struct hostapd_config *conf)
 {
-	struct hostapd_bss_config *bss = &conf->bss[0];
+	struct hostapd_bss_config *bss = conf->bss[0];
 
 	conf->driver = wpa_s->driver;
 
@@ -114,6 +147,11 @@
 				 HT_CAP_INFO_SHORT_GI40MHZ |
 				 HT_CAP_INFO_RX_STBC_MASK |
 				 HT_CAP_INFO_MAX_AMSDU_SIZE);
+
+			if (mode->vht_capab && ssid->vht) {
+				conf->ieee80211ac = 1;
+				wpas_conf_ap_vht(wpa_s, conf, mode);
+			}
 		}
 	}
 #endif /* CONFIG_IEEE80211N */
@@ -149,6 +187,16 @@
 
 	bss->isolate = !wpa_s->conf->p2p_intra_bss;
 	bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
+
+	if (ssid->p2p_group) {
+		os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4);
+		os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask,
+			  4);
+		os_memcpy(bss->ip_addr_start,
+			  wpa_s->parent->conf->ip_addr_start, 4);
+		os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end,
+			  4);
+	}
 #endif /* CONFIG_P2P */
 
 	if (ssid->ssid_len == 0) {
@@ -244,7 +292,9 @@
 
 	if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) &&
 	    (bss->wpa_group == WPA_CIPHER_CCMP ||
-	     bss->wpa_group == WPA_CIPHER_GCMP)) {
+	     bss->wpa_group == WPA_CIPHER_GCMP ||
+	     bss->wpa_group == WPA_CIPHER_CCMP_256 ||
+	     bss->wpa_group == WPA_CIPHER_GCMP_256)) {
 		/*
 		 * Strong ciphers do not need frequent rekeying, so increase
 		 * the default GTK rekeying period to 24 hours.
@@ -252,6 +302,11 @@
 		bss->wpa_group_rekey = 86400;
 	}
 
+#ifdef CONFIG_IEEE80211W
+	if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
+		bss->ieee80211w = ssid->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+
 #ifdef CONFIG_WPS
 	/*
 	 * Enable WPS by default for open and WPA/WPA2-Personal network, but
@@ -261,12 +316,10 @@
 	if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
 	    bss->ssid.security_policy != SECURITY_PLAINTEXT)
 		goto no_wps;
-#ifdef CONFIG_WPS2
 	if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
 	    (!(bss->rsn_pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
 		goto no_wps; /* WPS2 does not allow WPA/TKIP-only
 			      * configuration */
-#endif /* CONFIG_WPS2 */
 	bss->eap_server = 1;
 
 	if (!ssid->ignore_broadcast_ssid)
@@ -320,16 +373,16 @@
 #ifdef CONFIG_P2P
 	struct wpa_supplicant *wpa_s = ctx;
 	const struct ieee80211_mgmt *mgmt;
-	size_t hdr_len;
 
 	mgmt = (const struct ieee80211_mgmt *) buf;
-	hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
-	if (hdr_len > len)
+	if (len < IEEE80211_HDRLEN + 1)
+		return;
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
 		return;
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   mgmt->u.action.category,
-			   &mgmt->u.action.u.vs_public_action.action,
-			   len - hdr_len, freq);
+			   buf + IEEE80211_HDRLEN + 1,
+			   len - IEEE80211_HDRLEN - 1, freq);
 #endif /* CONFIG_P2P */
 }
 
@@ -385,16 +438,14 @@
 #ifdef CONFIG_P2P
 	struct wpa_supplicant *wpa_s = ctx;
 	const struct ieee80211_mgmt *mgmt;
-	size_t hdr_len;
 
 	mgmt = (const struct ieee80211_mgmt *) buf;
-	hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
-	if (hdr_len > len)
+	if (len < IEEE80211_HDRLEN + 1)
 		return -1;
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   mgmt->u.action.category,
-			   &mgmt->u.action.u.vs_public_action.action,
-			   len - hdr_len, freq);
+			   buf + IEEE80211_HDRLEN + 1,
+			   len - IEEE80211_HDRLEN - 1, freq);
 #endif /* CONFIG_P2P */
 	return 0;
 }
@@ -404,23 +455,17 @@
 			   const u8 *bssid, const u8 *ie, size_t ie_len,
 			   int ssi_signal)
 {
-#ifdef CONFIG_P2P
 	struct wpa_supplicant *wpa_s = ctx;
 	return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
 				     ssi_signal);
-#else /* CONFIG_P2P */
-	return 0;
-#endif /* CONFIG_P2P */
 }
 
 
 static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
 				  const u8 *uuid_e)
 {
-#ifdef CONFIG_P2P
 	struct wpa_supplicant *wpa_s = ctx;
 	wpas_p2p_wps_success(wpa_s, mac_addr, 1);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -458,17 +503,13 @@
 	params.ssid = ssid->ssid;
 	params.ssid_len = ssid->ssid_len;
 	switch (ssid->mode) {
-	case WPAS_MODE_INFRA:
-		params.mode = IEEE80211_MODE_INFRA;
-		break;
-	case WPAS_MODE_IBSS:
-		params.mode = IEEE80211_MODE_IBSS;
-		break;
 	case WPAS_MODE_AP:
 	case WPAS_MODE_P2P_GO:
 	case WPAS_MODE_P2P_GROUP_FORMATION:
 		params.mode = IEEE80211_MODE_AP;
 		break;
+	default:
+		return -1;
 	}
 	if (ssid->frequency == 0)
 		ssid->frequency = 2462; /* default channel 11 */
@@ -479,7 +520,7 @@
 		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
 	else
 		wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
-	params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+	params.key_mgmt_suite = wpa_s->key_mgmt;
 
 	wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher,
 							  1);
@@ -488,8 +529,7 @@
 			   "cipher.");
 		return -1;
 	}
-	params.pairwise_suite =
-		wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
+	params.pairwise_suite = wpa_s->pairwise_cipher;
 	params.group_suite = params.pairwise_suite;
 
 #ifdef CONFIG_P2P
@@ -500,6 +540,8 @@
 
 	if (wpa_s->parent->set_ap_uapsd)
 		params.uapsd = wpa_s->parent->ap_uapsd;
+	else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+		params.uapsd = 1; /* mandatory for P2P GO */
 	else
 		params.uapsd = -1;
 
@@ -529,8 +571,8 @@
 		  sizeof(wpa_s->conf->wmm_ac_params));
 
 	if (params.uapsd > 0) {
-		conf->bss->wmm_enabled = 1;
-		conf->bss->wmm_uapsd = 1;
+		conf->bss[0]->wmm_enabled = 1;
+		conf->bss[0]->wmm_uapsd = 1;
 	}
 
 	if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
@@ -541,9 +583,9 @@
 
 #ifdef CONFIG_P2P
 	if (ssid->mode == WPAS_MODE_P2P_GO)
-		conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+		conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
 	else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
-		conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+		conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
 			P2P_GROUP_FORMATION;
 #endif /* CONFIG_P2P */
 
@@ -558,7 +600,7 @@
 	for (i = 0; i < conf->num_bss; i++) {
 		hapd_iface->bss[i] =
 			hostapd_alloc_bss_data(hapd_iface, conf,
-					       &conf->bss[i]);
+					       conf->bss[i]);
 		if (hapd_iface->bss[i] == NULL) {
 			wpa_supplicant_ap_deinit(wpa_s);
 			return -1;
@@ -620,11 +662,10 @@
 	wpa_s->current_ssid = NULL;
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 	wpa_s->assoc_freq = 0;
-#ifdef CONFIG_P2P
-	if (wpa_s->ap_iface->bss)
-		wpa_s->ap_iface->bss[0]->p2p_group = NULL;
-	wpas_p2p_group_deinit(wpa_s);
-#endif /* CONFIG_P2P */
+	wpas_p2p_ap_deinit(wpa_s);
+	wpa_s->ap_iface->driver_ap_teardown =
+		!!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
 	hostapd_interface_deinit(wpa_s->ap_iface);
 	hostapd_interface_free(wpa_s->ap_iface);
 	wpa_s->ap_iface = NULL;
@@ -647,6 +688,8 @@
 {
 #ifdef NEED_AP_MLME
 	struct wpa_supplicant *wpa_s = ctx;
+	if (!wpa_s->ap_iface)
+		return;
 	hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack);
 #endif /* NEED_AP_MLME */
 }
@@ -909,6 +952,19 @@
 	return hostapd_wps_nfc_hs_cr(hapd, ndef);
 }
 
+
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	hapd = wpa_s->ap_iface->bss[0];
+	return hostapd_wps_nfc_report_handover(hapd, req, sel);
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 #endif /* CONFIG_WPS */
@@ -1010,9 +1066,9 @@
 
 #ifdef CONFIG_P2P
 	if (ssid->mode == WPAS_MODE_P2P_GO)
-		iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+		iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
 	else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
-		iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+		iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
 			P2P_GROUP_FORMATION;
 #endif /* CONFIG_P2P */
 
@@ -1026,14 +1082,40 @@
 }
 
 
+int ap_switch_channel(struct wpa_supplicant *wpa_s,
+		      struct csa_settings *settings)
+{
+#ifdef NEED_AP_MLME
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return -1;
+
+	return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings);
+#else /* NEED_AP_MLME */
+	return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
+int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
+{
+	struct csa_settings settings;
+	int ret = hostapd_parse_csa_settings(pos, &settings);
+
+	if (ret)
+		return ret;
+
+	return ap_switch_channel(wpa_s, &settings);
+}
+
+
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
-		       int offset)
+		       int offset, int width, int cf1, int cf2)
 {
 	if (!wpa_s->ap_iface)
 		return;
 
 	wpa_s->assoc_freq = freq;
-	hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset);
+	hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
 }
 
 
@@ -1076,3 +1158,48 @@
 
 	return 0;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+			   const struct wpabuf *pw, const u8 *pubkey_hash)
+{
+	struct hostapd_data *hapd;
+	struct wps_context *wps;
+
+	if (!wpa_s->ap_iface)
+		return -1;
+	hapd = wpa_s->ap_iface->bss[0];
+	wps = hapd->wps;
+
+	if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL ||
+	    wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known");
+		return -1;
+	}
+
+	dh5_free(wps->dh_ctx);
+	wpabuf_free(wps->dh_pubkey);
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = wpabuf_dup(
+		wpa_s->parent->conf->wps_nfc_dh_privkey);
+	wps->dh_pubkey = wpabuf_dup(
+		wpa_s->parent->conf->wps_nfc_dh_pubkey);
+	if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+		wps->dh_ctx = NULL;
+		wpabuf_free(wps->dh_pubkey);
+		wps->dh_pubkey = NULL;
+		wpabuf_free(wps->dh_privkey);
+		wps->dh_privkey = NULL;
+		return -1;
+	}
+	wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+	if (wps->dh_ctx == NULL)
+		return -1;
+
+	return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash,
+					      pw_id,
+					      pw ? wpabuf_head(pw) : NULL,
+					      pw ? wpabuf_len(pw) : 0, 1);
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 74a0b18..8aa5ffa 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -50,8 +50,11 @@
 int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
 				      const u8 *addr);
 void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
+int ap_switch_channel(struct wpa_supplicant *wpa_s,
+		      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 offset, int width, int cf1, int cf2);
 struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 					     int ndef);
 #ifdef CONFIG_AP
@@ -66,4 +69,10 @@
 }
 #endif /* CONFIG_AP */
 
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+			   const struct wpabuf *pw, const u8 *pubkey_hash);
+
 #endif /* AP_H */
diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c
index 9a9bd52..f74cdbf 100644
--- a/wpa_supplicant/bgscan.c
+++ b/wpa_supplicant/bgscan.c
@@ -31,9 +31,9 @@
 };
 
 
-int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+		const char *name)
 {
-	const char *name = ssid->bgscan;
 	const char *params;
 	size_t nlen;
 	int i;
@@ -41,7 +41,7 @@
 
 	bgscan_deinit(wpa_s);
 	if (name == NULL)
-		return 0;
+		return -1;
 
 	params = os_strchr(name, ':');
 	if (params == NULL) {
diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h
index e9d15fc..9131e4e 100644
--- a/wpa_supplicant/bgscan.h
+++ b/wpa_supplicant/bgscan.h
@@ -29,7 +29,8 @@
 
 #ifdef CONFIG_BGSCAN
 
-int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+		const char *name);
 void bgscan_deinit(struct wpa_supplicant *wpa_s);
 int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
 		       struct wpa_scan_results *scan_res);
@@ -41,7 +42,7 @@
 #else /* CONFIG_BGSCAN */
 
 static inline int bgscan_init(struct wpa_supplicant *wpa_s,
-			      struct wpa_ssid *ssid)
+			      struct wpa_ssid *ssid, const char name)
 {
 	return 0;
 }
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index 07d31e4..6a92b73 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -34,7 +34,7 @@
 	int signal_threshold;
 	int short_interval; /* use if signal < threshold */
 	int long_interval; /* use if signal > threshold */
-	struct os_time last_bgscan;
+	struct os_reltime last_bgscan;
 	char *fname;
 	struct dl_list bss;
 	int *supp_freqs;
@@ -240,17 +240,14 @@
 	if (data->supp_freqs == NULL)
 		return freqs;
 
-	idx = data->probe_idx + 1;
-	while (idx != data->probe_idx) {
-		if (data->supp_freqs[idx] == 0) {
-			if (data->probe_idx == 0)
-				break;
-			idx = 0;
-		}
+	idx = data->probe_idx;
+	do {
 		if (!in_array(freqs, data->supp_freqs[idx])) {
 			wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
 				   "%u", data->supp_freqs[idx]);
-			data->probe_idx = idx;
+			data->probe_idx = idx + 1;
+			if (data->supp_freqs[data->probe_idx] == 0)
+				data->probe_idx = 0;
 			n = os_realloc_array(freqs, count + 2, sizeof(int));
 			if (n == NULL)
 				return freqs;
@@ -262,7 +259,9 @@
 		}
 
 		idx++;
-	}
+		if (data->supp_freqs[idx] == 0)
+			idx = 0;
+	} while (idx != data->probe_idx);
 
 	return freqs;
 }
@@ -311,7 +310,7 @@
 		eloop_register_timeout(data->scan_interval, 0,
 				       bgscan_learn_timeout, data, NULL);
 	} else
-		os_get_time(&data->last_bgscan);
+		os_get_reltime(&data->last_bgscan);
 	os_free(freqs);
 }
 
@@ -363,6 +362,9 @@
 		for (j = 0; j < modes[i].num_channels; j++) {
 			if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
 				continue;
+			/* some hw modes (e.g. 11b & 11g) contain same freqs */
+			if (in_array(freqs, modes[i].channels[j].freq))
+				continue;
 			n = os_realloc_array(freqs, count + 2, sizeof(int));
 			if (n == NULL)
 				continue;
@@ -419,6 +421,14 @@
 
 	data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s);
 	data->scan_interval = data->short_interval;
+	if (data->signal_threshold) {
+		/* Poll for signal info to set initial scan interval */
+		struct wpa_signal_info siginfo;
+		if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
+		    siginfo.current_signal >= data->signal_threshold)
+			data->scan_interval = data->long_interval;
+	}
+
 	eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
 			       data, NULL);
 
@@ -428,7 +438,7 @@
 	 * us skip an immediate new scan in cases where the current signal
 	 * level is below the bgscan threshold.
 	 */
-	os_get_time(&data->last_bgscan);
+	os_get_reltime(&data->last_bgscan);
 
 	return data;
 }
@@ -555,7 +565,7 @@
 {
 	struct bgscan_learn_data *data = priv;
 	int scan = 0;
-	struct os_time now;
+	struct os_reltime now;
 
 	if (data->short_interval == data->long_interval ||
 	    data->signal_threshold == 0)
@@ -569,7 +579,7 @@
 		wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
 			   "interval");
 		data->scan_interval = data->short_interval;
-		os_get_time(&now);
+		os_get_reltime(&now);
 		if (now.sec > data->last_bgscan.sec + 1)
 			scan = 1;
 	} else if (data->scan_interval == data->short_interval && above) {
@@ -584,7 +594,7 @@
 		 * Signal dropped further 4 dB. Request a new scan if we have
 		 * not yet scanned in a while.
 		 */
-		os_get_time(&now);
+		os_get_reltime(&now);
 		if (now.sec > data->last_bgscan.sec + 10)
 			scan = 1;
 	}
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index 479f703..a467cc5 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -26,7 +26,7 @@
 	int max_short_scans; /* maximum times we short-scan before back-off */
 	int short_interval; /* use if signal < threshold */
 	int long_interval; /* use if signal > threshold */
-	struct os_time last_bgscan;
+	struct os_reltime last_bgscan;
 };
 
 
@@ -75,7 +75,7 @@
 			 */
 			data->short_scan_count--;
 		}
-		os_get_time(&data->last_bgscan);
+		os_get_reltime(&data->last_bgscan);
 	}
 }
 
@@ -159,7 +159,7 @@
 	 * us skip an immediate new scan in cases where the current signal
 	 * level is below the bgscan threshold.
 	 */
-	os_get_time(&data->last_bgscan);
+	os_get_reltime(&data->last_bgscan);
 
 	return data;
 }
@@ -211,7 +211,7 @@
 {
 	struct bgscan_simple_data *data = priv;
 	int scan = 0;
-	struct os_time now;
+	struct os_reltime now;
 
 	if (data->short_interval == data->long_interval ||
 	    data->signal_threshold == 0)
@@ -225,7 +225,7 @@
 		wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
 			   "bgscan interval");
 		data->scan_interval = data->short_interval;
-		os_get_time(&now);
+		os_get_reltime(&now);
 		if (now.sec > data->last_bgscan.sec + 1 &&
 		    data->short_scan_count <= data->max_short_scans)
 			/*
@@ -259,7 +259,7 @@
 		 * Signal dropped further 4 dB. Request a new scan if we have
 		 * not yet scanned in a while.
 		 */
-		os_get_time(&now);
+		os_get_reltime(&now);
 		if (now.sec > data->last_bgscan.sec + 10)
 			scan = 1;
 	}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 67a9f97..f99a8a7 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -98,6 +98,7 @@
 	ANQP_DUP(hs20_wan_metrics);
 	ANQP_DUP(hs20_connection_capability);
 	ANQP_DUP(hs20_operating_class);
+	ANQP_DUP(hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
 #undef ANQP_DUP
 
@@ -166,6 +167,7 @@
 	wpabuf_free(anqp->hs20_wan_metrics);
 	wpabuf_free(anqp->hs20_connection_capability);
 	wpabuf_free(anqp->hs20_operating_class);
+	wpabuf_free(anqp->hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
 
 	os_free(anqp);
@@ -224,9 +226,9 @@
 }
 
 
-static void calculate_update_time(const struct os_time *fetch_time,
+static void calculate_update_time(const struct os_reltime *fetch_time,
 				  unsigned int age_ms,
-				  struct os_time *update_time)
+				  struct os_reltime *update_time)
 {
 	os_time_t usec;
 
@@ -243,7 +245,7 @@
 
 
 static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
-			     struct os_time *fetch_time)
+			     struct os_reltime *fetch_time)
 {
 	dst->flags = src->flags;
 	os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
@@ -326,7 +328,7 @@
 static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
 				    const u8 *ssid, size_t ssid_len,
 				    struct wpa_scan_res *res,
-				    struct os_time *fetch_time)
+				    struct os_reltime *fetch_time)
 {
 	struct wpa_bss *bss;
 
@@ -492,7 +494,7 @@
 
 static struct wpa_bss *
 wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
-	       struct wpa_scan_res *res, struct os_time *fetch_time)
+	       struct wpa_scan_res *res, struct os_reltime *fetch_time)
 {
 	u32 changes;
 
@@ -502,6 +504,22 @@
 	wpa_bss_copy_res(bss, res, fetch_time);
 	/* Move the entry to the end of the list */
 	dl_list_del(&bss->list);
+#ifdef CONFIG_P2P
+	if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+	    !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) {
+		/*
+		 * This can happen when non-P2P station interface runs a scan
+		 * without P2P IE in the Probe Request frame. P2P GO would reply
+		 * to that with a Probe Response that does not include P2P IE.
+		 * Do not update the IEs in this BSS entry to avoid such loss of
+		 * information that may be needed for P2P operations to
+		 * determine group information.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
+			MACSTR " since that would remove P2P IE information",
+			MAC2STR(bss->bssid));
+	} else
+#endif /* CONFIG_P2P */
 	if (bss->ie_len + bss->beacon_ie_len >=
 	    res->ie_len + res->beacon_ie_len) {
 		os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
@@ -571,17 +589,18 @@
  */
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res,
-			     struct os_time *fetch_time)
+			     struct os_reltime *fetch_time)
 {
 	const u8 *ssid, *p2p;
 	struct wpa_bss *bss;
 
 	if (wpa_s->conf->ignore_old_scan_res) {
-		struct os_time update;
+		struct os_reltime update;
 		calculate_update_time(fetch_time, res->age, &update);
-		if (os_time_before(&update, &wpa_s->scan_trigger_time)) {
-			struct os_time age;
-			os_time_sub(&wpa_s->scan_trigger_time, &update, &age);
+		if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
+			struct os_reltime age;
+			os_reltime_sub(&wpa_s->scan_trigger_time, &update,
+				       &age);
 			wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
 				"table entry that is %u.%06u seconds older "
 				"than our scan trigger",
@@ -625,8 +644,18 @@
 	bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
 	if (bss == NULL)
 		bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
-	else
+	else {
 		bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
+		if (wpa_s->last_scan_res) {
+			unsigned int i;
+			for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+				if (bss == wpa_s->last_scan_res[i]) {
+					/* Already in the list */
+					return;
+				}
+			}
+		}
+	}
 
 	if (bss == NULL)
 		return;
@@ -645,7 +674,8 @@
 		wpa_s->last_scan_res_size = siz;
 	}
 
-	wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
+	if (wpa_s->last_scan_res)
+		wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
 }
 
 
@@ -705,26 +735,10 @@
 {
 	struct wpa_bss *bss, *n;
 
-	wpa_s->last_scan_full = 0;
-	os_get_time(&wpa_s->last_scan);
+	os_get_reltime(&wpa_s->last_scan);
 	if (!new_scan)
 		return; /* do not expire entries without new scan */
 
-	if (info && !info->aborted && !info->freqs) {
-		size_t i;
-		if (info->num_ssids == 0) {
-			wpa_s->last_scan_full = 1;
-		} else {
-			for (i = 0; i < info->num_ssids; i++) {
-				if (info->ssids[i].ssid == NULL ||
-				    info->ssids[i].ssid_len == 0) {
-					wpa_s->last_scan_full = 1;
-					break;
-				}
-			}
-		}
-	}
-
 	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
 		if (wpa_bss_in_use(wpa_s, bss))
 			continue;
@@ -738,10 +752,8 @@
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u "
-		   "last_scan_full=%d",
-		   wpa_s->last_scan_res_used, wpa_s->last_scan_res_size,
-		   wpa_s->last_scan_full);
+	wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u",
+		   wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
 }
 
 
@@ -755,19 +767,19 @@
 void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
 {
 	struct wpa_bss *bss, *n;
-	struct os_time t;
+	struct os_reltime t;
 
 	if (dl_list_empty(&wpa_s->bss))
 		return;
 
-	os_get_time(&t);
+	os_get_reltime(&t);
 	t.sec -= age;
 
 	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
 		if (wpa_bss_in_use(wpa_s, bss))
 			continue;
 
-		if (os_time_before(&bss->last_update, &t)) {
+		if (os_reltime_before(&bss->last_update, &t)) {
 			wpa_bss_remove(wpa_s, bss, __func__);
 		} else
 			break;
@@ -811,6 +823,8 @@
 {
 	struct wpa_bss *bss, *n;
 
+	wpa_s->clear_driver_scan_cache = 1;
+
 	if (wpa_s->bss.next == NULL)
 		return; /* BSS table not yet initialized */
 
@@ -874,7 +888,7 @@
 		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
 			continue;
 		if (found == NULL ||
-		    os_time_before(&found->last_update, &bss->last_update))
+		    os_reltime_before(&found->last_update, &bss->last_update))
 			found = bss;
 	}
 	return found;
@@ -1002,6 +1016,43 @@
 
 
 /**
+ * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ *
+ * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+					u32 vendor_type)
+{
+	const u8 *end, *pos;
+
+	if (bss->beacon_ie_len == 0)
+		return NULL;
+
+	pos = (const u8 *) (bss + 1);
+	pos += bss->ie_len;
+	end = pos + bss->beacon_ie_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+		    vendor_type == WPA_GET_BE32(&pos[2]))
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+
+/**
  * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
  * @bss: BSS table entry
  * @vendor_type: Vendor type (four octets starting the IE payload)
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 2b41948..4a624c5 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -39,6 +39,7 @@
 	struct wpabuf *hs20_wan_metrics;
 	struct wpabuf *hs20_connection_capability;
 	struct wpabuf *hs20_operating_class;
+	struct wpabuf *hs20_osu_providers_list;
 #endif /* CONFIG_HS20 */
 };
 
@@ -84,7 +85,7 @@
 	/** Timestamp of last Beacon/Probe Response frame */
 	u64 tsf;
 	/** Time of the last update (i.e., Beacon or Probe Response RX) */
-	struct os_time last_update;
+	struct os_reltime last_update;
 	/** ANQP data */
 	struct wpa_bss_anqp *anqp;
 	/** Length of the following IE field in octets (from Probe Response) */
@@ -98,7 +99,7 @@
 void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res,
-			     struct os_time *fetch_time);
+			     struct os_reltime *fetch_time);
 void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
 			int new_scan);
 int wpa_bss_init(struct wpa_supplicant *wpa_s);
@@ -118,6 +119,8 @@
 				      unsigned int idf, unsigned int idl);
 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+					u32 vendor_type);
 struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
 					    u32 vendor_type);
 struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
@@ -127,4 +130,9 @@
 struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
 int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
 
+static inline int bss_is_dmg(const struct wpa_bss *bss)
+{
+	return bss->freq > 45000;
+}
+
 #endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 69f920d..8cd4a2f 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "utils/uuid.h"
+#include "utils/ip_addr.h"
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
@@ -285,7 +286,7 @@
 {
 #ifdef CONFIG_EXT_PASSWORD
 	if (os_strncmp(value, "ext:", 4) == 0) {
-		os_free(ssid->passphrase);
+		str_clear_free(ssid->passphrase);
 		ssid->passphrase = NULL;
 		ssid->psk_set = 0;
 		os_free(ssid->ext_psk);
@@ -321,7 +322,7 @@
 		    os_memcmp(ssid->passphrase, value, len) == 0)
 			return 0;
 		ssid->psk_set = 0;
-		os_free(ssid->passphrase);
+		str_clear_free(ssid->passphrase);
 		ssid->passphrase = dup_binstr(value, len);
 		if (ssid->passphrase == NULL)
 			return -1;
@@ -340,7 +341,7 @@
 		return -1;
 	}
 
-	os_free(ssid->passphrase);
+	str_clear_free(ssid->passphrase);
 	ssid->passphrase = NULL;
 
 	ssid->psk_set = 1;
@@ -404,6 +405,8 @@
 		else if (os_strcmp(start, "RSN") == 0 ||
 			 os_strcmp(start, "WPA2") == 0)
 			val |= WPA_PROTO_RSN;
+		else if (os_strcmp(start, "OSEN") == 0)
+			val |= WPA_PROTO_OSEN;
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
 				   line, start);
@@ -432,28 +435,41 @@
 static char * wpa_config_write_proto(const struct parse_data *data,
 				     struct wpa_ssid *ssid)
 {
-	int first = 1, ret;
+	int ret;
 	char *buf, *pos, *end;
 
-	pos = buf = os_zalloc(10);
+	pos = buf = os_zalloc(20);
 	if (buf == NULL)
 		return NULL;
-	end = buf + 10;
+	end = buf + 20;
 
 	if (ssid->proto & WPA_PROTO_WPA) {
-		ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sWPA",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return buf;
 		pos += ret;
-		first = 0;
 	}
 
 	if (ssid->proto & WPA_PROTO_RSN) {
-		ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sRSN",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return buf;
 		pos += ret;
-		first = 0;
+	}
+
+	if (ssid->proto & WPA_PROTO_OSEN) {
+		ret = os_snprintf(pos, end - pos, "%sOSEN",
+				  pos == buf ? "" : " ");
+		if (ret < 0 || ret >= end - pos)
+			return buf;
+		pos += ret;
+	}
+
+	if (pos == buf) {
+		os_free(buf);
+		buf = NULL;
 	}
 
 	return buf;
@@ -515,6 +531,10 @@
 		else if (os_strcmp(start, "FT-SAE") == 0)
 			val |= WPA_KEY_MGMT_FT_SAE;
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_HS20
+		else if (os_strcmp(start, "OSEN") == 0)
+			val |= WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -546,10 +566,10 @@
 	char *buf, *pos, *end;
 	int ret;
 
-	pos = buf = os_zalloc(50);
+	pos = buf = os_zalloc(100);
 	if (buf == NULL)
 		return NULL;
-	end = buf + 50;
+	end = buf + 100;
 
 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
@@ -602,31 +622,66 @@
 	}
 
 #ifdef CONFIG_IEEE80211R
-	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK)
-		pos += os_snprintf(pos, end - pos, "%sFT-PSK",
-				   pos == buf ? "" : " ");
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		ret = os_snprintf(pos, end - pos, "%sFT-PSK",
+				  pos == buf ? "" : " ");
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
 
-	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
-		pos += os_snprintf(pos, end - pos, "%sFT-EAP",
-				   pos == buf ? "" : " ");
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "%sFT-EAP",
+				  pos == buf ? "" : " ");
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_IEEE80211W
-	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
-		pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
-				   pos == buf ? "" : " ");
+	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
+				  pos == buf ? "" : " ");
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
 
-	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
-		pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
-				   pos == buf ? "" : " ");
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
+				  pos == buf ? "" : " ");
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
 #endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_WPS
-	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
-		pos += os_snprintf(pos, end - pos, "%sWPS",
-				   pos == buf ? "" : " ");
+	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+		ret = os_snprintf(pos, end - pos, "%sWPS",
+				  pos == buf ? "" : " ");
+		if (ret < 0 || ret >= end - pos) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
 #endif /* CONFIG_WPS */
 
+	if (pos == buf) {
+		os_free(buf);
+		buf = NULL;
+	}
+
 	return buf;
 }
 #endif /* NO_CONFIG_WRITE */
@@ -818,6 +873,11 @@
 		pos += ret;
 	}
 
+	if (pos == buf) {
+		os_free(buf);
+		buf = NULL;
+	}
+
 	return buf;
 }
 #endif /* NO_CONFIG_WRITE */
@@ -873,6 +933,10 @@
 	freqs = wpa_config_parse_int_array(value);
 	if (freqs == NULL)
 		return -1;
+	if (freqs[0] == 0) {
+		os_free(freqs);
+		freqs = NULL;
+	}
 	os_free(ssid->scan_freq);
 	ssid->scan_freq = freqs;
 
@@ -889,6 +953,10 @@
 	freqs = wpa_config_parse_int_array(value);
 	if (freqs == NULL)
 		return -1;
+	if (freqs[0] == 0) {
+		os_free(freqs);
+		freqs = NULL;
+	}
 	os_free(ssid->freq_list);
 	ssid->freq_list = freqs;
 
@@ -1062,7 +1130,7 @@
 
 	if (os_strcmp(value, "NULL") == 0) {
 		wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
-		os_free(ssid->eap.password);
+		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 		ssid->eap.password = NULL;
 		ssid->eap.password_len = 0;
 		return 0;
@@ -1073,7 +1141,7 @@
 		char *name = os_strdup(value + 4);
 		if (name == NULL)
 			return -1;
-		os_free(ssid->eap.password);
+		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 		ssid->eap.password = (u8 *) name;
 		ssid->eap.password_len = os_strlen(name);
 		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1095,7 +1163,7 @@
 		wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
 				      (u8 *) tmp, res_len);
 
-		os_free(ssid->eap.password);
+		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 		ssid->eap.password = (u8 *) tmp;
 		ssid->eap.password_len = res_len;
 		ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1124,7 +1192,7 @@
 
 	wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
 
-	os_free(ssid->eap.password);
+	bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 	ssid->eap.password = hash;
 	ssid->eap.password_len = 16;
 	ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1194,7 +1262,7 @@
 			   line, (unsigned int) *len);
 	}
 	os_memcpy(key, buf, *len);
-	os_free(buf);
+	str_clear_free(buf);
 	res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
 	if (res >= 0 && (size_t) res < sizeof(title))
 		wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
@@ -1283,6 +1351,52 @@
 
 #ifdef CONFIG_P2P
 
+static int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data,
+					    struct wpa_ssid *ssid, int line,
+					    const char *value)
+{
+	if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+	    os_strcmp(value, "any") == 0) {
+		os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN);
+		wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any");
+		return 0;
+	}
+	if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.",
+			   line, value);
+		return -1;
+	}
+	ssid->bssid_set = 1;
+	wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR,
+		   MAC2STR(ssid->go_p2p_dev_addr));
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+	char *value;
+	int res;
+
+	if (is_zero_ether_addr(ssid->go_p2p_dev_addr))
+		return NULL;
+
+	value = os_malloc(20);
+	if (value == NULL)
+		return NULL;
+	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
+	if (res < 0 || res >= 20) {
+		os_free(value);
+		return NULL;
+	}
+	value[20 - 1] = '\0';
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
 					    struct wpa_ssid *ssid, int line,
 					    const char *value)
@@ -1541,6 +1655,7 @@
 	{ STRe(dh_file) },
 	{ STRe(subject_match) },
 	{ STRe(altsubject_match) },
+	{ STRe(domain_suffix_match) },
 	{ STRe(ca_cert2) },
 	{ STRe(ca_path2) },
 	{ STRe(client_cert2) },
@@ -1549,6 +1664,7 @@
 	{ STRe(dh_file2) },
 	{ STRe(subject_match2) },
 	{ STRe(altsubject_match2) },
+	{ STRe(domain_suffix_match2) },
 	{ STRe(phase1) },
 	{ STRe(phase2) },
 	{ STRe(pcsc) },
@@ -1565,6 +1681,7 @@
 	{ INTe(engine) },
 	{ INTe(engine2) },
 	{ INT(eapol_flags) },
+	{ INTe(sim_num) },
 #endif /* IEEE8021X_EAPOL */
 	{ FUNC_KEY(wep_key0) },
 	{ FUNC_KEY(wep_key1) },
@@ -1592,6 +1709,7 @@
 	{ STR(bgscan) },
 	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
 #ifdef CONFIG_P2P
+	{ FUNC(go_p2p_dev_addr) },
 	{ FUNC(p2p_client_list) },
 	{ FUNC(psk_list) },
 #endif /* CONFIG_P2P */
@@ -1599,6 +1717,8 @@
 	{ INT_RANGE(disable_ht, 0, 1) },
 	{ INT_RANGE(disable_ht40, -1, 1) },
 	{ INT_RANGE(disable_sgi, 0, 1) },
+	{ INT_RANGE(disable_ldpc, 0, 1) },
+	{ INT_RANGE(ht40_intolerant, 0, 1) },
 	{ INT_RANGE(disable_max_amsdu, -1, 1) },
 	{ INT_RANGE(ampdu_factor, -1, 3) },
 	{ INT_RANGE(ampdu_density, -1, 7) },
@@ -1628,6 +1748,12 @@
 	{ INT(ap_max_inactivity) },
 	{ INT(dtim_period) },
 	{ INT(beacon_int) },
+#ifdef CONFIG_MACSEC
+	{ INT_RANGE(macsec_policy, 0, 1) },
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+	{ INT(update_identifier) },
+#endif /* CONFIG_HS20 */
 };
 
 #undef OFFSET
@@ -1646,7 +1772,7 @@
 #undef _FUNC
 #undef FUNC
 #undef FUNC_KEY
-#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0]))
+#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
 
 
 /**
@@ -1737,29 +1863,31 @@
 static void eap_peer_config_free(struct eap_peer_config *eap)
 {
 	os_free(eap->eap_methods);
-	os_free(eap->identity);
+	bin_clear_free(eap->identity, eap->identity_len);
 	os_free(eap->anonymous_identity);
-	os_free(eap->password);
+	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);
-	os_free(eap->private_key_passwd);
+	str_clear_free(eap->private_key_passwd);
 	os_free(eap->dh_file);
 	os_free(eap->subject_match);
 	os_free(eap->altsubject_match);
+	os_free(eap->domain_suffix_match);
 	os_free(eap->ca_cert2);
 	os_free(eap->ca_path2);
 	os_free(eap->client_cert2);
 	os_free(eap->private_key2);
-	os_free(eap->private_key2_passwd);
+	str_clear_free(eap->private_key2_passwd);
 	os_free(eap->dh_file2);
 	os_free(eap->subject_match2);
 	os_free(eap->altsubject_match2);
+	os_free(eap->domain_suffix_match2);
 	os_free(eap->phase1);
 	os_free(eap->phase2);
 	os_free(eap->pcsc);
-	os_free(eap->pin);
+	str_clear_free(eap->pin);
 	os_free(eap->engine_id);
 	os_free(eap->key_id);
 	os_free(eap->cert_id);
@@ -1767,12 +1895,13 @@
 	os_free(eap->key2_id);
 	os_free(eap->cert2_id);
 	os_free(eap->ca_cert2_id);
-	os_free(eap->pin2);
+	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);
-	os_free(eap->new_password);
+	bin_clear_free(eap->new_password, eap->new_password_len);
+	str_clear_free(eap->external_sim_resp);
 }
 #endif /* IEEE8021X_EAPOL */
 
@@ -1789,7 +1918,8 @@
 	struct psk_list_entry *psk;
 
 	os_free(ssid->ssid);
-	os_free(ssid->passphrase);
+	os_memset(ssid->psk, 0, sizeof(ssid->psk));
+	str_clear_free(ssid->passphrase);
 	os_free(ssid->ext_psk);
 #ifdef IEEE8021X_EAPOL
 	eap_peer_config_free(&ssid->eap);
@@ -1813,24 +1943,51 @@
 
 void wpa_config_free_cred(struct wpa_cred *cred)
 {
+	size_t i;
+
 	os_free(cred->realm);
-	os_free(cred->username);
-	os_free(cred->password);
+	str_clear_free(cred->username);
+	str_clear_free(cred->password);
 	os_free(cred->ca_cert);
 	os_free(cred->client_cert);
 	os_free(cred->private_key);
-	os_free(cred->private_key_passwd);
+	str_clear_free(cred->private_key_passwd);
 	os_free(cred->imsi);
-	os_free(cred->milenage);
+	str_clear_free(cred->milenage);
+	for (i = 0; i < cred->num_domain; i++)
+		os_free(cred->domain[i]);
 	os_free(cred->domain);
+	os_free(cred->domain_suffix_match);
 	os_free(cred->eap_method);
 	os_free(cred->phase1);
 	os_free(cred->phase2);
 	os_free(cred->excluded_ssid);
+	os_free(cred->roaming_partner);
+	os_free(cred->provisioning_sp);
+	for (i = 0; i < cred->num_req_conn_capab; i++)
+		os_free(cred->req_conn_capab_port[i]);
+	os_free(cred->req_conn_capab_port);
+	os_free(cred->req_conn_capab_proto);
 	os_free(cred);
 }
 
 
+void wpa_config_flush_blobs(struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	struct wpa_config_blob *blob, *prev;
+
+	blob = config->blobs;
+	config->blobs = NULL;
+	while (blob) {
+		prev = blob;
+		blob = blob->next;
+		wpa_config_free_blob(prev);
+	}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
 /**
  * wpa_config_free - Free configuration data
  * @config: Configuration data from wpa_config_read()
@@ -1840,9 +1997,6 @@
  */
 void wpa_config_free(struct wpa_config *config)
 {
-#ifndef CONFIG_NO_CONFIG_BLOBS
-	struct wpa_config_blob *blob, *prevblob;
-#endif /* CONFIG_NO_CONFIG_BLOBS */
 	struct wpa_ssid *ssid, *prev = NULL;
 	struct wpa_cred *cred, *cprev;
 
@@ -1860,15 +2014,7 @@
 		wpa_config_free_cred(cprev);
 	}
 
-#ifndef CONFIG_NO_CONFIG_BLOBS
-	blob = config->blobs;
-	prevblob = NULL;
-	while (blob) {
-		prevblob = blob;
-		blob = blob->next;
-		wpa_config_free_blob(prevblob);
-	}
-#endif /* CONFIG_NO_CONFIG_BLOBS */
+	wpa_config_flush_blobs(config);
 
 	wpabuf_free(config->wps_vendor_ext_m1);
 	os_free(config->ctrl_interface);
@@ -1877,7 +2023,7 @@
 	os_free(config->pkcs11_engine_path);
 	os_free(config->pkcs11_module_path);
 	os_free(config->pcsc_reader);
-	os_free(config->pcsc_pin);
+	str_clear_free(config->pcsc_pin);
 	os_free(config->driver_param);
 	os_free(config->device_name);
 	os_free(config->manufacturer);
@@ -1888,6 +2034,7 @@
 	os_free(config->p2p_ssid_postfix);
 	os_free(config->pssid);
 	os_free(config->p2p_pref_chan);
+	os_free(config->p2p_no_go_freq.range);
 	os_free(config->autoscan);
 	os_free(config->freq_list);
 	wpabuf_free(config->wps_nfc_dh_pubkey);
@@ -1896,6 +2043,8 @@
 	os_free(config->ext_password_backend);
 	os_free(config->sae_groups);
 	wpabuf_free(config->ap_vendor_elements);
+	os_free(config->osu_dir);
+	os_free(config->wowlan_triggers);
 	os_free(config);
 }
 
@@ -2029,11 +2178,13 @@
 	ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
 	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
 	ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
+	ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_HT_OVERRIDES
 	ssid->disable_ht = DEFAULT_DISABLE_HT;
 	ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
 	ssid->disable_sgi = DEFAULT_DISABLE_SGI;
+	ssid->disable_ldpc = DEFAULT_DISABLE_LDPC;
 	ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
 	ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
 	ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
@@ -2256,7 +2407,7 @@
 					wpa_printf(MSG_DEBUG, "Do not allow "
 						   "key_data field to be "
 						   "exposed");
-					os_free(res);
+					str_clear_free(res);
 					return os_strdup("*");
 				}
 
@@ -2291,17 +2442,93 @@
 }
 
 
+static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
+					      const char *value)
+{
+	u8 *proto;
+	int **port;
+	int *ports, *nports;
+	const char *pos;
+	unsigned int num_ports;
+
+	proto = os_realloc_array(cred->req_conn_capab_proto,
+				 cred->num_req_conn_capab + 1, sizeof(u8));
+	if (proto == NULL)
+		return -1;
+	cred->req_conn_capab_proto = proto;
+
+	port = os_realloc_array(cred->req_conn_capab_port,
+				cred->num_req_conn_capab + 1, sizeof(int *));
+	if (port == NULL)
+		return -1;
+	cred->req_conn_capab_port = port;
+
+	proto[cred->num_req_conn_capab] = atoi(value);
+
+	pos = os_strchr(value, ':');
+	if (pos == NULL) {
+		port[cred->num_req_conn_capab] = NULL;
+		cred->num_req_conn_capab++;
+		return 0;
+	}
+	pos++;
+
+	ports = NULL;
+	num_ports = 0;
+
+	while (*pos) {
+		nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+		if (nports == NULL) {
+			os_free(ports);
+			return -1;
+		}
+		ports = nports;
+		ports[num_ports++] = atoi(pos);
+
+		pos = os_strchr(pos, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+	if (nports == NULL) {
+		os_free(ports);
+		return -1;
+	}
+	ports = nports;
+	ports[num_ports] = -1;
+
+	port[cred->num_req_conn_capab] = ports;
+	cred->num_req_conn_capab++;
+	return 0;
+}
+
+
 int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
 			const char *value, int line)
 {
 	char *val;
 	size_t len;
 
+	if (os_strcmp(var, "temporary") == 0) {
+		cred->temporary = atoi(value);
+		return 0;
+	}
+
 	if (os_strcmp(var, "priority") == 0) {
 		cred->priority = atoi(value);
 		return 0;
 	}
 
+	if (os_strcmp(var, "sp_priority") == 0) {
+		int prio = atoi(value);
+		if (prio < 0 || prio > 255)
+			return -1;
+		cred->sp_priority = prio;
+		return 0;
+	}
+
 	if (os_strcmp(var, "pcsc") == 0) {
 		cred->pcsc = atoi(value);
 		return 0;
@@ -2326,12 +2553,55 @@
 
 	if (os_strcmp(var, "password") == 0 &&
 	    os_strncmp(value, "ext:", 4) == 0) {
-		os_free(cred->password);
+		str_clear_free(cred->password);
 		cred->password = os_strdup(value);
 		cred->ext_password = 1;
 		return 0;
 	}
 
+	if (os_strcmp(var, "update_identifier") == 0) {
+		cred->update_identifier = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
+		cred->min_dl_bandwidth_home = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
+		cred->min_ul_bandwidth_home = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
+		cred->min_dl_bandwidth_roaming = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
+		cred->min_ul_bandwidth_roaming = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "max_bss_load") == 0) {
+		cred->max_bss_load = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "req_conn_capab") == 0)
+		return wpa_config_set_cred_req_conn_capab(cred, value);
+
+	if (os_strcmp(var, "ocsp") == 0) {
+		cred->ocsp = atoi(value);
+		return 0;
+	}
+
+	if (os_strcmp(var, "sim_num") == 0) {
+		cred->sim_num = atoi(value);
+		return 0;
+	}
+
 	val = wpa_config_parse_string(value, &len);
 	if (val == NULL) {
 		wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
@@ -2346,13 +2616,13 @@
 	}
 
 	if (os_strcmp(var, "username") == 0) {
-		os_free(cred->username);
+		str_clear_free(cred->username);
 		cred->username = val;
 		return 0;
 	}
 
 	if (os_strcmp(var, "password") == 0) {
-		os_free(cred->password);
+		str_clear_free(cred->password);
 		cred->password = val;
 		cred->ext_password = 0;
 		return 0;
@@ -2377,7 +2647,7 @@
 	}
 
 	if (os_strcmp(var, "private_key_passwd") == 0) {
-		os_free(cred->private_key_passwd);
+		str_clear_free(cred->private_key_passwd);
 		cred->private_key_passwd = val;
 		return 0;
 	}
@@ -2389,14 +2659,28 @@
 	}
 
 	if (os_strcmp(var, "milenage") == 0) {
-		os_free(cred->milenage);
+		str_clear_free(cred->milenage);
 		cred->milenage = val;
 		return 0;
 	}
 
+	if (os_strcmp(var, "domain_suffix_match") == 0) {
+		os_free(cred->domain_suffix_match);
+		cred->domain_suffix_match = val;
+		return 0;
+	}
+
 	if (os_strcmp(var, "domain") == 0) {
-		os_free(cred->domain);
-		cred->domain = val;
+		char **new_domain;
+		new_domain = os_realloc_array(cred->domain,
+					      cred->num_domain + 1,
+					      sizeof(char *));
+		if (new_domain == NULL) {
+			os_free(val);
+			return -1;
+		}
+		new_domain[cred->num_domain++] = val;
+		cred->domain = new_domain;
 		return 0;
 	}
 
@@ -2426,6 +2710,21 @@
 		return 0;
 	}
 
+	if (os_strcmp(var, "required_roaming_consortium") == 0) {
+		if (len < 3 || len > sizeof(cred->required_roaming_consortium))
+		{
+			wpa_printf(MSG_ERROR, "Line %d: invalid "
+				   "required_roaming_consortium length %d "
+				   "(3..15 expected)", line, (int) len);
+			os_free(val);
+			return -1;
+		}
+		os_memcpy(cred->required_roaming_consortium, val, len);
+		cred->required_roaming_consortium_len = len;
+		os_free(val);
+		return 0;
+	}
+
 	if (os_strcmp(var, "excluded_ssid") == 0) {
 		struct excluded_ssid *e;
 
@@ -2454,6 +2753,69 @@
 		return 0;
 	}
 
+	if (os_strcmp(var, "roaming_partner") == 0) {
+		struct roaming_partner *p;
+		char *pos;
+
+		p = os_realloc_array(cred->roaming_partner,
+				     cred->num_roaming_partner + 1,
+				     sizeof(struct roaming_partner));
+		if (p == NULL) {
+			os_free(val);
+			return -1;
+		}
+		cred->roaming_partner = p;
+
+		p = &cred->roaming_partner[cred->num_roaming_partner];
+
+		pos = os_strchr(val, ',');
+		if (pos == NULL) {
+			os_free(val);
+			return -1;
+		}
+		*pos++ = '\0';
+		if (pos - val - 1 >= (int) sizeof(p->fqdn)) {
+			os_free(val);
+			return -1;
+		}
+		os_memcpy(p->fqdn, val, pos - val);
+
+		p->exact_match = atoi(pos);
+
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			os_free(val);
+			return -1;
+		}
+		*pos++ = '\0';
+
+		p->priority = atoi(pos);
+
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			os_free(val);
+			return -1;
+		}
+		*pos++ = '\0';
+
+		if (os_strlen(pos) >= sizeof(p->country)) {
+			os_free(val);
+			return -1;
+		}
+		os_memcpy(p->country, pos, os_strlen(pos) + 1);
+
+		cred->num_roaming_partner++;
+		os_free(val);
+
+		return 0;
+	}
+
+	if (os_strcmp(var, "provisioning_sp") == 0) {
+		os_free(cred->provisioning_sp);
+		cred->provisioning_sp = val;
+		return 0;
+	}
+
 	if (line) {
 		wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
 			   line, var);
@@ -2465,6 +2827,275 @@
 }
 
 
+static char * alloc_int_str(int val)
+{
+	char *buf;
+
+	buf = os_malloc(20);
+	if (buf == NULL)
+		return NULL;
+	os_snprintf(buf, 20, "%d", val);
+	return buf;
+}
+
+
+static char * alloc_strdup(const char *str)
+{
+	if (str == NULL)
+		return NULL;
+	return os_strdup(str);
+}
+
+
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
+{
+	if (os_strcmp(var, "temporary") == 0)
+		return alloc_int_str(cred->temporary);
+
+	if (os_strcmp(var, "priority") == 0)
+		return alloc_int_str(cred->priority);
+
+	if (os_strcmp(var, "sp_priority") == 0)
+		return alloc_int_str(cred->sp_priority);
+
+	if (os_strcmp(var, "pcsc") == 0)
+		return alloc_int_str(cred->pcsc);
+
+	if (os_strcmp(var, "eap") == 0) {
+		if (!cred->eap_method)
+			return NULL;
+		return alloc_strdup(eap_get_name(cred->eap_method[0].vendor,
+						 cred->eap_method[0].method));
+	}
+
+	if (os_strcmp(var, "update_identifier") == 0)
+		return alloc_int_str(cred->update_identifier);
+
+	if (os_strcmp(var, "min_dl_bandwidth_home") == 0)
+		return alloc_int_str(cred->min_dl_bandwidth_home);
+
+	if (os_strcmp(var, "min_ul_bandwidth_home") == 0)
+		return alloc_int_str(cred->min_ul_bandwidth_home);
+
+	if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0)
+		return alloc_int_str(cred->min_dl_bandwidth_roaming);
+
+	if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0)
+		return alloc_int_str(cred->min_ul_bandwidth_roaming);
+
+	if (os_strcmp(var, "max_bss_load") == 0)
+		return alloc_int_str(cred->max_bss_load);
+
+	if (os_strcmp(var, "req_conn_capab") == 0) {
+		unsigned int i;
+		char *buf, *end, *pos;
+		int ret;
+
+		if (!cred->num_req_conn_capab)
+			return NULL;
+
+		buf = os_malloc(4000);
+		if (buf == NULL)
+			return NULL;
+		pos = buf;
+		end = pos + 4000;
+		for (i = 0; i < cred->num_req_conn_capab; i++) {
+			int *ports;
+
+			ret = os_snprintf(pos, end - pos, "%s%u",
+					  i > 0 ? "\n" : "",
+					  cred->req_conn_capab_proto[i]);
+			if (ret < 0 || ret >= end - pos)
+				return buf;
+			pos += ret;
+
+			ports = cred->req_conn_capab_port[i];
+			if (ports) {
+				int j;
+				for (j = 0; ports[j] != -1; j++) {
+					ret = os_snprintf(pos, end - pos,
+							  "%s%d",
+							  j > 0 ? "," : ":",
+							  ports[j]);
+					if (ret < 0 || ret >= end - pos)
+						return buf;
+					pos += ret;
+				}
+			}
+		}
+
+		return buf;
+	}
+
+	if (os_strcmp(var, "ocsp") == 0)
+		return alloc_int_str(cred->ocsp);
+
+	if (os_strcmp(var, "realm") == 0)
+		return alloc_strdup(cred->realm);
+
+	if (os_strcmp(var, "username") == 0)
+		return alloc_strdup(cred->username);
+
+	if (os_strcmp(var, "password") == 0) {
+		if (!cred->password)
+			return NULL;
+		return alloc_strdup("*");
+	}
+
+	if (os_strcmp(var, "ca_cert") == 0)
+		return alloc_strdup(cred->ca_cert);
+
+	if (os_strcmp(var, "client_cert") == 0)
+		return alloc_strdup(cred->client_cert);
+
+	if (os_strcmp(var, "private_key") == 0)
+		return alloc_strdup(cred->private_key);
+
+	if (os_strcmp(var, "private_key_passwd") == 0) {
+		if (!cred->private_key_passwd)
+			return NULL;
+		return alloc_strdup("*");
+	}
+
+	if (os_strcmp(var, "imsi") == 0)
+		return alloc_strdup(cred->imsi);
+
+	if (os_strcmp(var, "milenage") == 0) {
+		if (!(cred->milenage))
+			return NULL;
+		return alloc_strdup("*");
+	}
+
+	if (os_strcmp(var, "domain_suffix_match") == 0)
+		return alloc_strdup(cred->domain_suffix_match);
+
+	if (os_strcmp(var, "domain") == 0) {
+		unsigned int i;
+		char *buf, *end, *pos;
+		int ret;
+
+		if (!cred->num_domain)
+			return NULL;
+
+		buf = os_malloc(4000);
+		if (buf == NULL)
+			return NULL;
+		pos = buf;
+		end = pos + 4000;
+
+		for (i = 0; i < cred->num_domain; i++) {
+			ret = os_snprintf(pos, end - pos, "%s%s",
+					  i > 0 ? "\n" : "", cred->domain[i]);
+			if (ret < 0 || ret >= end - pos)
+				return buf;
+			pos += ret;
+		}
+
+		return buf;
+	}
+
+	if (os_strcmp(var, "phase1") == 0)
+		return alloc_strdup(cred->phase1);
+
+	if (os_strcmp(var, "phase2") == 0)
+		return alloc_strdup(cred->phase2);
+
+	if (os_strcmp(var, "roaming_consortium") == 0) {
+		size_t buflen;
+		char *buf;
+
+		if (!cred->roaming_consortium_len)
+			return NULL;
+		buflen = cred->roaming_consortium_len * 2 + 1;
+		buf = os_malloc(buflen);
+		if (buf == NULL)
+			return NULL;
+		wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
+				 cred->roaming_consortium_len);
+		return buf;
+	}
+
+	if (os_strcmp(var, "required_roaming_consortium") == 0) {
+		size_t buflen;
+		char *buf;
+
+		if (!cred->required_roaming_consortium_len)
+			return NULL;
+		buflen = cred->required_roaming_consortium_len * 2 + 1;
+		buf = os_malloc(buflen);
+		if (buf == NULL)
+			return NULL;
+		wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
+				 cred->required_roaming_consortium_len);
+		return buf;
+	}
+
+	if (os_strcmp(var, "excluded_ssid") == 0) {
+		unsigned int i;
+		char *buf, *end, *pos;
+
+		if (!cred->num_excluded_ssid)
+			return NULL;
+
+		buf = os_malloc(4000);
+		if (buf == NULL)
+			return NULL;
+		pos = buf;
+		end = pos + 4000;
+
+		for (i = 0; i < cred->num_excluded_ssid; i++) {
+			struct excluded_ssid *e;
+			int ret;
+
+			e = &cred->excluded_ssid[i];
+			ret = os_snprintf(pos, end - pos, "%s%s",
+					  i > 0 ? "\n" : "",
+					  wpa_ssid_txt(e->ssid, e->ssid_len));
+			if (ret < 0 || ret >= end - pos)
+				return buf;
+			pos += ret;
+		}
+
+		return buf;
+	}
+
+	if (os_strcmp(var, "roaming_partner") == 0) {
+		unsigned int i;
+		char *buf, *end, *pos;
+
+		if (!cred->num_roaming_partner)
+			return NULL;
+
+		buf = os_malloc(4000);
+		if (buf == NULL)
+			return NULL;
+		pos = buf;
+		end = pos + 4000;
+
+		for (i = 0; i < cred->num_roaming_partner; i++) {
+			struct roaming_partner *p;
+			int ret;
+
+			p = &cred->roaming_partner[i];
+			ret = os_snprintf(pos, end - pos, "%s%s,%d,%u,%s",
+					  i > 0 ? "\n" : "",
+					  p->fqdn, p->exact_match, p->priority,
+					  p->country);
+			if (ret < 0 || ret >= end - pos)
+				return buf;
+			pos += ret;
+		}
+
+		return buf;
+	}
+
+	if (os_strcmp(var, "provisioning_sp") == 0)
+		return alloc_strdup(cred->provisioning_sp);
+
+	return NULL;
+}
+
+
 struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
 {
 	struct wpa_cred *cred;
@@ -2499,6 +3130,7 @@
 	if (cred == NULL)
 		return NULL;
 	cred->id = id;
+	cred->sim_num = DEFAULT_USER_SELECTED_SIM;
 	if (last)
 		last->next = cred;
 	else
@@ -2579,7 +3211,7 @@
 {
 	if (blob) {
 		os_free(blob->name);
-		os_free(blob->data);
+		bin_clear_free(blob->data, blob->len);
 		os_free(blob);
 	}
 }
@@ -2643,6 +3275,7 @@
 	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
 	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
 	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
+	config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
 	config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
 	config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
 	config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
@@ -2653,6 +3286,7 @@
 	config->wmm_ac_params[1] = ac_bk;
 	config->wmm_ac_params[2] = ac_vi;
 	config->wmm_ac_params[3] = ac_vo;
+	config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
 
 	if (ctrl_interface)
 		config->ctrl_interface = os_strdup(ctrl_interface);
@@ -2770,6 +3404,27 @@
 }
 
 
+static int wpa_config_process_bgscan(const struct global_parse_data *data,
+				     struct wpa_config *config, int line,
+				     const char *pos)
+{
+	size_t len;
+	char *tmp;
+	int res;
+
+	tmp = wpa_config_parse_string(pos, &len);
+	if (tmp == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: failed to parse %s",
+			   line, data->name);
+		return -1;
+	}
+
+	res = wpa_global_config_parse_str(data, config, line, tmp);
+	os_free(tmp);
+	return res;
+}
+
+
 static int wpa_global_config_parse_bin(const struct global_parse_data *data,
 				       struct wpa_config *config, int line,
 				       const char *pos)
@@ -2808,12 +3463,39 @@
 	freqs = wpa_config_parse_int_array(value);
 	if (freqs == NULL)
 		return -1;
+	if (freqs[0] == 0) {
+		os_free(freqs);
+		freqs = NULL;
+	}
 	os_free(config->freq_list);
 	config->freq_list = freqs;
 	return 0;
 }
 
 
+#ifdef CONFIG_P2P
+static int wpa_global_config_parse_ipv4(const struct global_parse_data *data,
+					struct wpa_config *config, int line,
+					const char *pos)
+{
+	u32 *dst;
+	struct hostapd_ip_addr addr;
+
+	if (hostapd_parse_ip_addr(pos, &addr) < 0)
+		return -1;
+	if (addr.af != AF_INET)
+		return -1;
+
+	dst = (u32 *) (((u8 *) config) + (long) data->param1);
+	os_memcpy(dst, &addr.u.v4.s_addr, 4);
+	wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
+		   WPA_GET_BE32((u8 *) dst));
+
+	return 0;
+}
+#endif /* CONFIG_P2P */
+
+
 static int wpa_config_process_country(const struct global_parse_data *data,
 				      struct wpa_config *config, int line,
 				      const char *pos)
@@ -2998,6 +3680,26 @@
 	wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
 	return -1;
 }
+
+
+static int wpa_config_process_p2p_no_go_freq(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	int ret;
+
+	ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
+		   config->p2p_no_go_freq.num);
+
+	return 0;
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -3070,6 +3772,19 @@
 }
 
 
+#ifdef CONFIG_CTRL_IFACE
+static int wpa_config_process_no_ctrl_interface(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL");
+	os_free(config->ctrl_interface);
+	config->ctrl_interface = NULL;
+	return 0;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
 #ifdef OFFSET
 #undef OFFSET
 #endif /* OFFSET */
@@ -3085,14 +3800,21 @@
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
 #define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, OFFSET(f), NULL, NULL
 
 static const struct global_parse_data global_fields[] = {
 #ifdef CONFIG_CTRL_IFACE
 	{ STR(ctrl_interface), 0 },
+	{ FUNC_NO_VAR(no_ctrl_interface), 0 },
 	{ STR(ctrl_interface_group), 0 } /* deprecated */,
 #endif /* CONFIG_CTRL_IFACE */
+#ifdef CONFIG_MACSEC
+	{ INT_RANGE(eapol_version, 1, 3), 0 },
+#else /* CONFIG_MACSEC */
 	{ INT_RANGE(eapol_version, 1, 2), 0 },
+#endif /* CONFIG_MACSEC */
 	{ INT(ap_scan), 0 },
+	{ FUNC(bgscan), 0 },
 	{ INT(disable_scan_offload), 0 },
 	{ INT(fast_reauth), 0 },
 	{ STR(opensc_engine_path), 0 },
@@ -3100,6 +3822,7 @@
 	{ STR(pkcs11_module_path), 0 },
 	{ STR(pcsc_reader), 0 },
 	{ STR(pcsc_pin), 0 },
+	{ INT(external_sim), 0 },
 	{ STR(driver_param), 0 },
 	{ INT(dot11RSNAConfigPMKLifetime), 0 },
 	{ INT(dot11RSNAConfigPMKReauthThreshold), 0 },
@@ -3125,18 +3848,28 @@
 	{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
 	{ INT(p2p_listen_reg_class), 0 },
 	{ INT(p2p_listen_channel), 0 },
-	{ INT(p2p_oper_reg_class), 0 },
-	{ INT(p2p_oper_channel), 0 },
+	{ INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
+	{ INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
 	{ INT_RANGE(p2p_go_intent, 0, 15), 0 },
 	{ STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
 	{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
 	{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
 	{ INT(p2p_group_idle), 0 },
+	{ INT_RANGE(p2p_passphrase_len, 8, 63),
+	  CFG_CHANGED_P2P_PASSPHRASE_LEN },
 	{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
+	{ FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
+	{ INT_RANGE(p2p_add_cli_chan, 0, 1), 0 },
+	{ INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 },
 	{ INT(p2p_go_ht40), 0 },
+	{ INT(p2p_go_vht), 0 },
 	{ INT(p2p_disabled), 0 },
 	{ INT(p2p_no_group_iface), 0 },
 	{ INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
+	{ IPV4(ip_addr_go), 0 },
+	{ IPV4(ip_addr_mask), 0 },
+	{ IPV4(ip_addr_start), 0 },
+	{ IPV4(ip_addr_end), 0 },
 #endif /* CONFIG_P2P */
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },
@@ -3173,6 +3906,9 @@
 	{ INT(scan_cur_freq), 0 },
 	{ INT(sched_scan_interval), 0 },
 	{ INT(tdls_external_control), 0},
+	{ STR(osu_dir), 0 },
+	{ STR(wowlan_triggers), 0 },
+	{ INT(p2p_search_delay), 0},
 };
 
 #undef FUNC
@@ -3183,7 +3919,8 @@
 #undef STR
 #undef STR_RANGE
 #undef BIN
-#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
+#undef IPV4
+#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
 
 
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 3fe46e3..52add9d 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -19,12 +19,14 @@
 #define DEFAULT_P2P_GO_INTENT 7
 #define DEFAULT_P2P_INTRA_BSS 1
 #define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60)
+#define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0
 #define DEFAULT_BSS_MAX_COUNT 200
 #define DEFAULT_BSS_EXPIRATION_AGE 180
 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
 #define DEFAULT_MAX_NUM_STA 128
 #define DEFAULT_ACCESS_NETWORK_TYPE 15
 #define DEFAULT_SCAN_CUR_FREQ 0
+#define DEFAULT_P2P_SEARCH_DELAY 500
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -52,6 +54,11 @@
 	int id;
 
 	/**
+	 * temporary - Whether this credential is temporary and not to be saved
+	 */
+	int temporary;
+
+	/**
 	 * priority - Priority group
 	 *
 	 * By default, all networks and credentials get the same priority group
@@ -150,12 +157,37 @@
 	char *milenage;
 
 	/**
-	 * domain - Home service provider FQDN
+	 * domain_suffix_match - Constraint for server domain name
+	 *
+	 * If set, this FQDN is used as a suffix match requirement for the AAA
+	 * server certificate in SubjectAltName dNSName element(s). If a
+	 * matching dNSName is found, this constraint is met. If no dNSName
+	 * values are present, this constraint is matched against SubjetName CN
+	 * using same suffix match comparison. Suffix match here means that the
+	 * host/domain name is compared one label at a time starting from the
+	 * top-level domain and all the labels in @domain_suffix_match shall be
+	 * included in the certificate. The certificate may include additional
+	 * sub-level labels in addition to the required labels.
+	 *
+	 * For example, domain_suffix_match=example.com would match
+	 * test.example.com but would not match test-example.com.
+	 */
+	char *domain_suffix_match;
+
+	/**
+	 * domain - Home service provider FQDN(s)
 	 *
 	 * This is used to compare against the Domain Name List to figure out
-	 * whether the AP is operated by the Home SP.
+	 * whether the AP is operated by the Home SP. Multiple domain entries
+	 * can be used to configure alternative FQDNs that will be considered
+	 * home networks.
 	 */
-	char *domain;
+	char **domain;
+
+	/**
+	 * num_domain - Number of FQDNs in the domain array
+	 */
+	size_t num_domain;
 
 	/**
 	 * roaming_consortium - Roaming Consortium OI
@@ -175,6 +207,9 @@
 	 */
 	size_t roaming_consortium_len;
 
+	u8 required_roaming_consortium[15];
+	size_t required_roaming_consortium_len;
+
 	/**
 	 * eap_method - EAP method to use
 	 *
@@ -203,6 +238,66 @@
 		size_t ssid_len;
 	} *excluded_ssid;
 	size_t num_excluded_ssid;
+
+	struct roaming_partner {
+		char fqdn[128];
+		int exact_match;
+		u8 priority;
+		char country[3];
+	} *roaming_partner;
+	size_t num_roaming_partner;
+
+	int update_identifier;
+
+	/**
+	 * provisioning_sp - FQDN of the SP that provisioned the credential
+	 */
+	char *provisioning_sp;
+
+	/**
+	 * sp_priority - Credential priority within a provisioning SP
+	 *
+	 * This is the priority of the credential among all credentials
+	 * provisionined by the same SP (i.e., for entries that have identical
+	 * provisioning_sp value). The range of this priority is 0-255 with 0
+	 * being the highest and 255 the lower priority.
+	 */
+	int sp_priority;
+
+	unsigned int min_dl_bandwidth_home;
+	unsigned int min_ul_bandwidth_home;
+	unsigned int min_dl_bandwidth_roaming;
+	unsigned int min_ul_bandwidth_roaming;
+
+	/**
+	 * max_bss_load - Maximum BSS Load Channel Utilization (1..255)
+	 * This value is used as the maximum channel utilization for network
+	 * selection purposes for home networks. If the AP does not advertise
+	 * BSS Load or if the limit would prevent any connection, this
+	 * constraint will be ignored.
+	 */
+	unsigned int max_bss_load;
+
+	unsigned int num_req_conn_capab;
+	u8 *req_conn_capab_proto;
+	int **req_conn_capab_port;
+
+	/**
+	 * 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;
+
+	/**
+	 * sim_num - User selected SIM identifier
+	 *
+	 * This variable is used for identifying which SIM is used if the system
+	 * has more than one.
+	 */
+	int sim_num;
 };
 
 
@@ -222,6 +317,7 @@
 #define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
 #define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
 #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
+#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -301,6 +397,18 @@
 	int ap_scan;
 
 	/**
+	 * bgscan - Background scan and roaming parameters or %NULL if none
+	 *
+	 * This is an optional set of parameters for background scanning and
+	 * roaming within a network (ESS). For more detailed information see
+	 * ssid block documentation.
+	 *
+	 * The variable defines default bgscan behavior for all BSS station
+	 * networks except for those which have their own bgscan configuration.
+	 */
+	 char *bgscan;
+
+	/**
 	 * disable_scan_offload - Disable automatic offloading of scan requests
 	 *
 	 * By default, %wpa_supplicant tries to offload scanning if the driver
@@ -425,6 +533,11 @@
 	char *pcsc_pin;
 
 	/**
+	 * external_sim - Use external processing for SIM/USIM operations
+	 */
+	int external_sim;
+
+	/**
 	 * driver_param - Driver interface parameters
 	 *
 	 * This text string is passed to the selected driver interface with the
@@ -572,7 +685,10 @@
 	int p2p_intra_bss;
 	unsigned int num_p2p_pref_chan;
 	struct p2p_channel *p2p_pref_chan;
+	struct wpa_freq_range_list p2p_no_go_freq;
+	int p2p_add_cli_chan;
 	int p2p_ignore_shared_freq;
+	int p2p_optimize_listen_chan;
 
 	struct wpabuf *wps_vendor_ext_m1;
 
@@ -601,6 +717,14 @@
 	int p2p_group_idle;
 
 	/**
+	 * p2p_passphrase_len - Passphrase length (8..63) for P2P GO
+	 *
+	 * This parameter controls the length of the random passphrase that is
+	 * generated at the GO.
+	 */
+	unsigned int p2p_passphrase_len;
+
+	/**
 	 * bss_max_count - Maximum number of BSS entries to keep in memory
 	 */
 	unsigned int bss_max_count;
@@ -793,6 +917,16 @@
 	int p2p_go_ht40;
 
 	/**
+	 * p2p_go_vht - Default mode for VHT 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_vht;
+
+	/**
 	 * p2p_disabled - Whether P2P operations are disabled for this interface
 	 */
 	int p2p_disabled;
@@ -884,9 +1018,39 @@
 	 * to specify the TDLS link to get established to the driver. The
 	 * driver requests the TDLS setup to the supplicant only for the
 	 * specified TDLS peers.
-	 *
 	 */
 	int tdls_external_control;
+
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+
+	/**
+	 * osu_dir - OSU provider information directory
+	 *
+	 * If set, allow FETCH_OSU control interface command to be used to fetch
+	 * OSU provider information into all APs and store the results in this
+	 * directory.
+	 */
+	char *osu_dir;
+
+	/**
+	 * wowlan_triggers - Wake-on-WLAN triggers
+	 *
+	 * If set, these wowlan triggers will be configured.
+	 */
+	char *wowlan_triggers;
+
+	/**
+	 * p2p_search_delay - Extra delay between concurrent search iterations
+	 *
+	 * Add extra delay (in milliseconds) between search iterations when
+	 * there is a concurrent operation to make p2p_find friendlier to
+	 * concurrent operations by avoiding it from taking 100% of radio
+	 * resources.
+	 */
+	unsigned int p2p_search_delay;
 };
 
 
@@ -918,6 +1082,7 @@
 			 struct wpa_config_blob *blob);
 void wpa_config_free_blob(struct wpa_config_blob *blob);
 int wpa_config_remove_blob(struct wpa_config *config, const char *name);
+void wpa_config_flush_blobs(struct wpa_config *config);
 
 struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id);
 struct wpa_cred * wpa_config_add_cred(struct wpa_config *config);
@@ -925,6 +1090,7 @@
 void wpa_config_free_cred(struct wpa_cred *cred);
 int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
 			const char *value, int line);
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var);
 
 struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 					   const char *driver_param);
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index bb0e536..73ad57a 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -219,6 +219,7 @@
 	if (cred == NULL)
 		return NULL;
 	cred->id = id;
+	cred->sim_num = DEFAULT_USER_SELECTED_SIM;
 
 	while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
 		if (os_strcmp(pos, "}") == 0) {
@@ -351,8 +352,8 @@
 	FILE *f;
 	char buf[512], *pos;
 	int errors = 0, line = 0;
-	struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
-	struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL;
+	struct wpa_ssid *ssid, *tail, *head;
+	struct wpa_cred *cred, *cred_tail, *cred_head;
 	struct wpa_config *config;
 	int id = 0;
 	int cred_id = 0;
@@ -368,8 +369,12 @@
 			   "structure");
 		return NULL;
 	}
-	head = config->ssid;
-	cred_head = config->cred;
+	tail = head = config->ssid;
+	while (tail && tail->next)
+		tail = tail->next;
+	cred_tail = cred_head = config->cred;
+	while (cred_tail && cred_tail->next)
+		cred_tail = cred_tail->next;
 
 	wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
 	f = fopen(name, "r");
@@ -606,6 +611,15 @@
 
 #ifdef CONFIG_P2P
 
+static void write_go_p2p_dev_addr(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value = wpa_config_get(ssid, "go_p2p_dev_addr");
+	if (value == NULL)
+		return;
+	fprintf(f, "\tgo_p2p_dev_addr=%s\n", value);
+	os_free(value);
+}
+
 static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
 {
 	char *value = wpa_config_get(ssid, "p2p_client_list");
@@ -653,6 +667,7 @@
 	write_auth_alg(f, ssid);
 	STR(bgscan);
 	STR(autoscan);
+	STR(scan_freq);
 #ifdef IEEE8021X_EAPOL
 	write_eap(f, ssid);
 	STR(identity);
@@ -666,6 +681,7 @@
 	STR(dh_file);
 	STR(subject_match);
 	STR(altsubject_match);
+	STR(domain_suffix_match);
 	STR(ca_cert2);
 	STR(ca_path2);
 	STR(client_cert2);
@@ -674,6 +690,7 @@
 	STR(dh_file2);
 	STR(subject_match2);
 	STR(altsubject_match2);
+	STR(domain_suffix_match2);
 	STR(phase1);
 	STR(phase2);
 	STR(pcsc);
@@ -699,6 +716,8 @@
 	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);
 #endif /* IEEE8021X_EAPOL */
 	INT(mode);
 	INT(frequency);
@@ -711,11 +730,18 @@
 #endif /* CONFIG_IEEE80211W */
 	STR(id_str);
 #ifdef CONFIG_P2P
+	write_go_p2p_dev_addr(f, ssid);
 	write_p2p_client_list(f, ssid);
 	write_psk_list(f, ssid);
 #endif /* CONFIG_P2P */
 	INT(dtim_period);
 	INT(beacon_int);
+#ifdef CONFIG_MACSEC
+	INT(macsec_policy);
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+	INT(update_identifier);
+#endif /* CONFIG_HS20 */
 
 #undef STR
 #undef INT
@@ -725,6 +751,8 @@
 
 static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
 {
+	size_t i;
+
 	if (cred->priority)
 		fprintf(f, "\tpriority=%d\n", cred->priority);
 	if (cred->pcsc)
@@ -750,10 +778,12 @@
 		fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
 	if (cred->milenage)
 		fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
-	if (cred->domain)
-		fprintf(f, "\tdomain=\"%s\"\n", cred->domain);
+	for (i = 0; i < cred->num_domain; i++)
+		fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]);
+	if (cred->domain_suffix_match)
+		fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
+			cred->domain_suffix_match);
 	if (cred->roaming_consortium_len) {
-		size_t i;
 		fprintf(f, "\troaming_consortium=");
 		for (i = 0; i < cred->roaming_consortium_len; i++)
 			fprintf(f, "%02x", cred->roaming_consortium[i]);
@@ -763,14 +793,15 @@
 		const char *name;
 		name = eap_get_name(cred->eap_method[0].vendor,
 				    cred->eap_method[0].method);
-		fprintf(f, "\teap=%s\n", name);
+		if (name)
+			fprintf(f, "\teap=%s\n", name);
 	}
 	if (cred->phase1)
 		fprintf(f, "\tphase1=\"%s\"\n", cred->phase1);
 	if (cred->phase2)
 		fprintf(f, "\tphase2=\"%s\"\n", cred->phase2);
 	if (cred->excluded_ssid) {
-		size_t i, j;
+		size_t j;
 		for (i = 0; i < cred->num_excluded_ssid; i++) {
 			struct excluded_ssid *e = &cred->excluded_ssid[i];
 			fprintf(f, "\texcluded_ssid=");
@@ -779,6 +810,70 @@
 			fprintf(f, "\n");
 		}
 	}
+	if (cred->roaming_partner) {
+		for (i = 0; i < cred->num_roaming_partner; i++) {
+			struct roaming_partner *p = &cred->roaming_partner[i];
+			fprintf(f, "\troaming_partner=\"%s,%d,%u,%s\"\n",
+				p->fqdn, p->exact_match, p->priority,
+				p->country);
+		}
+	}
+	if (cred->update_identifier)
+		fprintf(f, "\tupdate_identifier=%d\n", cred->update_identifier);
+
+	if (cred->provisioning_sp)
+		fprintf(f, "\tprovisioning_sp=\"%s\"\n", cred->provisioning_sp);
+	if (cred->sp_priority)
+		fprintf(f, "\tsp_priority=%d\n", cred->sp_priority);
+
+	if (cred->min_dl_bandwidth_home)
+		fprintf(f, "\tmin_dl_bandwidth_home=%u\n",
+			cred->min_dl_bandwidth_home);
+	if (cred->min_ul_bandwidth_home)
+		fprintf(f, "\tmin_ul_bandwidth_home=%u\n",
+			cred->min_ul_bandwidth_home);
+	if (cred->min_dl_bandwidth_roaming)
+		fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n",
+			cred->min_dl_bandwidth_roaming);
+	if (cred->min_ul_bandwidth_roaming)
+		fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n",
+			cred->min_ul_bandwidth_roaming);
+
+	if (cred->max_bss_load)
+		fprintf(f, "\tmax_bss_load=%u\n",
+			cred->max_bss_load);
+
+	if (cred->ocsp)
+		fprintf(f, "\tocsp=%d\n", cred->ocsp);
+
+	if (cred->num_req_conn_capab) {
+		for (i = 0; i < cred->num_req_conn_capab; i++) {
+			int *ports;
+
+			fprintf(f, "\treq_conn_capab=%u",
+				cred->req_conn_capab_proto[i]);
+			ports = cred->req_conn_capab_port[i];
+			if (ports) {
+				int j;
+				for (j = 0; ports[j] != -1; j++) {
+					fprintf(f, "%s%d", j > 0 ? "," : ":",
+						ports[j]);
+				}
+			}
+			fprintf(f, "\n");
+		}
+	}
+
+	if (cred->required_roaming_consortium_len) {
+		fprintf(f, "\trequired_roaming_consortium=");
+		for (i = 0; i < cred->required_roaming_consortium_len; i++)
+			fprintf(f, "%02x",
+				cred->required_roaming_consortium[i]);
+		fprintf(f, "\n");
+	}
+
+	if (cred->sim_num != DEFAULT_USER_SELECTED_SIM)
+		fprintf(f, "\tsim_num=%d\n", cred->sim_num);
 }
 
 
@@ -924,6 +1019,9 @@
 		fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
 	if (config->p2p_group_idle)
 		fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+	if (config->p2p_passphrase_len)
+		fprintf(f, "p2p_passphrase_len=%u\n",
+			config->p2p_passphrase_len);
 	if (config->p2p_pref_chan) {
 		unsigned int i;
 		fprintf(f, "p2p_pref_chan=");
@@ -934,8 +1032,23 @@
 		}
 		fprintf(f, "\n");
 	}
+	if (config->p2p_no_go_freq.num) {
+		char *val = freq_range_list_str(&config->p2p_no_go_freq);
+		if (val) {
+			fprintf(f, "p2p_no_go_freq=%s\n", val);
+			os_free(val);
+		}
+	}
+	if (config->p2p_add_cli_chan)
+		fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan);
+	if (config->p2p_optimize_listen_chan !=
+	    DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN)
+		fprintf(f, "p2p_optimize_listen_chan=%d\n",
+			config->p2p_optimize_listen_chan);
 	if (config->p2p_go_ht40)
 		fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
+	if (config->p2p_go_vht)
+		fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht);
 	if (config->p2p_disabled)
 		fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled);
 	if (config->p2p_no_group_iface)
@@ -1049,9 +1162,23 @@
 		fprintf(f, "sched_scan_interval=%u\n",
 			config->sched_scan_interval);
 
+	if (config->external_sim)
+		fprintf(f, "external_sim=%d\n", config->external_sim);
+
 	if (config->tdls_external_control)
-		fprintf(f, "tdls_external_control=%u\n",
+		fprintf(f, "tdls_external_control=%d\n",
 			config->tdls_external_control);
+
+	if (config->wowlan_triggers)
+		fprintf(f, "wowlan_triggers=%s\n",
+			config->wowlan_triggers);
+
+	if (config->bgscan)
+		fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
+
+	if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
+		fprintf(f, "p2p_search_delay=%u\n",
+			config->p2p_search_delay);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1079,6 +1206,8 @@
 	wpa_config_write_global(f, config);
 
 	for (cred = config->cred; cred; cred = cred->next) {
+		if (cred->temporary)
+			continue;
 		fprintf(f, "\ncred={\n");
 		wpa_config_write_cred(f, cred);
 		fprintf(f, "}\n");
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index c6ea963..ab474ff 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -30,9 +30,11 @@
 #define DEFAULT_DISABLE_HT 0
 #define DEFAULT_DISABLE_HT40 0
 #define DEFAULT_DISABLE_SGI 0
+#define DEFAULT_DISABLE_LDPC 0
 #define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
 #define DEFAULT_AMPDU_FACTOR -1 /* no change */
 #define DEFAULT_AMPDU_DENSITY -1 /* no change */
+#define DEFAULT_USER_SELECTED_SIM 1
 
 struct psk_list_entry {
 	struct dl_list list;
@@ -131,6 +133,11 @@
 	int bssid_set;
 
 	/**
+	 * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
+	 */
+	u8 go_p2p_dev_addr[ETH_ALEN];
+
+	/**
 	 * psk - WPA pre-shared key (256 bits)
 	 */
 	u8 psk[32];
@@ -310,12 +317,13 @@
 	 * 4 = P2P Group Formation (used internally; not in configuration
 	 * files)
 	 *
-	 * Note: IBSS can only be used with key_mgmt NONE (plaintext and
-	 * static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In
-	 * addition, ap_scan has to be set to 2 for IBSS. WPA-None requires
-	 * following network block options: proto=WPA, key_mgmt=WPA-NONE,
-	 * pairwise=NONE, group=TKIP (or CCMP, but not both), and psk must also
-	 * be set (either directly or using ASCII passphrase).
+	 * Note: IBSS can only be used with key_mgmt NONE (plaintext and static
+	 * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE
+	 * (fixed group key TKIP/CCMP) is available for backwards compatibility,
+	 * but its use is deprecated. WPA-None requires following network block
+	 * options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or
+	 * CCMP, but not both), and psk must also be set (either directly or
+	 * using ASCII passphrase).
 	 */
 	enum wpas_mode {
 		WPAS_MODE_INFRA = 0,
@@ -394,6 +402,8 @@
 
 	int ht40;
 
+	int vht;
+
 	/**
 	 * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
 	 *
@@ -491,13 +501,6 @@
 	 */
 	int export_keys;
 
-#ifdef ANDROID_P2P
-	/**
-	 * assoc_retry - Number of times association should be retried.
-	 */
-	int assoc_retry;
-#endif
-
 #ifdef CONFIG_HT_OVERRIDES
 	/**
 	 * disable_ht - Disable HT (IEEE 802.11n) for this network
@@ -524,6 +527,19 @@
 	int disable_sgi;
 
 	/**
+	 * disable_ldpc - Disable LDPC for this network
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to 1 to have it disabled.
+	 */
+	int disable_ldpc;
+
+	/**
+	 * ht40_intolerant - Indicate 40 MHz intolerant for this network
+	 */
+	int ht40_intolerant;
+
+	/**
 	 * disable_max_amsdu - Disable MAX A-MSDU
 	 *
 	 * A-MDSU will be 3839 bytes when disabled, or 7935
@@ -610,7 +626,7 @@
 	/**
 	 * disabled_until - Network block disabled until this time if non-zero
 	 */
-	struct os_time disabled_until;
+	struct os_reltime disabled_until;
 
 	/**
 	 * parent_cred - Pointer to parent wpa_cred entry
@@ -620,6 +636,21 @@
 	 * dereferences since it may not be updated in all cases.
 	 */
 	void *parent_cred;
+
+#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;
+#endif /* CONFIG_MACSEC */
+
+#ifdef CONFIG_HS20
+	int update_identifier;
+#endif /* CONFIG_HS20 */
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 1b5e237..199f04f 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -623,6 +623,9 @@
 	wpa_config_write_reg_dword(hk, TEXT("okc"), config->okc, 0);
 	wpa_config_write_reg_dword(hk, TEXT("pmf"), config->pmf, 0);
 
+	wpa_config_write_reg_dword(hk, TEXT("external_sim"),
+				   config->external_sim, 0);
+
 	return 0;
 }
 
@@ -927,6 +930,9 @@
 		  MGMT_FRAME_PROTECTION_DEFAULT);
 #endif /* CONFIG_IEEE80211W */
 	STR(id_str);
+#ifdef CONFIG_HS20
+	INT(update_identifier);
+#endif /* CONFIG_HS20 */
 
 #undef STR
 #undef INT
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 6335605..79806d9 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/uuid.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -39,104 +40,14 @@
 #include "blacklist.h"
 #include "autoscan.h"
 #include "wnm_sta.h"
-
-extern struct wpa_driver_ops *wpa_drivers[];
+#include "offchannel.h"
 
 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,
 						  char *buf, int len);
-
-
-static int pno_start(struct wpa_supplicant *wpa_s)
-{
-	int ret;
-	size_t i, num_ssid;
-	struct wpa_ssid *ssid;
-	struct wpa_driver_scan_params params;
-
-	if (wpa_s->pno)
-		return 0;
-
-	if ((wpa_s->wpa_state > WPA_SCANNING) &&
-	    (wpa_s->wpa_state <= WPA_COMPLETED)) {
-		wpa_printf(MSG_ERROR, "PNO: In assoc process");
-		return -EAGAIN;
-	}
-
-	if (wpa_s->wpa_state == WPA_SCANNING) {
-		wpa_supplicant_cancel_sched_scan(wpa_s);
-		wpa_supplicant_cancel_scan(wpa_s);
-	}
-
-	os_memset(&params, 0, sizeof(params));
-
-	num_ssid = 0;
-	ssid = wpa_s->conf->ssid;
-	while (ssid) {
-		if (!wpas_network_disabled(wpa_s, ssid))
-			num_ssid++;
-		ssid = ssid->next;
-	}
-	if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
-		wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
-			   "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
-		num_ssid = WPAS_MAX_SCAN_SSIDS;
-	}
-
-	if (num_ssid == 0) {
-		wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
-		return -1;
-	}
-
-	params.filter_ssids = os_malloc(sizeof(struct wpa_driver_scan_filter) *
-					num_ssid);
-	if (params.filter_ssids == NULL)
-		return -1;
-	i = 0;
-	ssid = wpa_s->conf->ssid;
-	while (ssid) {
-		if (!wpas_network_disabled(wpa_s, ssid)) {
-			params.ssids[i].ssid = ssid->ssid;
-			params.ssids[i].ssid_len = ssid->ssid_len;
-			params.num_ssids++;
-			os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
-				  ssid->ssid_len);
-			params.filter_ssids[i].ssid_len = ssid->ssid_len;
-			params.num_filter_ssids++;
-			i++;
-			if (i == num_ssid)
-				break;
-		}
-		ssid = ssid->next;
-	}
-
-	if (wpa_s->conf->filter_rssi)
-		params.filter_rssi = wpa_s->conf->filter_rssi;
-
-	ret = wpa_drv_sched_scan(wpa_s, &params, 10 * 1000);
-	os_free(params.filter_ssids);
-	if (ret == 0)
-		wpa_s->pno = 1;
-	return ret;
-}
-
-
-static int pno_stop(struct wpa_supplicant *wpa_s)
-{
-	int ret = 0;
-
-	if (wpa_s->pno) {
-		wpa_s->pno = 0;
-		ret = wpa_drv_stop_sched_scan(wpa_s);
-	}
-
-	if (wpa_s->wpa_state == WPA_SCANNING)
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
-
-	return ret;
-}
-
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
+					char *val);
 
 static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
 {
@@ -184,7 +95,7 @@
 	struct wpa_ssid *c;
 
 	/*
-	 * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | “”
+	 * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
 	 * SSID_SPEC ::= ssid <SSID_HEX>
 	 * BSSID_SPEC ::= bssid <BSSID_HEX>
 	 */
@@ -297,6 +208,72 @@
 }
 
 
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos)
+{
+	char *name = pos;
+	struct wpa_config_blob *blob;
+	size_t len;
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+	len = os_strlen(pos);
+	if (len & 1)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name);
+	blob = os_zalloc(sizeof(*blob));
+	if (blob == NULL)
+		return -1;
+	blob->name = os_strdup(name);
+	blob->data = os_malloc(len / 2);
+	if (blob->name == NULL || blob->data == NULL) {
+		wpa_config_free_blob(blob);
+		return -1;
+	}
+
+	if (hexstr2bin(pos, blob->data, len / 2) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data");
+		wpa_config_free_blob(blob);
+		return -1;
+	}
+	blob->len = len / 2;
+
+	wpa_config_set_blob(wpa_s->conf, blob);
+
+	return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *params;
+	char *pos;
+	int *freqs = NULL;
+	int ret;
+
+	if (atoi(cmd)) {
+		params = os_strchr(cmd, ' ');
+		os_free(wpa_s->manual_sched_scan_freqs);
+		if (params) {
+			params++;
+			pos = os_strstr(params, "freq=");
+			if (pos)
+				freqs = freq_range_to_channel_list(wpa_s,
+								   pos + 5);
+		}
+		wpa_s->manual_sched_scan_freqs = freqs;
+		ret = wpas_start_pno(wpa_s);
+	} else {
+		ret = wpas_stop_pno(wpa_s);
+	}
+	return ret;
+}
+
+
 static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
 					 char *cmd)
 {
@@ -354,17 +331,21 @@
 		wps_testing_dummy_cred = atoi(value);
 		wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
 			   wps_testing_dummy_cred);
+	} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
+		wps_corrupt_pkhash = atoi(value);
+		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+			   wps_corrupt_pkhash);
 #endif /* CONFIG_WPS_TESTING */
 	} else if (os_strcasecmp(cmd, "ampdu") == 0) {
 		if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
 			ret = -1;
+#ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
 	} else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
 		extern unsigned int tdls_testing;
 		tdls_testing = strtol(value, NULL, 0);
 		wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
 #endif /* CONFIG_TDLS_TESTING */
-#ifdef CONFIG_TDLS
 	} else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
 		int disabled = atoi(value);
 		wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
@@ -376,10 +357,7 @@
 		wpa_tdls_enable(wpa_s->wpa, !disabled);
 #endif /* CONFIG_TDLS */
 	} else if (os_strcasecmp(cmd, "pno") == 0) {
-		if (atoi(value))
-			ret = pno_start(wpa_s);
-		else
-			ret = pno_stop(wpa_s);
+		ret = wpas_ctrl_pno(wpa_s, value);
 	} else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
 		int disabled = atoi(value);
 		if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
@@ -426,7 +404,11 @@
 		ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
 #ifdef CONFIG_WIFI_DISPLAY
 	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
-		wifi_display_enable(wpa_s->global, !!atoi(value));
+		int enabled = !!atoi(value);
+		if (enabled && !wpa_s->global->p2p)
+			ret = -1;
+		else
+			wifi_display_enable(wpa_s->global, enabled);
 #endif /* CONFIG_WIFI_DISPLAY */
 	} else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
 		ret = set_bssid_filter(wpa_s, value);
@@ -434,6 +416,14 @@
 		ret = set_disallow_aps(wpa_s, value);
 	} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
 		wpa_s->no_keep_alive = !!atoi(value);
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
+		wpa_s->ext_mgmt_frame_handling = !!atoi(value);
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifndef CONFIG_NO_CONFIG_BLOBS
+	} else if (os_strcmp(cmd, "blob") == 0) {
+		ret = wpas_ctrl_set_blob(wpa_s, value);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -461,8 +451,13 @@
 					  wpa_s->conf->country[1]);
 #ifdef CONFIG_WIFI_DISPLAY
 	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
-		res = os_snprintf(buf, buflen, "%d",
-				  wpa_s->global->wifi_display);
+		int enabled;
+		if (wpa_s->global->p2p == NULL ||
+		    wpa_s->global->p2p_disabled)
+			enabled = 0;
+		else
+			enabled = wpa_s->global->wifi_display;
+		res = os_snprintf(buf, buflen, "%d", enabled);
 		if (res < 0 || (unsigned int) res >= buflen)
 			return -1;
 		return res;
@@ -569,9 +564,8 @@
 		   MAC2STR(peer));
 
 	if ((wpa_s->conf->tdls_external_control) &&
-	     wpa_tdls_is_external_setup(wpa_s->wpa)) {
+	    wpa_tdls_is_external_setup(wpa_s->wpa))
 		return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
-	}
 
 	wpa_tdls_remove(wpa_s->wpa, peer);
 
@@ -590,6 +584,13 @@
 	u8 peer[ETH_ALEN];
 	int ret;
 
+	if (os_strcmp(addr, "*") == 0) {
+		/* remove everyone */
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *");
+		wpa_tdls_teardown_peers(wpa_s->wpa);
+		return 0;
+	}
+
 	if (hwaddr_aton(addr, peer)) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
 			   "address '%s'", addr);
@@ -600,9 +601,8 @@
 		   MAC2STR(peer));
 
 	if ((wpa_s->conf->tdls_external_control) &&
-	     wpa_tdls_is_external_setup(wpa_s->wpa)) {
+	    wpa_tdls_is_external_setup(wpa_s->wpa))
 		return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
-	}
 
 	if (wpa_tdls_is_external_setup(wpa_s->wpa))
 		ret = wpa_tdls_teardown_link(
@@ -614,6 +614,22 @@
 	return ret;
 }
 
+
+static int ctrl_iface_get_capability_tdls(
+	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+	int ret;
+
+	ret = os_snprintf(buf, buflen, "%s\n",
+			  wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ?
+			  (wpa_s->drv_flags &
+			   WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ?
+			   "EXTERNAL" : "INTERNAL") : "UNSUPPORTED");
+	if (ret < 0 || (size_t) ret > buflen)
+		return -1;
+	return ret;
+}
+
 #endif /* CONFIG_TDLS */
 
 
@@ -656,7 +672,7 @@
 	u8 *_p2p_dev_addr = NULL;
 #endif /* CONFIG_AP */
 
-	if (cmd == NULL || os_strcmp(cmd, "any") == 0 || cmd[0] == '\0') {
+	if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
 		_bssid = NULL;
 #ifdef CONFIG_P2P
 	} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
@@ -805,7 +821,8 @@
 	else if (hwaddr_aton(cmd, bssid))
 		return -1;
 
-	return wpas_wps_start_nfc(wpa_s, _bssid);
+	return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
+				  0, 0);
 }
 
 
@@ -877,6 +894,15 @@
 	size_t len;
 	struct wpabuf *buf;
 	int ret;
+	char *freq;
+	int forced_freq = 0;
+
+	freq = strstr(pos, " freq=");
+	if (freq) {
+		*freq = '\0';
+		freq += 6;
+		forced_freq = atoi(freq);
+	}
 
 	len = os_strlen(pos);
 	if (len & 0x01)
@@ -891,7 +917,7 @@
 		return -1;
 	}
 
-	ret = wpas_wps_nfc_tag_read(wpa_s, buf);
+	ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
 	wpabuf_free(buf);
 
 	return ret;
@@ -900,12 +926,12 @@
 
 static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
 					      char *reply, size_t max_len,
-					      int cr)
+					      int ndef)
 {
 	struct wpabuf *buf;
 	int res;
 
-	buf = wpas_wps_nfc_handover_req(wpa_s, cr);
+	buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
 	if (buf == NULL)
 		return -1;
 
@@ -920,25 +946,65 @@
 }
 
 
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
+					      char *reply, size_t max_len,
+					      int ndef)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
+	if (buf == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
+		return -1;
+	}
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+#endif /* CONFIG_P2P */
+
+
 static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
 					  char *cmd, char *reply,
 					  size_t max_len)
 {
 	char *pos;
+	int ndef;
 
 	pos = os_strchr(cmd, ' ');
 	if (pos == NULL)
 		return -1;
 	*pos++ = '\0';
 
-	if (os_strcmp(cmd, "NDEF") != 0)
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
 		return -1;
 
 	if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+		if (!ndef)
+			return -1;
 		return wpas_ctrl_nfc_get_handover_req_wps(
-			wpa_s, reply, max_len, os_strcmp(pos, "WPS-CR") == 0);
+			wpa_s, reply, max_len, ndef);
 	}
 
+#ifdef CONFIG_P2P
+	if (os_strcmp(pos, "P2P-CR") == 0) {
+		return wpas_ctrl_nfc_get_handover_req_p2p(
+			wpa_s, reply, max_len, ndef);
+	}
+#endif /* CONFIG_P2P */
+
 	return -1;
 }
 
@@ -965,6 +1031,30 @@
 }
 
 
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
+					      char *reply, size_t max_len,
+					      int ndef, int tag)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
+	if (buf == NULL)
+		return -1;
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+#endif /* CONFIG_P2P */
+
+
 static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
 					  char *cmd, char *reply,
 					  size_t max_len)
@@ -988,70 +1078,29 @@
 	if (pos2)
 		*pos2++ = '\0';
 	if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+		if (!ndef)
+			return -1;
 		return wpas_ctrl_nfc_get_handover_sel_wps(
 			wpa_s, reply, max_len, ndef,
 			os_strcmp(pos, "WPS-CR") == 0, pos2);
 	}
 
+#ifdef CONFIG_P2P
+	if (os_strcmp(pos, "P2P-CR") == 0) {
+		return wpas_ctrl_nfc_get_handover_sel_p2p(
+			wpa_s, reply, max_len, ndef, 0);
+	}
+
+	if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
+		return wpas_ctrl_nfc_get_handover_sel_p2p(
+			wpa_s, reply, max_len, ndef, 1);
+	}
+#endif /* CONFIG_P2P */
+
 	return -1;
 }
 
 
-static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
-					 char *cmd, char *reply,
-					 size_t max_len)
-{
-	size_t len;
-	struct wpabuf *buf;
-	int ret;
-
-	len = os_strlen(cmd);
-	if (len & 0x01)
-		return -1;
-	len /= 2;
-
-	buf = wpabuf_alloc(len);
-	if (buf == NULL)
-		return -1;
-	if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
-		wpabuf_free(buf);
-		return -1;
-	}
-
-	ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf);
-	wpabuf_free(buf);
-
-	return ret;
-}
-
-
-static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
-					 char *cmd)
-{
-	size_t len;
-	struct wpabuf *buf;
-	int ret;
-
-	len = os_strlen(cmd);
-	if (len & 0x01)
-		return -1;
-	len /= 2;
-
-	buf = wpabuf_alloc(len);
-	if (buf == NULL)
-		return -1;
-	if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
-		wpabuf_free(buf);
-		return -1;
-	}
-
-	ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf);
-	wpabuf_free(buf);
-
-	return ret;
-}
-
-
 static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
 					 char *cmd)
 {
@@ -1059,39 +1108,62 @@
 	struct wpabuf *req, *sel;
 	int ret;
 	char *pos, *role, *type, *pos2;
+#ifdef CONFIG_P2P
+	char *freq;
+	int forced_freq = 0;
+
+	freq = strstr(cmd, " freq=");
+	if (freq) {
+		*freq = '\0';
+		freq += 6;
+		forced_freq = atoi(freq);
+	}
+#endif /* CONFIG_P2P */
 
 	role = cmd;
 	pos = os_strchr(role, ' ');
-	if (pos == NULL)
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
 		return -1;
+	}
 	*pos++ = '\0';
 
 	type = pos;
 	pos = os_strchr(type, ' ');
-	if (pos == NULL)
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report");
 		return -1;
+	}
 	*pos++ = '\0';
 
 	pos2 = os_strchr(pos, ' ');
-	if (pos2 == NULL)
+	if (pos2 == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
 		return -1;
+	}
 	*pos2++ = '\0';
 
 	len = os_strlen(pos);
-	if (len & 0x01)
+	if (len & 0x01) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report");
 		return -1;
+	}
 	len /= 2;
 
 	req = wpabuf_alloc(len);
-	if (req == NULL)
+	if (req == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message");
 		return -1;
+	}
 	if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report");
 		wpabuf_free(req);
 		return -1;
 	}
 
 	len = os_strlen(pos2);
 	if (len & 0x01) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report");
 		wpabuf_free(req);
 		return -1;
 	}
@@ -1099,17 +1171,38 @@
 
 	sel = wpabuf_alloc(len);
 	if (sel == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message");
 		wpabuf_free(req);
 		return -1;
 	}
 	if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report");
 		wpabuf_free(req);
 		wpabuf_free(sel);
 		return -1;
 	}
 
+	wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d",
+		   role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel));
+
 	if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
 		ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
+#ifdef CONFIG_AP
+	} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
+	{
+		ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
+		if (ret < 0)
+			ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+	} else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
+	{
+		ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0);
+	} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
+	{
+		ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel,
+						   forced_freq);
+#endif /* CONFIG_P2P */
 	} else {
 		wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
 			   "reported: role=%s type=%s", role, type);
@@ -1118,6 +1211,9 @@
 	wpabuf_free(req);
 	wpabuf_free(sel);
 
+	if (ret)
+		wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
+
 	return ret;
 }
 
@@ -1426,7 +1522,12 @@
 {
 	char *pos, *end, tmp[30];
 	int res, verbose, wps, ret;
+#ifdef CONFIG_HS20
+	const u8 *hs20;
+#endif /* CONFIG_HS20 */
 
+	if (os_strcmp(params, "-DRIVER") == 0)
+		return wpa_drv_status(wpa_s, buf, buflen);
 	verbose = os_strcmp(params, "-VERBOSE") == 0;
 	wps = os_strcmp(params, "-WPS") == 0;
 	pos = buf;
@@ -1562,10 +1663,16 @@
 
 #ifdef CONFIG_HS20
 	if (wpa_s->current_bss &&
-	    wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) &&
+	    (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+					  HS20_IE_VENDOR_TYPE)) &&
 	    wpa_s->wpa_proto == WPA_PROTO_RSN &&
 	    wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
-		ret = os_snprintf(pos, end - pos, "hs20=1\n");
+		int release = 1;
+		if (hs20[1] >= 5) {
+			u8 rel_num = (hs20[6] & 0xf0) >> 4;
+			release = rel_num + 1;
+		}
+		ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
@@ -1576,17 +1683,43 @@
 		char *type;
 
 		for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+			size_t i;
+
 			if (wpa_s->current_ssid->parent_cred != cred)
 				continue;
-			if (!cred->domain)
-				continue;
 
+			if (cred->provisioning_sp) {
+				ret = os_snprintf(pos, end - pos,
+						  "provisioning_sp=%s\n",
+						  cred->provisioning_sp);
+				if (ret < 0 || ret >= end - pos)
+					return pos - buf;
+				pos += ret;
+			}
+
+			if (!cred->domain)
+				goto no_domain;
+
+			i = 0;
+			if (wpa_s->current_bss && wpa_s->current_bss->anqp) {
+				struct wpabuf *names =
+					wpa_s->current_bss->anqp->domain_name;
+				for (i = 0; names && i < cred->num_domain; i++)
+				{
+					if (domain_name_list_contains(
+						    names, cred->domain[i], 1))
+						break;
+				}
+				if (i == cred->num_domain)
+					i = 0; /* show first entry by default */
+			}
 			ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
-					  cred->domain);
+					  cred->domain[i]);
 			if (ret < 0 || ret >= end - pos)
 				return pos - buf;
 			pos += ret;
 
+		no_domain:
 			if (wpa_s->current_bss == NULL ||
 			    wpa_s->current_bss->anqp == NULL)
 				res = -1;
@@ -1623,22 +1756,41 @@
 	if (res >= 0)
 		pos += res;
 
+#ifdef CONFIG_WPS
+	{
+		char uuid_str[100];
+		uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str));
+		ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str);
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_WPS */
+
 #ifdef ANDROID
-	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
-		     "id=%d state=%d BSSID=" MACSTR " SSID=%s",
-		     wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
-		     wpa_s->wpa_state,
-		     MAC2STR(wpa_s->bssid),
-		     wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
-		     wpa_ssid_txt(wpa_s->current_ssid->ssid,
-		     wpa_s->current_ssid->ssid_len) : "");
-	if (wpa_s->wpa_state == WPA_COMPLETED) {
-		struct wpa_ssid *ssid = wpa_s->current_ssid;
-		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- connection to "
-			MACSTR " completed %s [id=%d id_str=%s]",
-			MAC2STR(wpa_s->bssid), "(auth)",
-			ssid ? ssid->id : -1,
-			ssid && ssid->id_str ? ssid->id_str : "");
+	/*
+	 * Allow using the STATUS command with default behavior, say for debug,
+	 * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE
+	 * events with STATUS-NO_EVENTS.
+	 */
+	if (os_strcmp(params, "-NO_EVENTS")) {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+			     "id=%d state=%d BSSID=" MACSTR " SSID=%s",
+			     wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+			     wpa_s->wpa_state,
+			     MAC2STR(wpa_s->bssid),
+			     wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
+			     wpa_ssid_txt(wpa_s->current_ssid->ssid,
+					  wpa_s->current_ssid->ssid_len) : "");
+		if (wpa_s->wpa_state == WPA_COMPLETED) {
+			struct wpa_ssid *ssid = wpa_s->current_ssid;
+			wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
+				     "- connection to " MACSTR
+				     " completed %s [id=%d id_str=%s]",
+				     MAC2STR(wpa_s->bssid), "(auth)",
+				     ssid ? ssid->id : -1,
+				     ssid && ssid->id_str ? ssid->id_str : "");
+		}
 	}
 #endif /* ANDROID */
 
@@ -1723,19 +1875,16 @@
 	 * skipped when processing scan results.
 	 */
 	ret = wpa_blacklist_add(wpa_s, bssid);
-	if (ret != 0)
+	if (ret < 0)
 		return -1;
 	ret = wpa_blacklist_add(wpa_s, bssid);
-	if (ret != 0)
+	if (ret < 0)
 		return -1;
 	os_memcpy(buf, "OK\n", 3);
 	return 3;
 }
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_timestamp;
-
 static const char * debug_level_str(int level)
 {
 	switch (level) {
@@ -1900,7 +2049,8 @@
 				    const u8 *ie, size_t ie_len)
 {
 	struct wpa_ie_data data;
-	int first, ret;
+	char *start;
+	int ret;
 
 	ret = os_snprintf(pos, end - pos, "[%s-", proto);
 	if (ret < 0 || ret >= end - pos)
@@ -1915,62 +2065,58 @@
 		return pos;
 	}
 
-	first = 1;
+	start = pos;
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
-		ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+");
+		ret = os_snprintf(pos, end - pos, "%sEAP",
+				  pos == start ? "" : "+");
 		if (ret < 0 || ret >= end - pos)
 			return pos;
 		pos += ret;
-		first = 0;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
-		ret = os_snprintf(pos, end - pos, "%sPSK", first ? "" : "+");
+		ret = os_snprintf(pos, end - pos, "%sPSK",
+				  pos == start ? "" : "+");
 		if (ret < 0 || ret >= end - pos)
 			return pos;
 		pos += ret;
-		first = 0;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
-		ret = os_snprintf(pos, end - pos, "%sNone", first ? "" : "+");
+		ret = os_snprintf(pos, end - pos, "%sNone",
+				  pos == start ? "" : "+");
 		if (ret < 0 || ret >= end - pos)
 			return pos;
 		pos += ret;
-		first = 0;
 	}
 #ifdef CONFIG_IEEE80211R
 	if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 		ret = os_snprintf(pos, end - pos, "%sFT/EAP",
-				  first ? "" : "+");
+				  pos == start ? "" : "+");
 		if (ret < 0 || ret >= end - pos)
 			return pos;
 		pos += ret;
-		first = 0;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
 		ret = os_snprintf(pos, end - pos, "%sFT/PSK",
-				  first ? "" : "+");
+				  pos == start ? "" : "+");
 		if (ret < 0 || ret >= end - pos)
 			return pos;
 		pos += ret;
-		first = 0;
 	}
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
-				  first ? "" : "+");
+				  pos == start ? "" : "+");
 		if (ret < 0 || ret >= end - pos)
 			return pos;
 		pos += ret;
-		first = 0;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
-				  first ? "" : "+");
+				  pos == start ? "" : "+");
 		if (ret < 0 || ret >= end - pos)
 			return pos;
 		pos += ret;
-		first = 0;
 	}
 #endif /* CONFIG_IEEE80211W */
 
@@ -2004,10 +2150,8 @@
 		return pos;
 	if (wps_is_selected_pbc_registrar(wps_ie))
 		txt = "[WPS-PBC]";
-#ifdef CONFIG_WPS2
 	else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
 		txt = "[WPS-AUTH]";
-#endif /* CONFIG_WPS2 */
 	else if (wps_is_selected_pin_registrar(wps_ie))
 		txt = "[WPS-PIN]";
 	else
@@ -2046,6 +2190,8 @@
 	const u8 *ie, *ie2, *p2p;
 
 	p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+	if (!p2p)
+		p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE);
 	if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
 	    os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
 	    0)
@@ -2072,17 +2218,43 @@
 			return -1;
 		pos += ret;
 	}
-	if (bss->caps & IEEE80211_CAP_IBSS) {
-		ret = os_snprintf(pos, end - pos, "[IBSS]");
+	if (bss_is_dmg(bss)) {
+		const char *s;
+		ret = os_snprintf(pos, end - pos, "[DMG]");
 		if (ret < 0 || ret >= end - pos)
 			return -1;
 		pos += ret;
-	}
-	if (bss->caps & IEEE80211_CAP_ESS) {
-		ret = os_snprintf(pos, end - pos, "[ESS]");
+		switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+		case IEEE80211_CAP_DMG_IBSS:
+			s = "[IBSS]";
+			break;
+		case IEEE80211_CAP_DMG_AP:
+			s = "[ESS]";
+			break;
+		case IEEE80211_CAP_DMG_PBSS:
+			s = "[PBSS]";
+			break;
+		default:
+			s = "";
+			break;
+		}
+		ret = os_snprintf(pos, end - pos, "%s", s);
 		if (ret < 0 || ret >= end - pos)
 			return -1;
 		pos += ret;
+	} else {
+		if (bss->caps & IEEE80211_CAP_IBSS) {
+			ret = os_snprintf(pos, end - pos, "[IBSS]");
+			if (ret < 0 || ret >= end - pos)
+				return -1;
+			pos += ret;
+		}
+		if (bss->caps & IEEE80211_CAP_ESS) {
+			ret = os_snprintf(pos, end - pos, "[ESS]");
+			if (ret < 0 || ret >= end - pos)
+				return -1;
+			pos += ret;
+		}
 	}
 	if (p2p) {
 		ret = os_snprintf(pos, end - pos, "[P2P]");
@@ -2146,9 +2318,10 @@
 {
 	int id;
 	struct wpa_ssid *ssid;
+	char *pos;
 
 	/* cmd: "<network id>" or "any" */
-	if (os_strcmp(cmd, "any") == 0) {
+	if (os_strncmp(cmd, "any", 3) == 0) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
 		ssid = NULL;
 	} else {
@@ -2168,6 +2341,16 @@
 		}
 	}
 
+	pos = os_strstr(cmd, " freq=");
+	if (pos) {
+		int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
+		if (freqs) {
+			wpa_s->scan_req = MANUAL_SCAN_REQ;
+			os_free(wpa_s->manual_scan_freqs);
+			wpa_s->manual_scan_freqs = freqs;
+		}
+	}
+
 	wpa_supplicant_select_network(wpa_s, ssid);
 
 	return 0;
@@ -2352,6 +2535,39 @@
 }
 
 
+static int wpa_supplicant_ctrl_iface_update_network(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+	char *name, char *value)
+{
+	if (wpa_config_set(ssid, name, value, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
+			   "variable '%s'", name);
+		return -1;
+	}
+
+	if (os_strcmp(name, "bssid") != 0 &&
+	    os_strcmp(name, "priority") != 0)
+		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+	if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
+		/*
+		 * Invalidate the EAP session cache if anything in the current
+		 * or previously used configuration changes.
+		 */
+		eapol_sm_invalidate_cached_session(wpa_s->eapol);
+	}
+
+	if ((os_strcmp(name, "psk") == 0 &&
+	     value[0] == '"' && ssid->ssid_len) ||
+	    (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
+		wpa_config_update_psk(ssid);
+	else if (os_strcmp(name, "priority") == 0)
+		wpa_config_update_prio_list(wpa_s->conf);
+
+	return 0;
+}
+
+
 static int wpa_supplicant_ctrl_iface_set_network(
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
@@ -2383,32 +2599,8 @@
 		return -1;
 	}
 
-	if (wpa_config_set(ssid, name, value, 0) < 0) {
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
-			   "variable '%s'", name);
-		return -1;
-	}
-
-	if (os_strcmp(name, "bssid") != 0 &&
-	    os_strcmp(name, "priority") != 0)
-		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-
-	if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
-		/*
-		 * Invalidate the EAP session cache if anything in the current
-		 * or previously used configuration changes.
-		 */
-		eapol_sm_invalidate_cached_session(wpa_s->eapol);
-	}
-
-	if ((os_strcmp(name, "psk") == 0 &&
-	     value[0] == '"' && ssid->ssid_len) ||
-	    (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
-		wpa_config_update_psk(ssid);
-	else if (os_strcmp(name, "priority") == 0)
-		wpa_config_update_prio_list(wpa_s->conf);
-
-	return 0;
+	return wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
+							value);
 }
 
 
@@ -2456,6 +2648,59 @@
 }
 
 
+static int wpa_supplicant_ctrl_iface_dup_network(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	struct wpa_ssid *ssid_s, *ssid_d;
+	char *name, *id, *value;
+	int id_s, id_d, ret;
+
+	/* cmd: "<src network id> <dst network id> <variable name>" */
+	id = os_strchr(cmd, ' ');
+	if (id == NULL)
+		return -1;
+	*id++ = '\0';
+
+	name = os_strchr(id, ' ');
+	if (name == NULL)
+		return -1;
+	*name++ = '\0';
+
+	id_s = atoi(cmd);
+	id_d = atoi(id);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
+		   id_s, id_d, name);
+
+	ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
+	if (ssid_s == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+			   "network id=%d", id_s);
+		return -1;
+	}
+
+	ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
+	if (ssid_d == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+			   "network id=%d", id_s);
+		return -1;
+	}
+
+	value = wpa_config_get(ssid_s, name);
+	if (value == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+			   "variable '%s'", name);
+		return -1;
+	}
+
+	ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
+						       value);
+
+	os_free(value);
+
+	return ret;
+}
+
+
 static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
 						char *buf, size_t buflen)
 {
@@ -2476,7 +2721,7 @@
 		ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
 				  cred->id, cred->realm ? cred->realm : "",
 				  cred->username ? cred->username : "",
-				  cred->domain ? cred->domain : "",
+				  cred->domain ? cred->domain[0] : "",
 				  cred->imsi ? cred->imsi : "");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
@@ -2501,6 +2746,8 @@
 	if (cred == NULL)
 		return -1;
 
+	wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id);
+
 	ret = os_snprintf(buf, buflen, "%d\n", cred->id);
 	if (ret < 0 || (size_t) ret >= buflen)
 		return -1;
@@ -2513,12 +2760,21 @@
 {
 	struct wpa_ssid *ssid;
 	char str[20];
+	int id;
 
-	if (cred == NULL || wpa_config_remove_cred(wpa_s->conf, cred->id) < 0) {
+	if (cred == NULL) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
 		return -1;
 	}
 
+	id = cred->id;
+	if (wpa_config_remove_cred(wpa_s->conf, id) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
+		return -1;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id);
+
 	/* Remove any network entry created based on the removed credential */
 	ssid = wpa_s->conf->ssid;
 	while (ssid) {
@@ -2542,7 +2798,8 @@
 	int id;
 	struct wpa_cred *cred, *prev;
 
-	/* cmd: "<cred id>", "all", or "sp_fqdn=<FQDN>" */
+	/* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or
+	 * "provisioning_sp=<FQDN> */
 	if (os_strcmp(cmd, "all") == 0) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
 		cred = wpa_s->conf->cred;
@@ -2561,8 +2818,29 @@
 		while (cred) {
 			prev = cred;
 			cred = cred->next;
-			if (prev->domain &&
-			    os_strcmp(prev->domain, cmd + 8) == 0)
+			if (prev->domain) {
+				size_t i;
+				for (i = 0; i < prev->num_domain; i++) {
+					if (os_strcmp(prev->domain[i], cmd + 8)
+					    != 0)
+						continue;
+					wpas_ctrl_remove_cred(wpa_s, prev);
+					break;
+				}
+			}
+		}
+		return 0;
+	}
+
+	if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'",
+			   cmd + 16);
+		cred = wpa_s->conf->cred;
+		while (cred) {
+			prev = cred;
+			cred = cred->next;
+			if (prev->provisioning_sp &&
+			    os_strcmp(prev->provisioning_sp, cmd + 16) == 0)
 				wpas_ctrl_remove_cred(wpa_s, prev);
 		}
 		return 0;
@@ -2613,10 +2891,57 @@
 		return -1;
 	}
 
+	wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name);
+
 	return 0;
 }
 
 
+static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s,
+					      char *cmd, char *buf,
+					      size_t buflen)
+{
+	int id;
+	size_t res;
+	struct wpa_cred *cred;
+	char *name, *value;
+
+	/* cmd: "<cred id> <variable name>" */
+	name = os_strchr(cmd, ' ');
+	if (name == NULL)
+		return -1;
+	*name++ = '\0';
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'",
+		   id, name);
+
+	cred = wpa_config_get_cred(wpa_s->conf, id);
+	if (cred == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+			   id);
+		return -1;
+	}
+
+	value = wpa_config_get_cred_no_key(cred, name);
+	if (value == NULL) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'",
+			   name);
+		return -1;
+	}
+
+	res = os_strlcpy(buf, value, buflen);
+	if (res >= buflen) {
+		os_free(value);
+		return -1;
+	}
+
+	os_free(value);
+
+	return res;
+}
+
+
 #ifndef CONFIG_NO_CONFIG_WRITE
 static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
 {
@@ -2642,13 +2967,32 @@
 #endif /* CONFIG_NO_CONFIG_WRITE */
 
 
+struct cipher_info {
+	unsigned int capa;
+	const char *name;
+	int group_only;
+};
+
+static const struct cipher_info ciphers[] = {
+	{ WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 },
+	{ WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 },
+	{ WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 },
+	{ WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 },
+	{ WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 },
+	{ WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 },
+	{ WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 },
+	{ WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 }
+};
+
+
 static int ctrl_iface_get_capability_pairwise(int res, char *strict,
 					      struct wpa_driver_capa *capa,
 					      char *buf, size_t buflen)
 {
-	int ret, first = 1;
+	int ret;
 	char *pos, *end;
 	size_t len;
+	unsigned int i;
 
 	pos = buf;
 	end = pos + buflen;
@@ -2662,36 +3006,15 @@
 		return len;
 	}
 
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-		ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
-	}
-
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-		ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
-	}
-
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-		ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
-	}
-
-	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-		ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
+	for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+		if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) {
+			ret = os_snprintf(pos, end - pos, "%s%s",
+					  pos == buf ? "" : " ",
+					  ciphers[i].name);
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
 	}
 
 	return pos - buf;
@@ -2702,9 +3025,10 @@
 					   struct wpa_driver_capa *capa,
 					   char *buf, size_t buflen)
 {
-	int ret, first = 1;
+	int ret;
 	char *pos, *end;
 	size_t len;
+	unsigned int i;
 
 	pos = buf;
 	end = pos + buflen;
@@ -2718,45 +3042,15 @@
 		return len;
 	}
 
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-		ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
-	}
-
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-		ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
-	}
-
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-		ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
-	}
-
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) {
-		ret = os_snprintf(pos, end - pos, "%sWEP104",
-				  first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
-	}
-
-	if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) {
-		ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-		first = 0;
+	for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+		if (capa->enc & ciphers[i].capa) {
+			ret = os_snprintf(pos, end - pos, "%s%s",
+					  pos == buf ? "" : " ",
+					  ciphers[i].name);
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
 	}
 
 	return pos - buf;
@@ -2820,7 +3114,7 @@
 					   struct wpa_driver_capa *capa,
 					   char *buf, size_t buflen)
 {
-	int ret, first = 1;
+	int ret;
 	char *pos, *end;
 	size_t len;
 
@@ -2838,20 +3132,20 @@
 
 	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
 			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-		ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sRSN",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		first = 0;
 	}
 
 	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 			      WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
-		ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sWPA",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		first = 0;
 	}
 
 	return pos - buf;
@@ -2862,7 +3156,7 @@
 					      struct wpa_driver_capa *capa,
 					      char *buf, size_t buflen)
 {
-	int ret, first = 1;
+	int ret;
 	char *pos, *end;
 	size_t len;
 
@@ -2879,28 +3173,27 @@
 	}
 
 	if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
-		ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sOPEN",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		first = 0;
 	}
 
 	if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
 		ret = os_snprintf(pos, end - pos, "%sSHARED",
-				  first ? "" : " ");
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		first = 0;
 	}
 
 	if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
-		ret = os_snprintf(pos, end - pos, "%sLEAP", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sLEAP",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		first = 0;
 	}
 
 	return pos - buf;
@@ -2911,7 +3204,7 @@
 					   struct wpa_driver_capa *capa,
 					   char *buf, size_t buflen)
 {
-	int ret, first = 1;
+	int ret;
 	char *pos, *end;
 	size_t len;
 
@@ -2928,19 +3221,19 @@
 	}
 
 	if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
-		ret = os_snprintf(pos, end - pos, "%sIBSS", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sIBSS",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		first = 0;
 	}
 
 	if (capa->flags & WPA_DRIVER_FLAGS_AP) {
-		ret = os_snprintf(pos, end - pos, "%sAP", first ? "" : " ");
+		ret = os_snprintf(pos, end - pos, "%sAP",
+				  pos == buf ? "" : " ");
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
-		first = 0;
 	}
 
 	return pos - buf;
@@ -3033,10 +3326,13 @@
 		for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
 			if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
 				continue;
-			ret = os_snprintf(pos, end - pos, " %d = %d MHz%s\n",
+			ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n",
 					  chnl[i].chan, chnl[i].freq,
 					  chnl[i].flag & HOSTAPD_CHAN_NO_IBSS ?
-					  " (NO_IBSS)" : "");
+					  " (NO_IBSS)" : "",
+					  chnl[i].flag & HOSTAPD_CHAN_RADAR ?
+					  " (DFS)" : "");
+
 			if (ret < 0 || ret >= end - pos)
 				return pos - buf;
 			pos += ret;
@@ -3111,6 +3407,11 @@
 	if (os_strcmp(field, "freq") == 0)
 		return ctrl_iface_get_capability_freq(wpa_s, buf, buflen);
 
+#ifdef CONFIG_TDLS
+	if (os_strcmp(field, "tdls") == 0)
+		return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
+#endif /* CONFIG_TDLS */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -3232,9 +3533,9 @@
 	}
 
 	if (mask & WPA_BSS_MASK_AGE) {
-		struct os_time now;
+		struct os_reltime now;
 
-		os_get_time(&now);
+		os_get_reltime(&now);
 		ret = os_snprintf(pos, end - pos, "age=%d\n",
 				  (int) (now.sec - bss->last_update.sec));
 		if (ret < 0 || ret >= end - pos)
@@ -3283,19 +3584,46 @@
 				return 0;
 			pos += ret;
 		}
-		if (bss->caps & IEEE80211_CAP_IBSS) {
-			ret = os_snprintf(pos, end - pos, "[IBSS]");
+		if (bss_is_dmg(bss)) {
+			const char *s;
+			ret = os_snprintf(pos, end - pos, "[DMG]");
 			if (ret < 0 || ret >= end - pos)
 				return 0;
 			pos += ret;
-		}
-		if (bss->caps & IEEE80211_CAP_ESS) {
-			ret = os_snprintf(pos, end - pos, "[ESS]");
+			switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+			case IEEE80211_CAP_DMG_IBSS:
+				s = "[IBSS]";
+				break;
+			case IEEE80211_CAP_DMG_AP:
+				s = "[ESS]";
+				break;
+			case IEEE80211_CAP_DMG_PBSS:
+				s = "[PBSS]";
+				break;
+			default:
+				s = "";
+				break;
+			}
+			ret = os_snprintf(pos, end - pos, "%s", s);
 			if (ret < 0 || ret >= end - pos)
 				return 0;
 			pos += ret;
+		} else {
+			if (bss->caps & IEEE80211_CAP_IBSS) {
+				ret = os_snprintf(pos, end - pos, "[IBSS]");
+				if (ret < 0 || ret >= end - pos)
+					return 0;
+				pos += ret;
+			}
+			if (bss->caps & IEEE80211_CAP_ESS) {
+				ret = os_snprintf(pos, end - pos, "[ESS]");
+				if (ret < 0 || ret >= end - pos)
+					return 0;
+				pos += ret;
+			}
 		}
-		if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) {
+		if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+		    wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
 			ret = os_snprintf(pos, end - pos, "[P2P]");
 			if (ret < 0 || ret >= end - pos)
 				return 0;
@@ -3352,8 +3680,10 @@
 						  WFD_IE_VENDOR_TYPE);
 		if (wfd) {
 			ret = os_snprintf(pos, end - pos, "wfd_subelems=");
-			if (ret < 0 || ret >= end - pos)
+			if (ret < 0 || ret >= end - pos) {
+				wpabuf_free(wfd);
 				return 0;
+			}
 			pos += ret;
 
 			pos += wpa_snprintf_hex(pos, end - pos,
@@ -3392,6 +3722,10 @@
 				   anqp->hs20_wan_metrics);
 		pos = anqp_add_hex(pos, end, "hs20_connection_capability",
 				   anqp->hs20_connection_capability);
+		pos = anqp_add_hex(pos, end, "hs20_operating_class",
+				   anqp->hs20_operating_class);
+		pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
+				   anqp->hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
 	}
 #endif /* CONFIG_INTERWORKING */
@@ -3586,6 +3920,7 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
 static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 {
 	wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
@@ -3607,6 +3942,7 @@
 				   MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 	wpa_sm_drop_sa(wpa_s->wpa);
 }
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
 static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
@@ -3659,9 +3995,15 @@
 	unsigned int timeout = atoi(cmd);
 	enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
 	u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+	u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
 	char *pos;
 	unsigned int search_delay;
 
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		wpa_dbg(wpa_s, MSG_INFO,
+			"Reject P2P_FIND since interface is disabled");
+		return -1;
+	}
 	if (os_strstr(cmd, "type=social"))
 		type = P2P_FIND_ONLY_SOCIAL;
 	else if (os_strstr(cmd, "type=progressive"))
@@ -3675,6 +4017,14 @@
 		_dev_id = dev_id;
 	}
 
+	pos = os_strstr(cmd, "dev_type=");
+	if (pos) {
+		pos += 9;
+		if (wps_dev_type_str2bin(pos, dev_type) < 0)
+			return -1;
+		_dev_type = dev_type;
+	}
+
 	pos = os_strstr(cmd, "delay=");
 	if (pos) {
 		pos += 6;
@@ -3682,8 +4032,8 @@
 	} else
 		search_delay = wpas_p2p_search_delay(wpa_s);
 
-	return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id,
-			     search_delay);
+	return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
+			     _dev_id, search_delay);
 }
 
 
@@ -3703,12 +4053,12 @@
 	int go_intent = -1;
 	int freq = 0;
 	int pd;
-	int ht40;
+	int ht40, vht;
 
 	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad]
 	 * [persistent|persistent=<network id>]
 	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
-	 * [ht40] */
+	 * [ht40] [vht] */
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -3736,7 +4086,9 @@
 	auth = os_strstr(pos, " auth") != NULL;
 	automatic = os_strstr(pos, " auto") != NULL;
 	pd = os_strstr(pos, " provdisc") != NULL;
-	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+	vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+		vht;
 
 	pos2 = os_strstr(pos, " go_intent=");
 	if (pos2) {
@@ -3777,7 +4129,7 @@
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, automatic, join,
 				   auth, go_intent, freq, persistent_id, pd,
-				   ht40);
+				   ht40, vht);
 	if (new_pin == -2) {
 		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
 		return 25;
@@ -3803,6 +4155,11 @@
 static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	unsigned int timeout = atoi(cmd);
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		wpa_dbg(wpa_s, MSG_INFO,
+			"Reject P2P_LISTEN since interface is disabled");
+		return -1;
+	}
 	return wpas_p2p_listen(wpa_s, timeout);
 }
 
@@ -4141,7 +4498,7 @@
 	struct wpa_ssid *ssid;
 	u8 *_peer = NULL, peer[ETH_ALEN];
 	int freq = 0, pref_freq = 0;
-	int ht40;
+	int ht40, vht;
 
 	id = atoi(cmd);
 	pos = os_strstr(cmd, " peer=");
@@ -4175,9 +4532,12 @@
 			return -1;
 	}
 
-	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+	vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+		vht;
 
-	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, pref_freq);
+	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht,
+			       pref_freq);
 }
 
 
@@ -4224,7 +4584,8 @@
 
 
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-					 char *cmd, int freq, int ht40)
+					 char *cmd, int freq, int ht40,
+					 int vht)
 {
 	int id;
 	struct wpa_ssid *ssid;
@@ -4238,31 +4599,34 @@
 		return -1;
 	}
 
-	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40, NULL);
+	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
+					     NULL, 0);
 }
 
 
 static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
-	int freq = 0, ht40;
+	int freq = 0, ht40, vht;
 	char *pos;
 
 	pos = os_strstr(cmd, "freq=");
 	if (pos)
 		freq = atoi(pos + 5);
 
-	ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+	vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht;
+	ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+		vht;
 
 	if (os_strncmp(cmd, "persistent=", 11) == 0)
 		return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
-						     ht40);
+						     ht40, vht);
 	if (os_strcmp(cmd, "persistent") == 0 ||
 	    os_strncmp(cmd, "persistent ", 11) == 0)
-		return wpas_p2p_group_add(wpa_s, 1, freq, ht40);
+		return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht);
 	if (os_strncmp(cmd, "freq=", 5) == 0)
-		return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+		return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
 	if (ht40)
-		return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+		return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
 
 	wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
 		   cmd);
@@ -4358,6 +4722,22 @@
 		return pos - buf;
 	pos += res;
 
+	if (info->vendor_elems) {
+		res = os_snprintf(pos, end - pos, "vendor_elems=");
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+
+		pos += wpa_snprintf_hex(pos, end - pos,
+					wpabuf_head(info->vendor_elems),
+					wpabuf_len(info->vendor_elems));
+
+		res = os_snprintf(pos, end - pos, "\n");
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+	}
+
 	return pos - buf;
 }
 
@@ -4365,48 +4745,21 @@
 static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
 				  const char *param)
 {
-	struct wpa_freq_range *freq = NULL, *n;
-	unsigned int count = 0, i;
-	const char *pos, *pos2, *pos3;
+	unsigned int i;
 
 	if (wpa_s->global->p2p == NULL)
 		return -1;
 
-	/*
-	 * param includes comma separated frequency range.
-	 * For example: 2412-2432,2462,5000-6000
-	 */
-	pos = param;
-	while (pos && pos[0]) {
-		n = os_realloc_array(freq, count + 1,
-				     sizeof(struct wpa_freq_range));
-		if (n == NULL) {
-			os_free(freq);
-			return -1;
-		}
-		freq = n;
-		freq[count].min = atoi(pos);
-		pos2 = os_strchr(pos, '-');
-		pos3 = os_strchr(pos, ',');
-		if (pos2 && (!pos3 || pos2 < pos3)) {
-			pos2++;
-			freq[count].max = atoi(pos2);
-		} else
-			freq[count].max = freq[count].min;
-		pos = pos3;
-		if (pos)
-			pos++;
-		count++;
-	}
+	if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
+		return -1;
 
-	for (i = 0; i < count; i++) {
+	for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) {
+		struct wpa_freq_range *freq;
+		freq = &wpa_s->global->p2p_disallow_freq.range[i];
 		wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
-			   freq[i].min, freq[i].max);
+			   freq->min, freq->max);
 	}
 
-	os_free(wpa_s->global->p2p_disallow_freq);
-	wpa_s->global->p2p_disallow_freq = freq;
-	wpa_s->global->num_p2p_disallow_freq = count;
 	wpas_p2p_update_channel_list(wpa_s);
 	return 0;
 }
@@ -4437,7 +4790,7 @@
 
 	if (os_strcmp(cmd, "listen_channel") == 0) {
 		return p2p_set_listen_channel(wpa_s->global->p2p, 81,
-					      atoi(param));
+					      atoi(param), 1);
 	}
 
 	if (os_strcmp(cmd, "ssid_postfix") == 0) {
@@ -4602,6 +4955,16 @@
 		return 0;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (os_strcmp(cmd, "nfc_tag") == 0)
+		return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param));
+#endif /* CONFIG_WPS_NFC */
+
+	if (os_strcmp(cmd, "disable_ip_addr_req") == 0) {
+		wpa_s->p2p_disable_ip_addr_req = !!atoi(param);
+		return 0;
+	}
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
 		   cmd);
 
@@ -4689,7 +5052,68 @@
 #endif /* CONFIG_P2P */
 
 
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
+{
+	struct wpa_freq_range_list ranges;
+	int *freqs = NULL;
+	struct hostapd_hw_modes *mode;
+	u16 i;
+
+	if (wpa_s->hw.modes == NULL)
+		return NULL;
+
+	os_memset(&ranges, 0, sizeof(ranges));
+	if (freq_range_list_parse(&ranges, val) < 0)
+		return NULL;
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		int j;
+
+		mode = &wpa_s->hw.modes[i];
+		for (j = 0; j < mode->num_channels; j++) {
+			unsigned int freq;
+
+			if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+				continue;
+
+			freq = mode->channels[j].freq;
+			if (!freq_range_list_includes(&ranges, freq))
+				continue;
+
+			int_array_add_unique(&freqs, freq);
+		}
+	}
+
+	os_free(ranges.range);
+	return freqs;
+}
+
+
 #ifdef CONFIG_INTERWORKING
+
+static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
+{
+	int auto_sel = 0;
+	int *freqs = NULL;
+
+	if (param) {
+		char *pos;
+
+		auto_sel = os_strstr(param, "auto") != NULL;
+
+		pos = os_strstr(param, "freq=");
+		if (pos) {
+			freqs = freq_range_to_channel_list(wpa_s, pos + 5);
+			if (freqs == NULL)
+				return -1;
+		}
+
+	}
+
+	return interworking_select(wpa_s, auto_sel, freqs);
+}
+
+
 static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
 {
 	u8 bssid[ETH_ALEN];
@@ -4719,15 +5143,27 @@
 #define MAX_ANQP_INFO_ID 100
 	u16 id[MAX_ANQP_INFO_ID];
 	size_t num_id = 0;
+	u32 subtypes = 0;
 
 	used = hwaddr_aton2(dst, dst_addr);
 	if (used < 0)
 		return -1;
 	pos = dst + used;
 	while (num_id < MAX_ANQP_INFO_ID) {
-		id[num_id] = atoi(pos);
-		if (id[num_id])
-			num_id++;
+		if (os_strncmp(pos, "hs20:", 5) == 0) {
+#ifdef CONFIG_HS20
+			int num = atoi(pos + 5);
+			if (num <= 0 || num > 31)
+				return -1;
+			subtypes |= BIT(num);
+#else /* CONFIG_HS20 */
+			return -1;
+#endif /* CONFIG_HS20 */
+		} else {
+			id[num_id] = atoi(pos);
+			if (id[num_id])
+				num_id++;
+		}
 		pos = os_strchr(pos + 1, ',');
 		if (pos == NULL)
 			break;
@@ -4737,7 +5173,7 @@
 	if (num_id == 0)
 		return -1;
 
-	return anqp_send_req(wpa_s, dst_addr, id, num_id);
+	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
 }
 
 
@@ -4813,9 +5249,8 @@
 	int used;
 	char *pos;
 	size_t resp_len, start, requested_len;
-
-	if (!wpa_s->last_gas_resp)
-		return -1;
+	struct wpabuf *resp;
+	int ret;
 
 	used = hwaddr_aton2(cmd, addr);
 	if (used < 0)
@@ -4826,11 +5261,18 @@
 		pos++;
 	dialog_token = atoi(pos);
 
-	if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
-	    dialog_token != wpa_s->last_gas_dialog_token)
+	if (wpa_s->last_gas_resp &&
+	    os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
+	    dialog_token == wpa_s->last_gas_dialog_token)
+		resp = wpa_s->last_gas_resp;
+	else if (wpa_s->prev_gas_resp &&
+		 os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
+		 dialog_token == wpa_s->prev_gas_dialog_token)
+		resp = wpa_s->prev_gas_resp;
+	else
 		return -1;
 
-	resp_len = wpabuf_len(wpa_s->last_gas_resp);
+	resp_len = wpabuf_len(resp);
 	start = 0;
 	requested_len = resp_len;
 
@@ -4851,9 +5293,24 @@
 	if (requested_len * 2 + 1 > buflen)
 		return os_snprintf(buf, buflen, "FAIL-Too long response");
 
-	return wpa_snprintf_hex(buf, buflen,
-				wpabuf_head_u8(wpa_s->last_gas_resp) + start,
-				requested_len);
+	ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
+			       requested_len);
+
+	if (start + requested_len == resp_len) {
+		/*
+		 * Free memory by dropping the response after it has been
+		 * fetched.
+		 */
+		if (resp == wpa_s->prev_gas_resp) {
+			wpabuf_free(wpa_s->prev_gas_resp);
+			wpa_s->prev_gas_resp = NULL;
+		} else {
+			wpabuf_free(wpa_s->last_gas_resp);
+			wpa_s->last_gas_resp = NULL;
+		}
+	}
+
+	return ret;
 }
 #endif /* CONFIG_INTERWORKING */
 
@@ -4941,7 +5398,7 @@
 	if (len == 0 && cred && cred->realm)
 		return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
 
-	if (len % 1)
+	if (len & 1)
 		return -1;
 	len /= 2;
 	buf = os_malloc(len);
@@ -4960,6 +5417,26 @@
 	return ret;
 }
 
+
+static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	char *icon;
+
+	used = hwaddr_aton2(cmd, dst_addr);
+	if (used < 0)
+		return -1;
+
+	while (cmd[used] == ' ')
+		used++;
+	icon = &cmd[used];
+
+	wpa_s->fetch_osu_icon_in_progress = 0;
+	return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
+				  (u8 *) icon, os_strlen(icon));
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -5177,11 +5654,68 @@
 				p2p_set_country(p2p, country);
 			}
 		}
-		ret = sprintf(buf, "%s\n", "OK");
+		ret = os_snprintf(buf, buflen, "%s\n", "OK");
 	}
 	return ret;
 }
-#endif
+#endif /* ANDROID */
+
+
+static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
+				     char *buf, size_t buflen)
+{
+	int ret;
+	char *pos;
+	u8 *data = NULL;
+	unsigned int vendor_id, subcmd;
+	struct wpabuf *reply;
+	size_t data_len = 0;
+
+	/* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+	vendor_id = strtoul(cmd, &pos, 16);
+	if (!isblank(*pos))
+		return -EINVAL;
+
+	subcmd = strtoul(pos, &pos, 10);
+
+	if (*pos != '\0') {
+		if (!isblank(*pos++))
+			return -EINVAL;
+		data_len = os_strlen(pos);
+	}
+
+	if (data_len) {
+		data_len /= 2;
+		data = os_malloc(data_len);
+		if (!data)
+			return -1;
+
+		if (hexstr2bin(pos, data, data_len)) {
+			wpa_printf(MSG_DEBUG,
+				   "Vendor command: wrong parameter format");
+			os_free(data);
+			return -EINVAL;
+		}
+	}
+
+	reply = wpabuf_alloc((buflen - 1) / 2);
+	if (!reply) {
+		os_free(data);
+		return -1;
+	}
+
+	ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
+				 reply);
+
+	if (ret == 0)
+		ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+				       wpabuf_len(reply));
+
+	wpabuf_free(reply);
+	os_free(data);
+
+	return ret;
+}
 
 
 static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
@@ -5189,24 +5723,36 @@
 	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
 
 #ifdef CONFIG_P2P
+	wpas_p2p_cancel(wpa_s);
 	wpas_p2p_stop_find(wpa_s);
 	p2p_ctrl_flush(wpa_s);
 	wpas_p2p_group_remove(wpa_s, "*");
+	wpas_p2p_service_flush(wpa_s);
+	wpa_s->global->p2p_disabled = 0;
+	wpa_s->global->p2p_per_sta_psk = 0;
+	wpa_s->conf->num_sec_device_types = 0;
+	wpa_s->p2p_disable_ip_addr_req = 0;
+	os_free(wpa_s->global->p2p_go_avoid_freq.range);
+	wpa_s->global->p2p_go_avoid_freq.range = NULL;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS_TESTING
 	wps_version_number = 0x20;
 	wps_testing_dummy_cred = 0;
+	wps_corrupt_pkhash = 0;
 #endif /* CONFIG_WPS_TESTING */
 #ifdef CONFIG_WPS
+	wpa_s->wps_fragment_size = 0;
 	wpas_wps_cancel(wpa_s);
 #endif /* CONFIG_WPS */
+	wpa_s->after_wps = 0;
+	wpa_s->known_wps_freq = 0;
 
+#ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
 	extern unsigned int tdls_testing;
 	tdls_testing = 0;
 #endif /* CONFIG_TDLS_TESTING */
-#ifdef CONFIG_TDLS
 	wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
 	wpa_tdls_enable(wpa_s->wpa, 1);
 #endif /* CONFIG_TDLS */
@@ -5233,6 +5779,678 @@
 	wpa_s->extra_blacklist_count = 0;
 	wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
 	wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+	wpa_config_flush_blobs(wpa_s->conf);
+	wpa_s->conf->auto_interworking = 0;
+	wpa_s->conf->okc = 0;
+
+	wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
+	wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
+	wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
+	eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+
+	radio_remove_works(wpa_s, NULL, 1);
+
+	wpa_s->next_ssid = NULL;
+
+#ifdef CONFIG_INTERWORKING
+	hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_INTERWORKING */
+
+	wpa_s->ext_mgmt_frame_handling = 0;
+}
+
+
+static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s,
+				     char *buf, size_t buflen)
+{
+	struct wpa_radio_work *work;
+	char *pos, *end;
+	struct os_reltime now, diff;
+
+	pos = buf;
+	end = buf + buflen;
+
+	os_get_reltime(&now);
+
+	dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+	{
+		int ret;
+
+		os_reltime_sub(&now, &work->time, &diff);
+		ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
+				  work->type, work->wpa_s->ifname, work->freq,
+				  work->started, diff.sec, diff.usec);
+		if (ret < 0 || ret >= end - pos)
+			break;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_radio_work *work = eloop_ctx;
+	struct wpa_external_work *ework = work->ctx;
+
+	wpa_dbg(work->wpa_s, MSG_DEBUG,
+		"Timing out external radio work %u (%s)",
+		ework->id, work->type);
+	wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
+	radio_work_done(work);
+	os_free(ework);
+}
+
+
+static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_external_work *ework = work->ctx;
+
+	if (deinit) {
+		if (work->started)
+			eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+					     work, NULL);
+
+		os_free(ework);
+		return;
+	}
+
+	wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
+		ework->id, ework->type);
+	wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
+	if (!ework->timeout)
+		ework->timeout = 10;
+	eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
+			       work, NULL);
+}
+
+
+static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd,
+				    char *buf, size_t buflen)
+{
+	struct wpa_external_work *ework;
+	char *pos, *pos2;
+	size_t type_len;
+	int ret;
+	unsigned int freq = 0;
+
+	/* format: <name> [freq=<MHz>] [timeout=<seconds>] */
+
+	ework = os_zalloc(sizeof(*ework));
+	if (ework == NULL)
+		return -1;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos) {
+		type_len = pos - cmd;
+		pos++;
+
+		pos2 = os_strstr(pos, "freq=");
+		if (pos2)
+			freq = atoi(pos2 + 5);
+
+		pos2 = os_strstr(pos, "timeout=");
+		if (pos2)
+			ework->timeout = atoi(pos2 + 8);
+	} else {
+		type_len = os_strlen(cmd);
+	}
+	if (4 + type_len >= sizeof(ework->type))
+		type_len = sizeof(ework->type) - 4 - 1;
+	os_strlcpy(ework->type, "ext:", sizeof(ework->type));
+	os_memcpy(ework->type + 4, cmd, type_len);
+	ework->type[4 + type_len] = '\0';
+
+	wpa_s->ext_work_id++;
+	if (wpa_s->ext_work_id == 0)
+		wpa_s->ext_work_id++;
+	ework->id = wpa_s->ext_work_id;
+
+	if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb,
+			   ework) < 0) {
+		os_free(ework);
+		return -1;
+	}
+
+	ret = os_snprintf(buf, buflen, "%u", ework->id);
+	if (ret < 0 || (size_t) ret >= buflen)
+		return -1;
+	return ret;
+}
+
+
+static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	struct wpa_radio_work *work;
+	unsigned int id = atoi(cmd);
+
+	dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+	{
+		struct wpa_external_work *ework;
+
+		if (os_strncmp(work->type, "ext:", 4) != 0)
+			continue;
+		ework = work->ctx;
+		if (id && ework->id != id)
+			continue;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Completed external radio work %u (%s)",
+			ework->id, ework->type);
+		eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
+		radio_work_done(work);
+		os_free(ework);
+		return 3; /* "OK\n" */
+	}
+
+	return -1;
+}
+
+
+static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd,
+				char *buf, size_t buflen)
+{
+	if (os_strcmp(cmd, "show") == 0)
+		return wpas_ctrl_radio_work_show(wpa_s, buf, buflen);
+	if (os_strncmp(cmd, "add ", 4) == 0)
+		return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen);
+	if (os_strncmp(cmd, "done ", 5) == 0)
+		return wpas_ctrl_radio_work_done(wpa_s, cmd + 4);
+	return -1;
+}
+
+
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_radio_work *work, *tmp;
+
+	if (!wpa_s || !wpa_s->radio)
+		return;
+
+	dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
+			      struct wpa_radio_work, list) {
+		struct wpa_external_work *ework;
+
+		if (os_strncmp(work->type, "ext:", 4) != 0)
+			continue;
+		ework = work->ctx;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Flushing%s external radio work %u (%s)",
+			work->started ? " started" : "", ework->id,
+			ework->type);
+		if (work->started)
+			eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+					     work, NULL);
+		radio_work_done(work);
+		os_free(ework);
+	}
+}
+
+
+static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	eapol_sm_notify_ctrl_response(wpa_s->eapol);
+}
+
+
+static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val)
+{
+	int *freqs = NULL;
+
+	freqs = freq_range_to_channel_list(wpa_s, val);
+	if (freqs == NULL)
+		return -1;
+
+	os_free(wpa_s->manual_scan_freqs);
+	wpa_s->manual_scan_freqs = freqs;
+
+	return 0;
+}
+
+
+static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value)
+{
+	const char *pos = value;
+
+	while (pos) {
+		if (*pos == ' ' || *pos == '\0')
+			break;
+		if (wpa_s->scan_id_count == MAX_SCAN_ID)
+			return -1;
+		wpa_s->scan_id[wpa_s->scan_id_count++] = atoi(pos);
+		pos = os_strchr(pos, ',');
+		if (pos)
+			pos++;
+	}
+
+	return 0;
+}
+
+
+static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
+			   char *reply, int reply_size, int *reply_len)
+{
+	char *pos;
+
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		*reply_len = -1;
+		return;
+	}
+
+	wpa_s->manual_scan_passive = 0;
+	wpa_s->manual_scan_use_id = 0;
+	wpa_s->manual_scan_only_new = 0;
+	wpa_s->scan_id_count = 0;
+
+	if (params) {
+		if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
+			wpa_s->scan_res_handler = scan_only_handler;
+
+		pos = os_strstr(params, "freq=");
+		if (pos && set_scan_freqs(wpa_s, pos + 5) < 0) {
+			*reply_len = -1;
+			return;
+		}
+
+		pos = os_strstr(params, "passive=");
+		if (pos)
+			wpa_s->manual_scan_passive = !!atoi(pos + 8);
+
+		pos = os_strstr(params, "use_id=");
+		if (pos)
+			wpa_s->manual_scan_use_id = atoi(pos + 7);
+
+		pos = os_strstr(params, "only_new=1");
+		if (pos)
+			wpa_s->manual_scan_only_new = 1;
+
+		pos = os_strstr(params, "scan_id=");
+		if (pos && scan_id_list_parse(wpa_s, pos + 8) < 0) {
+			*reply_len = -1;
+			return;
+		}
+	} else {
+		os_free(wpa_s->manual_scan_freqs);
+		wpa_s->manual_scan_freqs = NULL;
+		if (wpa_s->scan_res_handler == scan_only_handler)
+			wpa_s->scan_res_handler = NULL;
+	}
+
+	if (!wpa_s->sched_scanning && !wpa_s->scanning &&
+	    ((wpa_s->wpa_state <= WPA_SCANNING) ||
+	     (wpa_s->wpa_state == WPA_COMPLETED))) {
+		wpa_s->normal_scans = 0;
+		wpa_s->scan_req = MANUAL_SCAN_REQ;
+		wpa_s->after_wps = 0;
+		wpa_s->known_wps_freq = 0;
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		if (wpa_s->manual_scan_use_id) {
+			wpa_s->manual_scan_id++;
+			wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+				wpa_s->manual_scan_id);
+			*reply_len = os_snprintf(reply, reply_size, "%u\n",
+						 wpa_s->manual_scan_id);
+		}
+	} else if (wpa_s->sched_scanning) {
+		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_s->scan_req = MANUAL_SCAN_REQ;
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		if (wpa_s->manual_scan_use_id) {
+			wpa_s->manual_scan_id++;
+			*reply_len = os_snprintf(reply, reply_size, "%u\n",
+						 wpa_s->manual_scan_id);
+			wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+				wpa_s->manual_scan_id);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
+		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+	}
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+static void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s,
+				       unsigned int freq, const u8 *dst,
+				       const u8 *src, const u8 *bssid,
+				       const u8 *data, size_t data_len,
+				       enum offchannel_send_action_result
+				       result)
+{
+	wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR
+		" src=" MACSTR " bssid=" MACSTR " result=%s",
+		freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+		result == OFFCHANNEL_SEND_ACTION_SUCCESS ?
+		"SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ?
+			     "NO_ACK" : "FAILED"));
+}
+
+
+static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos, *param;
+	size_t len;
+	u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN];
+	int res, used;
+	int freq = 0, no_cck = 0, wait_time = 0;
+
+	/* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1]
+	 *    <action=Action frame payload> */
+
+	wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, da);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+	used = hwaddr_aton2(pos, bssid);
+	if (used < 0)
+		return -1;
+	pos += used;
+
+	param = os_strstr(pos, " freq=");
+	if (param) {
+		param += 6;
+		freq = atoi(param);
+	}
+
+	param = os_strstr(pos, " no_cck=");
+	if (param) {
+		param += 8;
+		no_cck = atoi(param);
+	}
+
+	param = os_strstr(pos, " wait_time=");
+	if (param) {
+		param += 11;
+		wait_time = atoi(param);
+	}
+
+	param = os_strstr(pos, " action=");
+	if (param == NULL)
+		return -1;
+	param += 8;
+
+	len = os_strlen(param);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(param, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid,
+				     buf, len, wait_time,
+				     wpas_ctrl_iface_mgmt_tx_cb, no_cck);
+	os_free(buf);
+	return res;
+}
+
+
+static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting");
+	offchannel_send_action_done(wpa_s);
+}
+
+
+static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos, *param;
+	union wpa_event_data event;
+	enum wpa_event_type ev;
+
+	/* <event name> [parameters..] */
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd);
+
+	pos = cmd;
+	param = os_strchr(pos, ' ');
+	if (param)
+		*param++ = '\0';
+
+	os_memset(&event, 0, sizeof(event));
+
+	if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) {
+		ev = EVENT_INTERFACE_ENABLED;
+	} else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) {
+		ev = EVENT_INTERFACE_DISABLED;
+	} else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) {
+		ev = EVENT_AVOID_FREQUENCIES;
+		if (param == NULL)
+			param = "";
+		if (freq_range_list_parse(&event.freq_range, param) < 0)
+			return -1;
+		wpa_supplicant_event(wpa_s, ev, &event);
+		os_free(event.freq_range.range);
+		return 0;
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
+			cmd);
+		return -1;
+	}
+
+	wpa_supplicant_event(wpa_s, ev, &event);
+
+	return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+	char buf[30];
+
+	wpa_printf(MSG_DEBUG, "Update vendor elements");
+
+	for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+		if (wpa_s->vendor_elem[i]) {
+			os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+			wpa_hexdump_buf(MSG_DEBUG, buf, wpa_s->vendor_elem[i]);
+		}
+	}
+
+#ifdef CONFIG_P2P
+	if (wpa_s->parent == wpa_s &&
+	    wpa_s->global->p2p &&
+	    !wpa_s->global->p2p_disabled)
+		p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
+}
+
+
+static struct wpa_supplicant *
+wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s,
+			    enum wpa_vendor_elem_frame frame)
+{
+	switch (frame) {
+#ifdef CONFIG_P2P
+	case VENDOR_ELEM_PROBE_REQ_P2P:
+	case VENDOR_ELEM_PROBE_RESP_P2P:
+	case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+	case VENDOR_ELEM_BEACON_P2P_GO:
+	case VENDOR_ELEM_P2P_PD_REQ:
+	case VENDOR_ELEM_P2P_PD_RESP:
+	case VENDOR_ELEM_P2P_GO_NEG_REQ:
+	case VENDOR_ELEM_P2P_GO_NEG_RESP:
+	case VENDOR_ELEM_P2P_GO_NEG_CONF:
+	case VENDOR_ELEM_P2P_INV_REQ:
+	case VENDOR_ELEM_P2P_INV_RESP:
+	case VENDOR_ELEM_P2P_ASSOC_REQ:
+		return wpa_s->parent;
+#endif /* CONFIG_P2P */
+	default:
+		return wpa_s;
+	}
+}
+
+
+static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos = cmd;
+	int frame;
+	size_t len;
+	struct wpabuf *buf;
+	struct ieee802_11_elems elems;
+
+	frame = atoi(pos);
+	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+		return -1;
+	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+	pos++;
+
+	len = os_strlen(pos);
+	if (len == 0)
+		return 0;
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+		wpabuf_free(buf);
+		return -1;
+	}
+
+	if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) ==
+	    ParseFailed) {
+		wpabuf_free(buf);
+		return -1;
+	}
+
+	if (wpa_s->vendor_elem[frame] == NULL) {
+		wpa_s->vendor_elem[frame] = buf;
+		wpas_ctrl_vendor_elem_update(wpa_s);
+		return 0;
+	}
+
+	if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) {
+		wpabuf_free(buf);
+		return -1;
+	}
+
+	wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
+	wpabuf_free(buf);
+	wpas_ctrl_vendor_elem_update(wpa_s);
+
+	return 0;
+}
+
+
+static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd,
+				     char *buf, size_t buflen)
+{
+	int frame = atoi(cmd);
+
+	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+		return -1;
+	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+	if (wpa_s->vendor_elem[frame] == NULL)
+		return 0;
+
+	return wpa_snprintf_hex(buf, buflen,
+				wpabuf_head_u8(wpa_s->vendor_elem[frame]),
+				wpabuf_len(wpa_s->vendor_elem[frame]));
+}
+
+
+static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos = cmd;
+	int frame;
+	size_t len;
+	u8 *buf;
+	struct ieee802_11_elems elems;
+	u8 *ie, *end;
+
+	frame = atoi(pos);
+	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+		return -1;
+	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+	pos++;
+
+	if (*pos == '*') {
+		wpabuf_free(wpa_s->vendor_elem[frame]);
+		wpa_s->vendor_elem[frame] = NULL;
+		wpas_ctrl_vendor_elem_update(wpa_s);
+		return 0;
+	}
+
+	if (wpa_s->vendor_elem[frame] == NULL)
+		return -1;
+
+	len = os_strlen(pos);
+	if (len == 0)
+		return 0;
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(pos, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) {
+		os_free(buf);
+		return -1;
+	}
+
+	ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+	end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
+
+	for (; ie + 1 < end; ie += 2 + ie[1]) {
+		if (ie + len > end)
+			break;
+		if (os_memcmp(ie, buf, len) != 0)
+			continue;
+
+		if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+			wpabuf_free(wpa_s->vendor_elem[frame]);
+			wpa_s->vendor_elem[frame] = NULL;
+		} else {
+			os_memmove(ie, ie + len,
+				   wpabuf_len(wpa_s->vendor_elem[frame]) - len);
+			wpa_s->vendor_elem[frame]->used -= len;
+		}
+		os_free(buf);
+		wpas_ctrl_vendor_elem_update(wpa_s);
+		return 0;
+	}
+
+	os_free(buf);
+
+	return -1;
 }
 
 
@@ -5241,22 +6459,27 @@
 {
 	char *reply;
 	const int reply_size = 4096;
-	int ctrl_rsp = 0;
 	int reply_len;
 
 	if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
-	    os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
-	    os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
-	    os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0 ||
-	    os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) {
+	    os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+		if (wpa_debug_show_keys)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Control interface command '%s'", buf);
+		else
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Control interface command '%s [REMOVED]'",
+				os_strncmp(buf, WPA_CTRL_RSP,
+					   os_strlen(WPA_CTRL_RSP)) == 0 ?
+				WPA_CTRL_RSP : "SET_NETWORK");
+	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
+		   os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
 		wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
 				      (const u8 *) buf, os_strlen(buf));
 	} else {
 		int level = MSG_DEBUG;
 		if (os_strcmp(buf, "PING") == 0)
 			level = MSG_EXCESSIVE;
-		wpa_hexdump_ascii(level, "RX ctrl_iface",
-				  (const u8 *) buf, os_strlen(buf));
 		wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
 	}
 
@@ -5312,6 +6535,14 @@
 			reply_len = -1;
 		else
 			wpas_request_connection(wpa_s);
+	} else if (os_strcmp(buf, "REATTACH") == 0) {
+		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED ||
+		    !wpa_s->current_ssid)
+			reply_len = -1;
+		else {
+			wpa_s->reattach = 1;
+			wpas_request_connection(wpa_s);
+		}
 	} else if (os_strcmp(buf, "RECONNECT") == 0) {
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 			reply_len = -1;
@@ -5380,12 +6611,6 @@
 	} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
 		reply_len = wpas_ctrl_nfc_get_handover_sel(
 			wpa_s, buf + 21, reply, reply_size);
-	} else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) {
-		reply_len = wpas_ctrl_nfc_rx_handover_req(
-			wpa_s, buf + 20, reply, reply_size);
-	} else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) {
-		if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20))
-			reply_len = -1;
 	} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
 		if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
 			reply_len = -1;
@@ -5468,7 +6693,7 @@
 		if (wpas_p2p_group_remove(wpa_s, buf + 17))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
-		if (wpas_p2p_group_add(wpa_s, 0, 0, 0))
+		if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
 		if (p2p_ctrl_group_add(wpa_s, buf + 14))
@@ -5550,9 +6775,11 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
 		interworking_stop_fetch_anqp(wpa_s);
-	} else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
-		if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
-					NULL) < 0)
+	} else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
+		if (ctrl_interworking_select(wpa_s, NULL) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
+		if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
 			reply_len = -1;
 	} else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
 		if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
@@ -5574,14 +6801,28 @@
 	} else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
 		if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
+		if (hs20_icon_request(wpa_s, buf + 18) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "FETCH_OSU") == 0) {
+		if (hs20_fetch_osu(wpa_s) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
+		hs20_cancel_fetch_osu(wpa_s);
 #endif /* CONFIG_HS20 */
 	} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
 	{
 		if (wpa_supplicant_ctrl_iface_ctrl_rsp(
 			    wpa_s, buf + os_strlen(WPA_CTRL_RSP)))
 			reply_len = -1;
-		else
-			ctrl_rsp = 1;
+		else {
+			/*
+			 * Notify response from timeout to allow the control
+			 * interface response to be sent first.
+			 */
+			eloop_register_timeout(0, 0, wpas_ctrl_eapol_response,
+					       wpa_s, NULL);
+		}
 	} else if (os_strcmp(buf, "RECONFIGURE") == 0) {
 		if (wpa_supplicant_reload_configuration(wpa_s))
 			reply_len = -1;
@@ -5609,34 +6850,10 @@
 		wpa_supplicant_cancel_scan(wpa_s);
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
-	} else if (os_strcmp(buf, "SCAN") == 0 ||
-		   os_strncmp(buf, "SCAN ", 5) == 0) {
-		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
-			reply_len = -1;
-		else {
-			if (os_strlen(buf) > 4 &&
-			    os_strncasecmp(buf + 5, "TYPE=ONLY", 9) == 0)
-				wpa_s->scan_res_handler = scan_only_handler;
-			if (!wpa_s->sched_scanning && !wpa_s->scanning &&
-			    ((wpa_s->wpa_state <= WPA_SCANNING) ||
-			     (wpa_s->wpa_state == WPA_COMPLETED))) {
-				wpa_s->normal_scans = 0;
-				wpa_s->scan_req = MANUAL_SCAN_REQ;
-				wpa_supplicant_req_scan(wpa_s, 0, 0);
-			} else if (wpa_s->sched_scanning) {
-				wpa_printf(MSG_DEBUG, "Stop ongoing "
-					   "sched_scan to allow requested "
-					   "full scan to proceed");
-				wpa_supplicant_cancel_sched_scan(wpa_s);
-				wpa_s->scan_req = MANUAL_SCAN_REQ;
-				wpa_supplicant_req_scan(wpa_s, 0, 0);
-			} else {
-				wpa_printf(MSG_DEBUG, "Ongoing scan action - "
-					   "reject new request");
-				reply_len = os_snprintf(reply, reply_size,
-							"FAIL-BUSY\n");
-			}
-		}
+	} else if (os_strcmp(buf, "SCAN") == 0) {
+		wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
+	} else if (os_strncmp(buf, "SCAN ", 5) == 0) {
+		wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);
 	} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_scan_results(
 			wpa_s, reply, reply_size);
@@ -5661,6 +6878,9 @@
 	} else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_get_network(
 			wpa_s, buf + 12, reply, reply_size);
+	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+		if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12))
+			reply_len = -1;
 	} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_list_creds(
 			wpa_s, reply, reply_size);
@@ -5673,6 +6893,10 @@
 	} else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
 		if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "GET_CRED ", 9) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9,
+							       reply,
+							       reply_size);
 #ifndef CONFIG_NO_CONFIG_WRITE
 	} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
 		if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
@@ -5711,13 +6935,18 @@
 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
 		if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+		if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+			reply_len = -1;
 #endif /* CONFIG_AP */
 	} else if (os_strcmp(buf, "SUSPEND") == 0) {
 		wpas_notify_suspend(wpa_s->global);
 	} else if (os_strcmp(buf, "RESUME") == 0) {
 		wpas_notify_resume(wpa_s->global);
+#ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcmp(buf, "DROP_SA") == 0) {
 		wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
+#endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
 		if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
 			reply_len = -1;
@@ -5760,7 +6989,10 @@
 	} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
 		reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
 						      reply_size);
-#endif
+#endif /* ANDROID */
+	} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
+		reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply,
+						      reply_size);
 	} else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
 		pmksa_cache_clear_current(wpa_s->wpa);
 		eapol_sm_request_reauth(wpa_s->eapol);
@@ -5774,6 +7006,28 @@
 #endif /* CONFIG_WNM */
 	} else if (os_strcmp(buf, "FLUSH") == 0) {
 		wpa_supplicant_ctrl_iface_flush(wpa_s);
+	} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+		reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply,
+						 reply_size);
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
+		if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) {
+		wpas_ctrl_iface_mgmt_tx_done(wpa_s);
+	} else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
+		if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
+			reply_len = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
+	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
+		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) {
+		reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply,
+						      reply_size);
+	} else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
+		if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
+			reply_len = -1;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -5784,9 +7038,6 @@
 		reply_len = 5;
 	}
 
-	if (ctrl_rsp)
-		eapol_sm_notify_ctrl_response(wpa_s->eapol);
-
 	*resp_len = reply_len;
 	return reply;
 }
@@ -5993,10 +7244,7 @@
 {
 #ifdef CONFIG_P2P
 	static const char * cmd[] = {
-#ifdef ANDROID_P2P
 		"LIST_NETWORKS",
-		"SAVE_CONFIG",
-#endif
 		"P2P_FIND",
 		"P2P_STOP_FIND",
 		"P2P_LISTEN",
@@ -6011,12 +7259,11 @@
 		NULL
 	};
 	static const char * prefix[] = {
-#ifdef ANDROID_P2P
+#ifdef ANDROID
 		"DRIVER ",
+#endif /* ANDROID */
 		"GET_NETWORK ",
 		"REMOVE_NETWORK ",
-		"SET ",
-#endif
 		"P2P_FIND ",
 		"P2P_CONNECT ",
 		"P2P_LISTEN ",
@@ -6037,6 +7284,9 @@
 		"P2P_PRESENCE_REQ ",
 		"P2P_EXT_LISTEN ",
 		"P2P_REMOVE_CLIENT ",
+		"NFC_GET_HANDOVER_SEL ",
+		"NFC_GET_HANDOVER_REQ ",
+		"NFC_REPORT_HANDOVER ",
 		NULL
 	};
 	int found = 0;
@@ -6113,6 +7363,9 @@
 	}
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	/* Restore cmd to its original value to allow redirection */
+	value[-1] = ' ';
+
 	return -1;
 }
 
@@ -6120,7 +7373,7 @@
 #ifndef CONFIG_NO_CONFIG_WRITE
 static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
 {
-	int ret = 0;
+	int ret = 0, saved = 0;
 	struct wpa_supplicant *wpa_s;
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
@@ -6134,9 +7387,16 @@
 			ret = 1;
 		} else {
 			wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated");
+			saved++;
 		}
 	}
 
+	if (!saved && !ret) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated");
+		ret = 1;
+	}
+
 	return ret;
 }
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -6249,8 +7509,19 @@
 	} else if (os_strcmp(buf, "RESUME") == 0) {
 		wpas_notify_resume(global);
 	} else if (os_strncmp(buf, "SET ", 4) == 0) {
-		if (wpas_global_ctrl_iface_set(global, buf + 4))
+		if (wpas_global_ctrl_iface_set(global, buf + 4)) {
+#ifdef CONFIG_P2P
+			if (global->p2p_init_wpa_s) {
+				os_free(reply);
+				/* Check if P2P redirection would work for this
+				 * command. */
+				return wpa_supplicant_ctrl_iface_process(
+					global->p2p_init_wpa_s,
+					buf, resp_len);
+			}
+#endif /* CONFIG_P2P */
 			reply_len = -1;
+		}
 #ifndef CONFIG_NO_CONFIG_WRITE
 	} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
 		if (wpas_global_ctrl_iface_save_config(global))
@@ -6259,6 +7530,12 @@
 	} else if (os_strcmp(buf, "STATUS") == 0) {
 		reply_len = wpas_global_ctrl_iface_status(global, reply,
 							  reply_size);
+#ifdef CONFIG_MODULE_TESTS
+	} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+		int wpas_module_tests(void);
+		if (wpas_module_tests() < 0)
+			reply_len = -1;
+#endif /* CONFIG_MODULE_TESTS */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index a329ef3..d54cc07 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -32,7 +32,7 @@
 					 char *buf, size_t *resp_len);
 
 /**
- * wpa_supplicant_ctrl_iface_process - Process global ctrl_iface command
+ * wpa_supplicant_global_ctrl_iface_process - Process global ctrl_iface command
  * @global: Pointer to global data from wpa_supplicant_init()
  * @buf: Received command buffer (nul terminated string)
  * @resp_len: Variable to be set to the response length
@@ -113,6 +113,8 @@
 void wpa_supplicant_global_ctrl_iface_deinit(
 	struct ctrl_iface_global_priv *priv);
 
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s);
+
 #else /* CONFIG_CTRL_IFACE */
 
 static inline struct ctrl_iface_priv *
@@ -148,6 +150,10 @@
 {
 }
 
+static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE */
 
 #endif /* CTRL_IFACE_H */
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 8c09ba1..9d0674d 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -30,7 +30,11 @@
  */
 struct wpa_ctrl_dst {
 	struct wpa_ctrl_dst *next;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	struct sockaddr_in6 addr;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	struct sockaddr_in addr;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	socklen_t addrlen;
 	int debug_level;
 	int errors;
@@ -51,38 +55,68 @@
 
 
 static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+					    struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 					    struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 					    socklen_t fromlen)
 {
 	struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_UDP_IPV6 */
 
 	dst = os_zalloc(sizeof(*dst));
 	if (dst == NULL)
 		return -1;
-	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in));
+	os_memcpy(&dst->addr, from, sizeof(*from));
 	dst->addrlen = fromlen;
 	dst->debug_level = MSG_INFO;
 	dst->next = priv->ctrl_dst;
 	priv->ctrl_dst = dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
+		   inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)),
+		   ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
 		   inet_ntoa(from->sin_addr), ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	return 0;
 }
 
 
 static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+					    struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 					    struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 					    socklen_t fromlen)
 {
 	struct wpa_ctrl_dst *dst, *prev = NULL;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 	dst = priv->ctrl_dst;
 	while (dst) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+		if (from->sin6_port == dst->addr.sin6_port &&
+		    !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+			       sizeof(from->sin6_addr))) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d",
+				   inet_ntop(AF_INET6, &from->sin6_addr, addr,
+					     sizeof(*from)),
+				   ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
 		    from->sin_port == dst->addr.sin_port) {
 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
 				   "%s:%d", inet_ntoa(from->sin_addr),
 				   ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 			if (prev == NULL)
 				priv->ctrl_dst = dst->next;
 			else
@@ -98,21 +132,38 @@
 
 
 static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+					   struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 					   struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 					   socklen_t fromlen,
 					   char *level)
 {
 	struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
 
 	dst = priv->ctrl_dst;
 	while (dst) {
+#if CONFIG_CTRL_IFACE_UDP_IPV6
+		if (from->sin6_port == dst->addr.sin6_port &&
+		    !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+			       sizeof(from->sin6_addr))) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d",
+				   inet_ntop(AF_INET6, &from->sin6_addr, addr,
+					     sizeof(*from)),
+				   ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
 		    from->sin_port == dst->addr.sin_port) {
 			wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
 				   "level %s:%d", inet_ntoa(from->sin_addr),
 				   ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 			dst->debug_level = atoi(level);
 			return 0;
 		}
@@ -150,7 +201,14 @@
 	struct ctrl_iface_priv *priv = sock_ctx;
 	char buf[256], *pos;
 	int res;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	struct sockaddr_in6 from;
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+	char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	struct sockaddr_in from;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	socklen_t fromlen = sizeof(from);
 	char *reply = NULL;
 	size_t reply_len = 0;
@@ -165,6 +223,13 @@
 	}
 
 #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
+	if (os_strcmp(addr, "::1")) {
+		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
+			   addr);
+	}
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
 		/*
 		 * The OS networking stack is expected to drop this kind of
@@ -176,6 +241,7 @@
 			   "source %s", inet_ntoa(from.sin_addr));
 		return;
 	}
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
 	buf[res] = '\0';
@@ -269,8 +335,14 @@
 wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
 {
 	struct ctrl_iface_priv *priv;
-	struct sockaddr_in addr;
 	int port = WPA_CTRL_IFACE_PORT;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	struct sockaddr_in6 addr;
+	int domain = PF_INET6;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+	struct sockaddr_in addr;
+	int domain = PF_INET;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 	priv = os_zalloc(sizeof(*priv));
 	if (priv == NULL)
@@ -282,21 +354,34 @@
 	if (wpa_s->conf->ctrl_interface == NULL)
 		return priv;
 
-	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+	priv->sock = socket(domain, SOCK_DGRAM, 0);
 	if (priv->sock < 0) {
 		perror("socket(PF_INET)");
 		goto fail;
 	}
 
 	os_memset(&addr, 0, sizeof(addr));
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	addr.sin6_family = AF_INET6;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	addr.sin6_addr = in6addr_any;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+	inet_pton(AF_INET6, "::1", &addr.sin6_addr);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	addr.sin_family = AF_INET;
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 	addr.sin_addr.s_addr = INADDR_ANY;
 #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 try_again:
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	addr.sin6_port = htons(port);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	addr.sin_port = htons(port);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 		port--;
 		if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
@@ -362,6 +447,9 @@
 	int idx;
 	char *sbuf;
 	int llen;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 	dst = priv->ctrl_dst;
 	if (priv->sock < 0 || dst == NULL)
@@ -381,9 +469,16 @@
 	while (dst) {
 		next = dst->next;
 		if (level >= dst->debug_level) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
+				   inet_ntop(AF_INET6, &dst->addr.sin6_addr,
+					     addr, sizeof(dst->addr)),
+				   ntohs(dst->addr.sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
 				   inet_ntoa(dst->addr.sin_addr),
 				   ntohs(dst->addr.sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 			if (sendto(priv->sock, sbuf, llen + len, 0,
 				   (struct sockaddr *) &dst->addr,
 				   sizeof(dst->addr)) < 0) {
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index e35d2c3..40082e2 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / UNIX domain socket -based control interface
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -57,10 +57,17 @@
 };
 
 
-static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+					   const char *ifname, int sock,
 					   struct dl_list *ctrl_dst,
 					   int level, const char *buf,
-					   size_t len);
+					   size_t len,
+					   struct ctrl_iface_priv *priv,
+					   struct ctrl_iface_global_priv *gp);
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+				  struct ctrl_iface_priv *priv);
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+					 struct ctrl_iface_global_priv *priv);
 
 
 static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
@@ -68,6 +75,7 @@
 					    socklen_t fromlen)
 {
 	struct wpa_ctrl_dst *dst;
+	char addr_txt[200];
 
 	dst = os_zalloc(sizeof(*dst));
 	if (dst == NULL)
@@ -76,9 +84,10 @@
 	dst->addrlen = fromlen;
 	dst->debug_level = MSG_INFO;
 	dl_list_add(ctrl_dst, &dst->list);
-	wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
-		    (u8 *) from->sun_path,
-		    fromlen - offsetof(struct sockaddr_un, sun_path));
+	printf_encode(addr_txt, sizeof(addr_txt),
+		      (u8 *) from->sun_path,
+		      fromlen - offsetof(struct sockaddr_un, sun_path));
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s", addr_txt);
 	return 0;
 }
 
@@ -94,10 +103,13 @@
 		    os_memcmp(from->sun_path, dst->addr.sun_path,
 			      fromlen - offsetof(struct sockaddr_un, sun_path))
 		    == 0) {
-			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
-				    (u8 *) from->sun_path,
-				    fromlen -
-				    offsetof(struct sockaddr_un, sun_path));
+			char addr_txt[200];
+			printf_encode(addr_txt, sizeof(addr_txt),
+				      (u8 *) from->sun_path,
+				      fromlen -
+				      offsetof(struct sockaddr_un, sun_path));
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s",
+				   addr_txt);
 			dl_list_del(&dst->list);
 			os_free(dst);
 			return 0;
@@ -121,11 +133,13 @@
 		    os_memcmp(from->sun_path, dst->addr.sun_path,
 			      fromlen - offsetof(struct sockaddr_un, sun_path))
 		    == 0) {
-			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
-				    "level", (u8 *) from->sun_path,
-				    fromlen -
-				    offsetof(struct sockaddr_un, sun_path));
+			char addr_txt[200];
 			dst->debug_level = atoi(level);
+			printf_encode(addr_txt, sizeof(addr_txt),
+				      (u8 *) from->sun_path, fromlen -
+				      offsetof(struct sockaddr_un, sun_path));
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s",
+				   dst->debug_level, addr_txt);
 			return 0;
 		}
 	}
@@ -143,7 +157,7 @@
 	int res;
 	struct sockaddr_un from;
 	socklen_t fromlen = sizeof(from);
-	char *reply = NULL;
+	char *reply = NULL, *reply_buf = NULL;
 	size_t reply_len = 0;
 	int new_attached = 0;
 
@@ -177,21 +191,49 @@
 		else
 			reply_len = 2;
 	} else {
-		reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
-							  &reply_len);
+		reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+							      &reply_len);
+		reply = reply_buf;
+	}
+
+	if (!reply && reply_len == 1) {
+		reply = "FAIL\n";
+		reply_len = 5;
+	} else if (!reply && reply_len == 2) {
+		reply = "OK\n";
+		reply_len = 3;
 	}
 
 	if (reply) {
-		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
-		       fromlen);
-		os_free(reply);
-	} else if (reply_len == 1) {
-		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-		       fromlen);
-	} else if (reply_len == 2) {
-		sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
-		       fromlen);
+		if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+			   fromlen) < 0) {
+			int _errno = errno;
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"ctrl_iface sendto failed: %d - %s",
+				_errno, strerror(_errno));
+			if (_errno == ENOBUFS || _errno == EAGAIN) {
+				/*
+				 * The socket send buffer could be full. This
+				 * may happen if client programs are not
+				 * receiving their pending messages. Close and
+				 * reopen the socket as a workaround to avoid
+				 * getting stuck being unable to send any new
+				 * responses.
+				 */
+				sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+				if (sock < 0) {
+					wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket");
+				}
+			}
+			if (new_attached) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching");
+				new_attached = 0;
+				wpa_supplicant_ctrl_iface_detach(
+					&priv->ctrl_dst, &from, fromlen);
+			}
+		}
 	}
+	os_free(reply_buf);
 
 	if (new_attached)
 		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
@@ -202,7 +244,7 @@
 {
 	char *buf;
 	size_t len;
-	char *pbuf, *dir = NULL, *gid_str = NULL;
+	char *pbuf, *dir = NULL;
 	int res;
 
 	if (wpa_s->conf->ctrl_interface == NULL)
@@ -212,12 +254,11 @@
 	if (pbuf == NULL)
 		return NULL;
 	if (os_strncmp(pbuf, "DIR=", 4) == 0) {
+		char *gid_str;
 		dir = pbuf + 4;
 		gid_str = os_strstr(dir, " GROUP=");
-		if (gid_str) {
+		if (gid_str)
 			*gid_str = '\0';
-			gid_str += 7;
-		}
 	} else
 		dir = pbuf;
 
@@ -262,26 +303,27 @@
 	if (global != 2 && wpa_s->global->ctrl_iface) {
 		struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
 		if (!dl_list_empty(&priv->ctrl_dst)) {
-			wpa_supplicant_ctrl_iface_send(global ? NULL :
+			wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL :
 						       wpa_s->ifname,
 						       priv->sock,
 						       &priv->ctrl_dst,
-						       level, txt, len);
+						       level, txt, len, NULL,
+						       priv);
 		}
 	}
 
 	if (wpa_s->ctrl_iface == NULL)
 		return;
-	wpa_supplicant_ctrl_iface_send(NULL, wpa_s->ctrl_iface->sock,
+	wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
 				       &wpa_s->ctrl_iface->ctrl_dst,
-				       level, txt, len);
+				       level, txt, len, wpa_s->ctrl_iface,
+				       NULL);
 }
 
 
-struct ctrl_iface_priv *
-wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,
+				     struct ctrl_iface_priv *priv)
 {
-	struct ctrl_iface_priv *priv;
 	struct sockaddr_un addr;
 	char *fname = NULL;
 	gid_t gid = 0;
@@ -291,16 +333,6 @@
 	char *endp;
 	int flags;
 
-	priv = os_zalloc(sizeof(*priv));
-	if (priv == NULL)
-		return NULL;
-	dl_list_init(&priv->ctrl_dst);
-	priv->wpa_s = wpa_s;
-	priv->sock = -1;
-
-	if (wpa_s->conf->ctrl_interface == NULL)
-		return priv;
-
 	buf = os_strdup(wpa_s->conf->ctrl_interface);
 	if (buf == NULL)
 		goto fail;
@@ -476,18 +508,61 @@
 	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
 
 	os_free(buf);
-	return priv;
+	return 0;
 
 fail:
-	if (priv->sock >= 0)
+	if (priv->sock >= 0) {
 		close(priv->sock);
-	os_free(priv);
+		priv->sock = -1;
+	}
 	if (fname) {
 		unlink(fname);
 		os_free(fname);
 	}
 	os_free(buf);
-	return NULL;
+	return -1;
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+	struct ctrl_iface_priv *priv;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+	dl_list_init(&priv->ctrl_dst);
+	priv->wpa_s = wpa_s;
+	priv->sock = -1;
+
+	if (wpa_s->conf->ctrl_interface == NULL)
+		return priv;
+
+	if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
+		os_free(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+				  struct ctrl_iface_priv *priv)
+{
+	int res;
+
+	if (priv->sock <= 0)
+		return -1;
+
+	eloop_unregister_read_sock(priv->sock);
+	close(priv->sock);
+	priv->sock = -1;
+	res = wpas_ctrl_iface_open_sock(wpa_s, priv);
+	if (res < 0)
+		return -1;
+	return priv->sock;
 }
 
 
@@ -497,7 +572,7 @@
 
 	if (priv->sock > -1) {
 		char *fname;
-		char *buf, *dir = NULL, *gid_str = NULL;
+		char *buf, *dir = NULL;
 		eloop_unregister_read_sock(priv->sock);
 		if (!dl_list_empty(&priv->ctrl_dst)) {
 			/*
@@ -517,16 +592,17 @@
 			os_free(fname);
 		}
 
+		if (priv->wpa_s->conf->ctrl_interface == NULL)
+			goto free_dst;
 		buf = os_strdup(priv->wpa_s->conf->ctrl_interface);
 		if (buf == NULL)
 			goto free_dst;
 		if (os_strncmp(buf, "DIR=", 4) == 0) {
+			char *gid_str;
 			dir = buf + 4;
 			gid_str = os_strstr(dir, " GROUP=");
-			if (gid_str) {
+			if (gid_str)
 				*gid_str = '\0';
-				gid_str += 7;
-			}
 		} else
 			dir = buf;
 
@@ -563,10 +639,13 @@
  *
  * Send a packet to all monitor programs attached to the control interface.
  */
-static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+					   const char *ifname, int sock,
 					   struct dl_list *ctrl_dst,
 					   int level, const char *buf,
-					   size_t len)
+					   size_t len,
+					   struct ctrl_iface_priv *priv,
+					   struct ctrl_iface_global_priv *gp)
 {
 	struct wpa_ctrl_dst *dst, *next;
 	char levelstr[10];
@@ -602,31 +681,58 @@
 	msg.msg_iov = io;
 	msg.msg_iovlen = idx;
 
-	idx = 0;
 	dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
-		if (level >= dst->debug_level) {
-			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
-				    (u8 *) dst->addr.sun_path, dst->addrlen -
-				    offsetof(struct sockaddr_un, sun_path));
-			msg.msg_name = (void *) &dst->addr;
-			msg.msg_namelen = dst->addrlen;
-			if (sendmsg(sock, &msg, MSG_DONTWAIT) < 0) {
-				int _errno = errno;
-				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
-					   "%d - %s",
-					   idx, errno, strerror(errno));
-				dst->errors++;
-				if (dst->errors > 1000 ||
-				    (_errno != ENOBUFS && dst->errors > 10) ||
-				    _errno == ENOENT) {
-					wpa_supplicant_ctrl_iface_detach(
-						ctrl_dst, &dst->addr,
-						dst->addrlen);
-				}
-			} else
-				dst->errors = 0;
+		int _errno;
+		char addr_txt[200];
+
+		if (level < dst->debug_level)
+			continue;
+
+		printf_encode(addr_txt, sizeof(addr_txt),
+			      (u8 *) dst->addr.sun_path, dst->addrlen -
+			      offsetof(struct sockaddr_un, sun_path));
+		msg.msg_name = (void *) &dst->addr;
+		msg.msg_namelen = dst->addrlen;
+		if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
+			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s",
+				   addr_txt);
+			dst->errors = 0;
+			continue;
 		}
-		idx++;
+
+		_errno = errno;
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s",
+			   addr_txt, errno, strerror(errno));
+		dst->errors++;
+
+		if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) {
+			wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages",
+				addr_txt);
+			wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr,
+							 dst->addrlen);
+		}
+
+		if (_errno == ENOBUFS || _errno == EAGAIN) {
+			/*
+			 * The socket send buffer could be full. This may happen
+			 * if client programs are not receiving their pending
+			 * messages. Close and reopen the socket as a workaround
+			 * to avoid getting stuck being unable to send any new
+			 * responses.
+			 */
+			if (priv)
+				sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+			else if (gp)
+				sock = wpas_ctrl_iface_global_reinit(
+					wpa_s->global, gp);
+			else
+				break;
+			if (sock < 0) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"Failed to reinitialize ctrl_iface socket");
+				break;
+			}
+		}
 	}
 }
 
@@ -656,18 +762,30 @@
 			/* handle ATTACH signal of first monitor interface */
 			if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
 							      &from, fromlen)) {
-				sendto(priv->sock, "OK\n", 3, 0,
-				       (struct sockaddr *) &from, fromlen);
+				if (sendto(priv->sock, "OK\n", 3, 0,
+					   (struct sockaddr *) &from, fromlen) <
+				    0) {
+					wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+						   strerror(errno));
+				}
 				/* OK to continue */
 				return;
 			} else {
-				sendto(priv->sock, "FAIL\n", 5, 0,
-				       (struct sockaddr *) &from, fromlen);
+				if (sendto(priv->sock, "FAIL\n", 5, 0,
+					   (struct sockaddr *) &from, fromlen) <
+				    0) {
+					wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+						   strerror(errno));
+				}
 			}
 		} else {
 			/* return FAIL for all other signals */
-			sendto(priv->sock, "FAIL\n", 5, 0,
-			       (struct sockaddr *) &from, fromlen);
+			if (sendto(priv->sock, "FAIL\n", 5, 0,
+				   (struct sockaddr *) &from, fromlen) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "ctrl_iface sendto failed: %s",
+					   strerror(errno));
+			}
 		}
 	}
 }
@@ -680,11 +798,11 @@
 {
 	struct wpa_global *global = eloop_ctx;
 	struct ctrl_iface_global_priv *priv = sock_ctx;
-	char buf[256];
+	char buf[4096];
 	int res;
 	struct sockaddr_un from;
 	socklen_t fromlen = sizeof(from);
-	char *reply = NULL;
+	char *reply = NULL, *reply_buf = NULL;
 	size_t reply_len;
 
 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
@@ -709,41 +827,37 @@
 		else
 			reply_len = 2;
 	} else {
-		reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
-								 &reply_len);
+		reply_buf = wpa_supplicant_global_ctrl_iface_process(
+			global, buf, &reply_len);
+		reply = reply_buf;
+	}
+
+	if (!reply && reply_len == 1) {
+		reply = "FAIL\n";
+		reply_len = 5;
+	} else if (!reply && reply_len == 2) {
+		reply = "OK\n";
+		reply_len = 3;
 	}
 
 	if (reply) {
-		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
-		       fromlen);
-		os_free(reply);
-	} else if (reply_len == 1) {
-		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-		       fromlen);
-	} else if (reply_len == 2) {
-		sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, fromlen);
+		if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+			   fromlen) < 0) {
+			wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+				strerror(errno));
+		}
 	}
+	os_free(reply_buf);
 }
 
 
-struct ctrl_iface_global_priv *
-wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global,
+					    struct ctrl_iface_global_priv *priv)
 {
-	struct ctrl_iface_global_priv *priv;
 	struct sockaddr_un addr;
 	const char *ctrl = global->params.ctrl_interface;
 	int flags;
 
-	priv = os_zalloc(sizeof(*priv));
-	if (priv == NULL)
-		return NULL;
-	dl_list_init(&priv->ctrl_dst);
-	priv->global = global;
-	priv->sock = -1;
-
-	if (ctrl == NULL)
-		return priv;
-
 	wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl);
 
 #ifdef ANDROID
@@ -876,7 +990,13 @@
 			goto fail;
 		}
 	} else {
-		chmod(ctrl, S_IRWXU);
+		if (chmod(ctrl, S_IRWXU) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "chmod[global_ctrl_interface=%s](S_IRWXU): %s",
+				   ctrl, strerror(errno));
+			/* continue anyway since group change was not required
+			 */
+		}
 	}
 
 havesock:
@@ -899,13 +1019,58 @@
 				 wpa_supplicant_global_ctrl_iface_receive,
 				 global, priv);
 
-	return priv;
+	return 0;
 
 fail:
-	if (priv->sock >= 0)
+	if (priv->sock >= 0) {
 		close(priv->sock);
-	os_free(priv);
-	return NULL;
+		priv->sock = -1;
+	}
+	return -1;
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+	struct ctrl_iface_global_priv *priv;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+	dl_list_init(&priv->ctrl_dst);
+	priv->global = global;
+	priv->sock = -1;
+
+	if (global->params.ctrl_interface == NULL)
+		return priv;
+
+	if (wpas_global_ctrl_iface_open_sock(global, priv) < 0) {
+		os_free(priv);
+		return NULL;
+	}
+
+	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+	return priv;
+}
+
+
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+					 struct ctrl_iface_global_priv *priv)
+{
+	int res;
+
+	if (priv->sock <= 0)
+		return -1;
+
+	eloop_unregister_read_sock(priv->sock);
+	close(priv->sock);
+	priv->sock = -1;
+	res = wpas_global_ctrl_iface_open_sock(global, priv);
+	if (res < 0)
+		return -1;
+	return priv->sock;
 }
 
 
diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile
index d64c65c..f355ebe 100644
--- a/wpa_supplicant/dbus/Makefile
+++ b/wpa_supplicant/dbus/Makefile
@@ -1,7 +1,7 @@
 all: libwpadbus.a
 
 clean:
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 	rm -f libwpadbus.a
 
 install:
diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c
index 6caf740..5cc1505 100644
--- a/wpa_supplicant/dbus/dbus_common.c
+++ b/wpa_supplicant/dbus/dbus_common.c
@@ -320,6 +320,8 @@
 	if (priv->con) {
 		eloop_cancel_timeout(dispatch_initial_dbus_messages,
 				     priv->con, NULL);
+		eloop_cancel_timeout(process_timeout, priv, ELOOP_ALL_CTX);
+
 		dbus_connection_set_watch_functions(priv->con, NULL, NULL,
 						    NULL, NULL, NULL);
 		dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index 61a9430..949ce7c 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -881,6 +881,8 @@
 		}
 
 		dbus_message_iter_recurse(iter, &iter_array);
+		os_memset(&tmpentry, 0, sizeof(tmpentry));
+		tmpentry.type = DBUS_TYPE_ARRAY;
 		if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry)
 					== FALSE)
 			goto cleanup;
@@ -932,6 +934,7 @@
 		break;
 	case DBUS_TYPE_ARRAY:
 		success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
+		break;
 	default:
 		break;
 	}
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index ddd2c82..e9f6589 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -24,6 +24,7 @@
 #include "dbus_common_i.h"
 #include "dbus_new_handlers_p2p.h"
 #include "p2p/p2p.h"
+#include "../p2p_supplicant.h"
 
 #ifdef CONFIG_AP /* until needed by something else */
 
@@ -709,9 +710,9 @@
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface;
-	char *auth_type[6]; /* we have six possible authorization types */
+	char *auth_type[5]; /* we have five possible authentication types */
 	int at_num = 0;
-	char *encr_type[4]; /* we have four possible encryption types */
+	char *encr_type[3]; /* we have three possible encryption types */
 	int et_num = 0;
 
 	iface = wpa_s->global->dbus;
@@ -734,20 +735,15 @@
 		auth_type[at_num++] = "open";
 	if (cred->auth_type & WPS_AUTH_WPAPSK)
 		auth_type[at_num++] = "wpa-psk";
-	if (cred->auth_type & WPS_AUTH_SHARED)
-		auth_type[at_num++] = "shared";
 	if (cred->auth_type & WPS_AUTH_WPA)
 		auth_type[at_num++] = "wpa-eap";
 	if (cred->auth_type & WPS_AUTH_WPA2)
 		auth_type[at_num++] = "wpa2-eap";
 	if (cred->auth_type & WPS_AUTH_WPA2PSK)
-		auth_type[at_num++] =
-		"wpa2-psk";
+		auth_type[at_num++] = "wpa2-psk";
 
 	if (cred->encr_type & WPS_ENCR_NONE)
 		encr_type[et_num++] = "none";
-	if (cred->encr_type & WPS_ENCR_WEP)
-		encr_type[et_num++] = "wep";
 	if (cred->encr_type & WPS_ENCR_TKIP)
 		encr_type[et_num++] = "tkip";
 	if (cred->encr_type & WPS_ENCR_AES)
@@ -950,37 +946,49 @@
 void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
 					const char *role)
 {
-
+	int error = 1;
 	DBusMessage *msg;
-	DBusMessageIter iter;
+	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface = wpa_s->global->dbus;
-	char *ifname = wpa_s->ifname;
 
 	/* Do nothing if the control interface is not turned on */
 	if (iface == NULL)
 		return;
 
-	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+	if (!wpa_s->dbus_groupobj_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
 				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 				      "GroupFinished");
 	if (msg == NULL)
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+		goto nomem;
 
-	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ifname)) {
-		wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
-				      "signal -not enough memory for ifname ");
-		goto err;
-	}
+	if (!wpa_dbus_dict_append_object_path(&dict_iter,
+					      "interface_object",
+					      wpa_s->dbus_new_path))
+		goto nomem;
 
-	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &role))
-		wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
-				      "signal -not enough memory for role ");
-	else
-		dbus_connection_send(iface->con, msg, NULL);
+	if (!wpa_dbus_dict_append_string(&dict_iter, "role", role))
+		goto nomem;
 
-err:
+	if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+					      wpa_s->dbus_groupobj_path) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		goto nomem;
+
+	error = 0;
+	dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+	if (error > 0)
+		wpa_printf(MSG_ERROR,
+			   "dbus: Failed to construct GroupFinished");
+
 	dbus_message_unref(msg);
 }
 
@@ -1156,6 +1164,69 @@
 }
 
 
+struct group_changed_data {
+	struct wpa_supplicant *wpa_s;
+	struct p2p_peer_info *info;
+};
+
+
+static int match_group_where_peer_is_client(struct p2p_group *group,
+					    void *user_data)
+{
+	struct group_changed_data *data = user_data;
+	const struct p2p_group_config *cfg;
+	struct wpa_supplicant *wpa_s_go;
+
+	if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr))
+		return 1;
+
+	cfg = p2p_group_get_config(group);
+
+	wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid,
+					 cfg->ssid_len);
+	if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) {
+		wpas_dbus_signal_peer_groups_changed(
+			data->wpa_s->parent, data->info->p2p_device_addr);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static void signal_peer_groups_changed(struct p2p_peer_info *info,
+				       void *user_data)
+{
+	struct group_changed_data *data = user_data;
+	struct wpa_supplicant *wpa_s_go;
+
+	wpa_s_go = wpas_get_p2p_client_iface(data->wpa_s,
+					     info->p2p_device_addr);
+	if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) {
+		wpas_dbus_signal_peer_groups_changed(data->wpa_s->parent,
+						     info->p2p_device_addr);
+		return;
+	}
+
+	data->info = info;
+	p2p_loop_on_all_groups(data->wpa_s->global->p2p,
+			       match_group_where_peer_is_client, data);
+	data->info = NULL;
+}
+
+
+static void peer_groups_changed(struct wpa_supplicant *wpa_s)
+{
+	struct group_changed_data data;
+
+	os_memset(&data, 0, sizeof(data));
+	data.wpa_s = wpa_s;
+
+	p2p_loop_on_known_peers(wpa_s->global->p2p,
+				signal_peer_groups_changed, &data);
+}
+
+
 /**
  * wpas_dbus_signal_p2p_group_started - Signals P2P group has
  * started. Emitted when a group is successfully started
@@ -1174,7 +1245,6 @@
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface;
-	char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 
 	iface = wpa_s->parent->global->dbus;
 
@@ -1182,14 +1252,13 @@
 	if (iface == NULL)
 		return;
 
-	if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+	if (wpa_s->dbus_groupobj_path == NULL)
 		return;
 
 	/* New interface has been created for this group */
 	msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
 				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 				      "GroupStarted");
-
 	if (msg == NULL)
 		return;
 
@@ -1212,12 +1281,15 @@
 		goto nomem;
 
 	if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
-					     group_obj_path) ||
+					      wpa_s->dbus_groupobj_path) ||
 	   !wpa_dbus_dict_close_write(&iter, &dict_iter))
 		goto nomem;
 
 	dbus_connection_send(iface->con, msg, NULL);
 
+	if (client)
+		peer_groups_changed(wpa_s);
+
 nomem:
 	dbus_message_unref(msg);
 }
@@ -1225,7 +1297,7 @@
 
 /**
  *
- * Method to emit GONeogtiation Success or Failure signals based
+ * Method to emit GONegotiation Success or Failure signals based
  * on status.
  * @status: Status of the GO neg request. 0 for success, other for errors.
  */
@@ -1362,7 +1434,7 @@
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface;
 
-	wpa_printf(MSG_INFO, "%s\n", __func__);
+	wpa_printf(MSG_DEBUG, "%s", __func__);
 
 	iface = wpa_s->global->dbus;
 	/* Do nothing if the control interface is not turned on */
@@ -1405,15 +1477,15 @@
  * constructed using p2p i/f addr used for connecting.
  *
  * @wpa_s: %wpa_supplicant network interface data
- * @member_addr: addr (p2p i/f) of the peer joining the group
+ * @peer_addr: P2P Device Address of the peer joining the group
  */
 void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
-				      const u8 *member)
+				      const u8 *peer_addr)
 {
 	struct wpas_dbus_priv *iface;
 	DBusMessage *msg;
 	DBusMessageIter iter;
-	char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 
 	iface = wpa_s->global->dbus;
 
@@ -1424,10 +1496,10 @@
 	if (!wpa_s->dbus_groupobj_path)
 		return;
 
-	os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-			"%s/"  WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			"%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 			COMPACT_MACSTR,
-			wpa_s->dbus_groupobj_path, MAC2STR(member));
+			wpa_s->parent->dbus_new_path, MAC2STR(peer_addr));
 
 	msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
 				      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1436,14 +1508,16 @@
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
-	path = groupmember_obj_path;
+	path = peer_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 					    &path))
 		goto err;
 
 	dbus_connection_send(iface->con, msg, NULL);
-
 	dbus_message_unref(msg);
+
+	wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr);
+
 	return;
 
 err:
@@ -1456,18 +1530,18 @@
  *
  * Method to emit a signal for a peer disconnecting the group.
  * The signal will carry path to the group member object
- * constructed using p2p i/f addr used for connecting.
+ * constructed using the P2P Device Address of the peer.
  *
  * @wpa_s: %wpa_supplicant network interface data
- * @member_addr: addr (p2p i/f) of the peer joining the group
+ * @peer_addr: P2P Device Address of the peer joining the group
  */
 void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
-				      const u8 *member)
+					    const u8 *peer_addr)
 {
 	struct wpas_dbus_priv *iface;
 	DBusMessage *msg;
 	DBusMessageIter iter;
-	char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 
 	iface = wpa_s->global->dbus;
 
@@ -1478,10 +1552,10 @@
 	if (!wpa_s->dbus_groupobj_path)
 		return;
 
-	os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-			"%s/"  WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+			"%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 			COMPACT_MACSTR,
-			wpa_s->dbus_groupobj_path, MAC2STR(member));
+			wpa_s->dbus_groupobj_path, MAC2STR(peer_addr));
 
 	msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
 				      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1490,14 +1564,16 @@
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
-	path = groupmember_obj_path;
+	path = peer_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 					    &path))
 		goto err;
 
 	dbus_connection_send(iface->con, msg, NULL);
-
 	dbus_message_unref(msg);
+
+	wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr);
+
 	return;
 
 err:
@@ -2462,6 +2538,12 @@
 		  END_ARGS
 	  }
 	},
+	{ "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) &wpas_dbus_handler_reattach,
+	  {
+		  END_ARGS
+	  }
+	},
 	{ "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
 	  {
@@ -2516,6 +2598,15 @@
 	  }
 	},
 #endif /* CONFIG_NO_CONFIG_BLOBS */
+	{ "SetPKCS11EngineAndModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler)
+	  &wpas_dbus_handler_set_pkcs11_engine_and_module_path,
+	  {
+		  { "pkcs11_engine_path", "s", ARG_IN },
+		  { "pkcs11_module_path", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
 #ifdef CONFIG_WPS
 	{ "Start", WPAS_DBUS_NEW_IFACE_WPS,
 	  (WPADBusMethodHandler) &wpas_dbus_handler_wps_start,
@@ -2634,6 +2725,7 @@
 	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req,
 	  {
 		  { "args", "a{sv}", ARG_IN },
+		  { "ref", "t", ARG_OUT },
 		  END_ARGS
 	  }
 	},
@@ -2664,13 +2756,6 @@
 		  END_ARGS
 	  }
 	},
-	{ "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
-	  {
-		  { "arg", "i", ARG_IN },
-		  END_ARGS
-	  }
-	},
 	{ "AddPersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  (WPADBusMethodHandler) wpas_dbus_handler_add_persistent_group,
 	  {
@@ -2736,6 +2821,37 @@
 	  }
 	},
 #endif /* CONFIG_AUTOSCAN */
+#ifdef CONFIG_TDLS
+	{ "TDLSDiscover", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_discover,
+	  {
+		  { "peer_address", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "TDLSSetup", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_setup,
+	  {
+		  { "peer_address", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "TDLSStatus", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_status,
+	  {
+		  { "peer_address", "s", ARG_IN },
+		  { "status", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "TDLSTeardown", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_tdls_teardown,
+	  {
+		  { "peer_address", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_TDLS */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -2812,6 +2928,14 @@
 	  wpas_dbus_getter_scan_interval,
 	  wpas_dbus_setter_scan_interval
 	},
+	{ "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_pkcs11_engine_path,
+	  NULL
+	},
+	{ "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_pkcs11_module_path,
+	  NULL
+	},
 #ifdef CONFIG_WPS
 	{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
 	  wpas_dbus_getter_process_credentials,
@@ -2941,7 +3065,6 @@
 	{ "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
 		  { "path", "o", ARG_OUT },
-		  { "properties", "a{sv}", ARG_OUT },
 		  END_ARGS
 	  }
 	},
@@ -3004,12 +3127,13 @@
 	},
 	{ "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
+		  { "properties", "a{sv}", ARG_OUT },
 		  END_ARGS
 	  }
 	},
 	{ "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
-		  { "status", "i", ARG_OUT },
+		  { "properties", "a{sv}", ARG_OUT },
 		  END_ARGS
 	  }
 	},
@@ -3028,8 +3152,7 @@
 	},
 	{ "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
-		  { "ifname", "s", ARG_OUT },
-		  { "role", "s", ARG_OUT },
+		  { "properties", "a{sv}", ARG_OUT },
 		  END_ARGS
 	  }
 	},
@@ -3226,11 +3349,25 @@
 	  wpas_dbus_getter_p2p_peer_ies,
 	  NULL
 	},
+	{ "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+	  wpas_dbus_getter_p2p_peer_device_address,
+	  NULL
+	},
+	{ "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao",
+	  wpas_dbus_getter_p2p_peer_groups,
+	  NULL
+	},
 	{ NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
-
+	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+	{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_P2P_PEER,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 	{ NULL, NULL, { END_ARGS } }
 };
 
@@ -3414,6 +3551,20 @@
 }
 
 
+void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+					  const u8 *dev_addr)
+{
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+	wpa_dbus_mark_property_changed(wpa_s->global->dbus, peer_obj_path,
+				       WPAS_DBUS_NEW_IFACE_P2P_PEER, "Groups");
+}
+
+
 static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = {
 	{ "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao",
 	  wpas_dbus_getter_p2p_group_members,
@@ -3559,6 +3710,8 @@
 		return;
 	}
 
+	peer_groups_changed(wpa_s);
+
 	wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'",
 		   wpa_s->dbus_groupobj_path);
 
@@ -3570,109 +3723,6 @@
 }
 
 static const struct wpa_dbus_property_desc
-wpas_dbus_p2p_groupmember_properties[] = {
-	{ NULL, NULL, NULL, NULL, NULL }
-};
-
-/**
- * wpas_dbus_register_p2p_groupmember - Register a p2p groupmember
- * object with dbus
- * @wpa_s: wpa_supplicant interface structure
- * @p2p_if_addr: i/f addr of the device joining this group
- *
- * Registers p2p groupmember representing object with dbus
- */
-void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
-					const u8 *p2p_if_addr)
-{
-	struct wpas_dbus_priv *ctrl_iface;
-	struct wpa_dbus_object_desc *obj_desc = NULL;
-	struct groupmember_handler_args *arg;
-	char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-
-	/* Do nothing if the control interface is not turned on */
-	if (wpa_s == NULL || wpa_s->global == NULL)
-		return;
-
-	ctrl_iface = wpa_s->global->dbus;
-	if (ctrl_iface == NULL)
-		return;
-
-	if (!wpa_s->dbus_groupobj_path)
-		return;
-
-	os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-		"%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
-		wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
-
-	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
-	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create object description");
-		goto err;
-	}
-
-	/* allocate memory for handlers arguments */
-	arg = os_zalloc(sizeof(struct groupmember_handler_args));
-	if (!arg) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create arguments for method");
-		goto err;
-	}
-
-	arg->wpa_s = wpa_s;
-	os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN);
-
-	wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
-			   wpas_dbus_p2p_groupmember_properties, NULL);
-
-	if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path,
-					       wpa_s->ifname, obj_desc))
-		goto err;
-
-	wpa_printf(MSG_INFO,
-		   "dbus: Registered group member object '%s' successfully",
-		   groupmember_obj_path);
-	return;
-
-err:
-	free_dbus_object_desc(obj_desc);
-}
-
-/**
- * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember
- * object with dbus
- * @wpa_s: wpa_supplicant interface structure
- * @p2p_if_addr: i/f addr of the device joining this group
- *
- * Unregisters p2p groupmember representing object with dbus
- */
-void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
-					  const u8 *p2p_if_addr)
-{
-	struct wpas_dbus_priv *ctrl_iface;
-	char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-
-	/* Do nothing if the control interface is not turned on */
-	if (wpa_s == NULL || wpa_s->global == NULL)
-		return;
-
-	ctrl_iface = wpa_s->global->dbus;
-	if (ctrl_iface == NULL)
-		return;
-
-	if (!wpa_s->dbus_groupobj_path)
-		return;
-
-	os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-		"%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
-		wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
-
-	wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path);
-}
-
-
-static const struct wpa_dbus_property_desc
 	wpas_dbus_persistent_group_properties[] = {
 	{ "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
 	  wpas_dbus_getter_persistent_group_properties,
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 61c480a..881d351 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -79,11 +79,7 @@
 #define WPAS_DBUS_NEW_P2P_PEERS_PART	"Peers"
 #define	WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
 
-#define WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART	"Members"
-#define	WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER \
-	WPAS_DBUS_NEW_INTERFACE ".GroupMember"
-
-/* Errors */
+/* Top-level Errors */
 #define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
 	WPAS_DBUS_NEW_INTERFACE ".UnknownError"
 #define WPAS_DBUS_ERROR_INVALID_ARGS \
@@ -91,6 +87,8 @@
 
 #define WPAS_DBUS_ERROR_IFACE_EXISTS \
 	WPAS_DBUS_NEW_INTERFACE ".InterfaceExists"
+#define WPAS_DBUS_ERROR_IFACE_DISABLED            \
+	WPAS_DBUS_NEW_INTERFACE ".InterfaceDisabled"
 #define WPAS_DBUS_ERROR_IFACE_UNKNOWN \
 	WPAS_DBUS_NEW_INTERFACE ".InterfaceUnknown"
 
@@ -118,6 +116,9 @@
 #define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \
 	WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou"
 
+/* Interface-level errors */
+#define WPAS_DBUS_ERROR_IFACE_SCAN_ERROR \
+	WPAS_DBUS_NEW_IFACE_INTERFACE ".ScanError"
 
 void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv);
 void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv);
@@ -172,6 +173,8 @@
 				  const u8 *dev_addr);
 void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
 					   const u8 *dev_addr);
+void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+					  const u8 *dev_addr);
 void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
 					const char *role);
 void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
@@ -196,10 +199,6 @@
 					  int nid);
 void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
 					    int status, const u8 *bssid);
-void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
-					const u8 *p2p_if_addr);
-void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
-					  const u8 *p2p_if_addr);
 void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
 					    const u8 *member);
 void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
@@ -355,6 +354,12 @@
 }
 
 static inline void
+wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+				     const u8 *dev_addr)
+{
+}
+
+static inline void
 wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
 				   const char *role)
 {
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 478d02f..6dc5f76 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -28,10 +28,6 @@
 #include "dbus_dict_helpers.h"
 #include "dbus_common_i.h"
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
 static const char *debug_strings[] = {
 	"excessive", "msgdump", "debug", "info", "warning", "error", NULL
 };
@@ -120,6 +116,27 @@
 }
 
 
+/**
+ * wpas_dbus_error_scan_error - Return a new ScanError error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * @error: Optional string to be used as the error message
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return a scan error
+ */
+DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message,
+					 const char *error)
+{
+	DBusMessage *reply;
+
+	reply = dbus_message_new_error(message,
+				       WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+				       error);
+
+	return reply;
+}
+
+
 static const char *dont_quote[] = {
 	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
 	"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
@@ -254,6 +271,7 @@
 			wpa_config_update_prio_list(wpa_s->conf);
 
 		os_free(value);
+		value = NULL;
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
@@ -422,8 +440,13 @@
 	}
 
 	for (i = 0; i < array_len; i++) {
-		dbus_message_iter_append_basic(&array_iter, type,
-					       array + i * element_size);
+		if (!dbus_message_iter_append_basic(&array_iter, type,
+						    array + i * element_size)) {
+			dbus_set_error(error, DBUS_ERROR_FAILED,
+				       "%s: failed to construct message 2.5",
+				       __func__);
+			return FALSE;
+		}
 	}
 
 	if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
@@ -489,7 +512,7 @@
 		return FALSE;
 	}
 
-	for (i = 0; i < array_len; i++) {
+	for (i = 0; i < array_len && array[i]; i++) {
 		wpa_dbus_dict_bin_array_add_element(&array_iter,
 						    wpabuf_head(array[i]),
 						    wpabuf_len(array[i]));
@@ -544,24 +567,28 @@
 			goto error;
 		if (!os_strcmp(entry.key, "Driver") &&
 		    (entry.type == DBUS_TYPE_STRING)) {
+			os_free(driver);
 			driver = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (driver == NULL)
 				goto error;
 		} else if (!os_strcmp(entry.key, "Ifname") &&
 			   (entry.type == DBUS_TYPE_STRING)) {
+			os_free(ifname);
 			ifname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (ifname == NULL)
 				goto error;
 		} else if (!os_strcmp(entry.key, "ConfigFile") &&
 			   (entry.type == DBUS_TYPE_STRING)) {
+			os_free(confname);
 			confname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (confname == NULL)
 				goto error;
 		} else if (!os_strcmp(entry.key, "BridgeIfname") &&
 			   (entry.type == DBUS_TYPE_STRING)) {
+			os_free(bridge_ifname);
 			bridge_ifname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (bridge_ifname == NULL)
@@ -1236,6 +1263,23 @@
 }
 
 
+static int wpas_dbus_get_scan_allow_roam(DBusMessage *message,
+					 DBusMessageIter *var,
+					 dbus_bool_t *allow,
+					 DBusMessage **reply)
+{
+	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) {
+		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+			   "Type must be a boolean");
+		*reply = wpas_dbus_error_invalid_args(
+			message, "Wrong Type value type. Boolean required");
+		return -1;
+	}
+	dbus_message_iter_get_basic(var, allow);
+	return 0;
+}
+
+
 /**
  * wpas_dbus_handler_scan - Request a wireless scan on an interface
  * @message: Pointer to incoming dbus message
@@ -1254,6 +1298,7 @@
 	char *key = NULL, *type = NULL;
 	struct wpa_driver_scan_params params;
 	size_t i;
+	dbus_bool_t allow_roam = 1;
 
 	os_memset(&params, 0, sizeof(params));
 
@@ -1284,6 +1329,12 @@
 			if (wpas_dbus_get_scan_channels(message, &variant_iter,
 							&params, &reply) < 0)
 				goto out;
+		} else if (os_strcmp(key, "AllowRoam") == 0) {
+			if (wpas_dbus_get_scan_allow_roam(message,
+							  &variant_iter,
+							  &allow_roam,
+							  &reply) < 0)
+				goto out;
 		} else {
 			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
 				   "Unknown argument %s", key);
@@ -1310,7 +1361,10 @@
 				"passive scan");
 			goto out;
 		} else if (params.freqs && params.freqs[0]) {
-			wpa_supplicant_trigger_scan(wpa_s, &params);
+			if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+				reply = wpas_dbus_error_scan_error(
+					message, "Scan request rejected");
+			}
 		} else {
 			wpa_s->scan_req = MANUAL_SCAN_REQ;
 			wpa_supplicant_req_scan(wpa_s, 0, 0);
@@ -1323,7 +1377,10 @@
 #ifdef CONFIG_AUTOSCAN
 		autoscan_deinit(wpa_s);
 #endif /* CONFIG_AUTOSCAN */
-		wpa_supplicant_trigger_scan(wpa_s, &params);
+		if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+			reply = wpas_dbus_error_scan_error(
+				message, "Scan request rejected");
+		}
 	} else {
 		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
 			   "Unknown scan type: %s", type);
@@ -1332,6 +1389,9 @@
 		goto out;
 	}
 
+	if (!allow_roam)
+		wpa_s->scan_res_handler = scan_only_handler;
+
 out:
 	for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++)
 		os_free((u8 *) params.ssids[i].ssid);
@@ -1442,10 +1502,10 @@
 
 
 /**
- * wpas_dbus_handler_reassociate - Reassociate to current AP
+ * wpas_dbus_handler_reassociate - Reassociate
  * @message: Pointer to incoming dbus message
  * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: NotConnected DBus error message if not connected
+ * Returns: InterfaceDisabled DBus error message if disabled
  * or NULL otherwise.
  *
  * Handler function for "Reassociate" method call of network interface.
@@ -1453,7 +1513,30 @@
 DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
 					    struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) {
+		wpas_request_connection(wpa_s);
+		return NULL;
+	}
+
+	return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_DISABLED,
+				      "This interface is disabled");
+}
+
+
+/**
+ * wpas_dbus_handler_reattach - Reattach to current AP
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NotConnected DBus error message if not connected
+ * or NULL otherwise.
+ *
+ * Handler function for "Reattach" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s)
+{
 	if (wpa_s->current_ssid != NULL) {
+		wpa_s->reattach = 1;
 		wpas_request_connection(wpa_s);
 		return NULL;
 	}
@@ -1510,16 +1593,6 @@
 
 	wpas_notify_network_removed(wpa_s, ssid);
 
-	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
-		wpa_printf(MSG_ERROR,
-			   "wpas_dbus_handler_remove_network[dbus]: "
-			   "error occurred when removing network %d", id);
-		reply = wpas_dbus_error_unknown_error(
-			message, "error removing the specified network on "
-			"this interface.");
-		goto out;
-	}
-
 	if (ssid == wpa_s->current_ssid)
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
@@ -1530,6 +1603,15 @@
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
 
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "wpas_dbus_handler_remove_network[dbus]: "
+			   "error occurred when removing network %d", id);
+		reply = wpas_dbus_error_unknown_error(
+			message, "error removing the specified network on "
+			"this interface.");
+		goto out;
+	}
 
 out:
 	os_free(iface);
@@ -1970,6 +2052,227 @@
 }
 
 
+#ifdef CONFIG_TDLS
+
+static int get_peer_hwaddr_helper(DBusMessage *message, const char *func_name,
+				  u8 *peer_address, DBusMessage **error)
+{
+	const char *peer_string;
+
+	*error = NULL;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_STRING, &peer_string,
+				   DBUS_TYPE_INVALID)) {
+		*error = wpas_dbus_error_invalid_args(message, NULL);
+		return -1;
+	}
+
+	if (hwaddr_aton(peer_string, peer_address)) {
+		wpa_printf(MSG_DEBUG, "%s: invalid address '%s'",
+			   func_name, peer_string);
+		*error = wpas_dbus_error_invalid_args(
+			message, "Invalid hardware address format");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_discover - Discover TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSDiscover" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s)
+{
+	u8 peer[ETH_ALEN];
+	DBusMessage *error_reply;
+	int ret;
+
+	if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+		return error_reply;
+
+	wpa_printf(MSG_DEBUG, "DBUS TDLS_DISCOVER " MACSTR, MAC2STR(peer));
+
+	if (wpa_tdls_is_external_setup(wpa_s->wpa))
+		ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
+	else
+		ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
+
+	if (ret) {
+		return wpas_dbus_error_unknown_error(
+			message, "error performing TDLS discovery");
+	}
+
+	return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_setup - Setup TDLS session
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSSetup" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	u8 peer[ETH_ALEN];
+	DBusMessage *error_reply;
+	int ret;
+
+	if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+		return error_reply;
+
+	wpa_printf(MSG_DEBUG, "DBUS TDLS_SETUP " MACSTR, MAC2STR(peer));
+
+	wpa_tdls_remove(wpa_s->wpa, peer);
+	if (wpa_tdls_is_external_setup(wpa_s->wpa))
+		ret = wpa_tdls_start(wpa_s->wpa, peer);
+	else
+		ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+
+	if (ret) {
+		return wpas_dbus_error_unknown_error(
+			message, "error performing TDLS setup");
+	}
+
+	return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_status - Return TDLS session status
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A string representing the state of the link to this TDLS peer
+ *
+ * Handler function for "TDLSStatus" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	u8 peer[ETH_ALEN];
+	DBusMessage *reply;
+	const char *tdls_status;
+
+	if (get_peer_hwaddr_helper(message, __func__, peer, &reply) < 0)
+		return reply;
+
+	wpa_printf(MSG_DEBUG, "DBUS TDLS_STATUS " MACSTR, MAC2STR(peer));
+
+	tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
+
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply, DBUS_TYPE_STRING,
+				 &tdls_status, DBUS_TYPE_INVALID);
+	return reply;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_teardown - Teardown TDLS session
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSTeardown" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s)
+{
+	u8 peer[ETH_ALEN];
+	DBusMessage *error_reply;
+	int ret;
+
+	if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+		return error_reply;
+
+	wpa_printf(MSG_DEBUG, "DBUS TDLS_TEARDOWN " MACSTR, MAC2STR(peer));
+
+	if (wpa_tdls_is_external_setup(wpa_s->wpa))
+		ret = wpa_tdls_teardown_link(
+			wpa_s->wpa, peer,
+			WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	else
+		ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+	if (ret) {
+		return wpas_dbus_error_unknown_error(
+			message, "error performing TDLS teardown");
+	}
+
+	return NULL;
+}
+
+#endif /* CONFIG_TDLS */
+
+
+/**
+ * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing an error on failure or NULL on success
+ *
+ * Sets the PKCS #11 engine and module path.
+ */
+DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path(
+	DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter;
+	char *value = NULL;
+	char *pkcs11_engine_path = NULL;
+	char *pkcs11_module_path = NULL;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &value);
+	if (value == NULL) {
+		return dbus_message_new_error(
+			message, DBUS_ERROR_INVALID_ARGS,
+			"Invalid pkcs11_engine_path argument");
+	}
+	/* Empty path defaults to NULL */
+	if (os_strlen(value))
+		pkcs11_engine_path = value;
+
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_get_basic(&iter, &value);
+	if (value == NULL) {
+		os_free(pkcs11_engine_path);
+		return dbus_message_new_error(
+			message, DBUS_ERROR_INVALID_ARGS,
+			"Invalid pkcs11_module_path argument");
+	}
+	/* Empty path defaults to NULL */
+	if (os_strlen(value))
+		pkcs11_module_path = value;
+
+	if (wpas_set_pkcs11_engine_and_module_path(wpa_s, pkcs11_engine_path,
+						   pkcs11_module_path))
+		return dbus_message_new_error(
+			message, DBUS_ERROR_FAILED,
+			"Reinit of the EAPOL state machine with the new PKCS "
+			"#11 engine and module path failed.");
+
+	wpa_dbus_mark_property_changed(
+		wpa_s->global->dbus, wpa_s->dbus_new_path,
+		WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath");
+	wpa_dbus_mark_property_changed(
+		wpa_s->global->dbus, wpa_s->dbus_new_path,
+		WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath");
+
+	return NULL;
+}
+
+
 /**
  * wpas_dbus_getter_capabilities - Return interface capabilities
  * @iter: Pointer to incoming dbus message iter
@@ -2003,7 +2306,7 @@
 		const char *args[] = {"ccmp", "tkip", "none"};
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "Pairwise", args,
-			    sizeof(args) / sizeof(char*)))
+			    ARRAY_SIZE(args)))
 			goto nomem;
 	} else {
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise",
@@ -2012,6 +2315,18 @@
 						      &iter_array))
 			goto nomem;
 
+		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "ccmp-256"))
+				goto nomem;
+		}
+
+		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "gcmp-256"))
+				goto nomem;
+		}
+
 		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "ccmp"))
@@ -2050,7 +2365,7 @@
 		};
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "Group", args,
-			    sizeof(args) / sizeof(char*)))
+			    ARRAY_SIZE(args)))
 			goto nomem;
 	} else {
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group",
@@ -2059,6 +2374,18 @@
 						      &iter_array))
 			goto nomem;
 
+		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "ccmp-256"))
+				goto nomem;
+		}
+
+		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) {
+			if (!wpa_dbus_dict_string_array_add_element(
+				    &iter_array, "gcmp-256"))
+				goto nomem;
+		}
+
 		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "ccmp"))
@@ -2107,7 +2434,7 @@
 		};
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "KeyMgmt", args,
-			    sizeof(args) / sizeof(char*)))
+			    ARRAY_SIZE(args)))
 			goto nomem;
 	} else {
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt",
@@ -2187,7 +2514,7 @@
 		const char *args[] = { "rsn", "wpa" };
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "Protocol", args,
-			    sizeof(args) / sizeof(char*)))
+			    ARRAY_SIZE(args)))
 			goto nomem;
 	} else {
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol",
@@ -2222,7 +2549,7 @@
 		const char *args[] = { "open", "shared", "leap" };
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "AuthAlg", args,
-			    sizeof(args) / sizeof(char*)))
+			    ARRAY_SIZE(args)))
 			goto nomem;
 	} else {
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg",
@@ -2258,7 +2585,7 @@
 
 	/***** Scan */
 	if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans,
-					       sizeof(scans) / sizeof(char *)))
+					       ARRAY_SIZE(scans)))
 		goto nomem;
 
 	/***** Modes */
@@ -2986,6 +3313,76 @@
 
 
 /**
+ * wpas_dbus_getter_pkcs11_engine_path - Get PKCS #11 engine path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: A dbus message containing the PKCS #11 engine path
+ *
+ * Getter for "PKCS11EnginePath" property.
+ */
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *pkcs11_engine_path;
+
+	if (wpa_s->conf == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "wpas_dbus_getter_pkcs11_engine_path[dbus]: An "
+			   "error occurred getting the PKCS #11 engine path.");
+		dbus_set_error_const(
+			error, DBUS_ERROR_FAILED,
+			"An error occured getting the PKCS #11 engine path.");
+		return FALSE;
+	}
+
+	if (wpa_s->conf->pkcs11_engine_path == NULL)
+		pkcs11_engine_path = "";
+	else
+		pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&pkcs11_engine_path, error);
+}
+
+
+/**
+ * wpas_dbus_getter_pkcs11_module_path - Get PKCS #11 module path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: A dbus message containing the PKCS #11 module path
+ *
+ * Getter for "PKCS11ModulePath" property.
+ */
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *pkcs11_module_path;
+
+	if (wpa_s->conf == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "wpas_dbus_getter_pkcs11_module_path[dbus]: An "
+			   "error occurred getting the PKCS #11 module path.");
+		dbus_set_error_const(
+			error, DBUS_ERROR_FAILED,
+			"An error occured getting the PKCS #11 module path.");
+		return FALSE;
+	}
+
+	if (wpa_s->conf->pkcs11_module_path == NULL)
+		pkcs11_module_path = "";
+	else
+		pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&pkcs11_module_path, error);
+}
+
+
+/**
  * wpas_dbus_getter_blobs - Get all blobs defined for this interface
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -3159,11 +3556,22 @@
 	res = get_bss_helper(args, error, __func__);
 	if (!res)
 		return FALSE;
-
-	if (res->caps & IEEE80211_CAP_IBSS)
-		mode = "ad-hoc";
-	else
-		mode = "infrastructure";
+	if (bss_is_dmg(res)) {
+		switch (res->caps & IEEE80211_CAP_DMG_MASK) {
+		case IEEE80211_CAP_DMG_PBSS:
+		case IEEE80211_CAP_DMG_IBSS:
+			mode = "ad-hoc";
+			break;
+		case IEEE80211_CAP_DMG_AP:
+			mode = "infrastructure";
+			break;
+		}
+	} else {
+		if (res->caps & IEEE80211_CAP_IBSS)
+			mode = "ad-hoc";
+		else
+			mode = "infrastructure";
+	}
 
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 						&mode, error);
@@ -3283,7 +3691,7 @@
 {
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
-	const char *pairwise[3]; /* max 3 pairwise ciphers is supported */
+	const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
 	const char *key_mgmt[7]; /* max 7 key managements may be supported */
 	int n;
 
@@ -3332,6 +3740,12 @@
 	case WPA_CIPHER_WEP104:
 		group = "wep104";
 		break;
+	case WPA_CIPHER_CCMP_256:
+		group = "ccmp-256";
+		break;
+	case WPA_CIPHER_GCMP_256:
+		group = "gcmp-256";
+		break;
 	default:
 		group = "";
 		break;
@@ -3348,6 +3762,10 @@
 		pairwise[n++] = "ccmp";
 	if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP)
 		pairwise[n++] = "gcmp";
+	if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP_256)
+		pairwise[n++] = "ccmp-256";
+	if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP_256)
+		pairwise[n++] = "gcmp-256";
 
 	if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise",
 					       pairwise, n))
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index fbc8358..461970d 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -101,6 +101,9 @@
 DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
 					    struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message,
+					 struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 					       struct wpa_supplicant *wpa_s);
 
@@ -122,6 +125,9 @@
 DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
 					    struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
 					  struct wpa_supplicant *wpa_s);
 
@@ -218,6 +224,14 @@
 dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
 				      void *user_data);
 
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data);
+
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data);
+
 dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
 				   void *user_data);
 
@@ -278,6 +292,15 @@
 						 DBusError *error,
 						 void *user_data);
 
+DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
 					   const char *arg);
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 6ec96df..c9ecc7b 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -346,14 +346,14 @@
 		if (ssid == NULL || ssid->disabled != 2)
 			goto inv_args;
 
-		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0,
-						  NULL)) {
+		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
+						  NULL, 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))
+	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0))
 		goto inv_args;
 
 out:
@@ -505,7 +505,7 @@
 
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, 0, join, authorize_only,
-				   go_intent, freq, -1, 0, 0);
+				   go_intent, freq, -1, 0, 0, 0);
 
 	if (new_pin >= 0) {
 		char npin[9];
@@ -631,8 +631,8 @@
 		if (ssid == NULL || ssid->disabled != 2)
 			goto err;
 
-		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0) < 0)
-		{
+		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) <
+		    0) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -825,6 +825,16 @@
 					 wpa_s->conf->disassoc_low_ack))
 		goto err_no_mem;
 
+	/* No Group Iface */
+	if (!wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface",
+				       wpa_s->conf->p2p_no_group_iface))
+		goto err_no_mem;
+
+	/* P2P Search Delay */
+	if (!wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay",
+					 wpa_s->conf->p2p_search_delay))
+		goto err_no_mem;
+
 	if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
 	    !dbus_message_iter_close_container(iter, &variant_iter))
 		goto err_no_mem;
@@ -974,6 +984,12 @@
 		else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 &&
 			 entry.type == DBUS_TYPE_UINT32)
 			wpa_s->conf->disassoc_low_ack = entry.uint32_value;
+		else if (os_strcmp(entry.key, "NoGroupIface") == 0 &&
+			 entry.type == DBUS_TYPE_BOOLEAN)
+			wpa_s->conf->p2p_no_group_iface = entry.bool_value;
+		else if (os_strcmp(entry.key, "p2p_search_delay") == 0 &&
+			 entry.type == DBUS_TYPE_UINT32)
+			wpa_s->conf->p2p_search_delay = entry.uint32_value;
 		else
 			goto error;
 
@@ -1127,6 +1143,7 @@
 		break;
 	default:
 		str = "device";
+		break;
 	}
 
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
@@ -1438,7 +1455,7 @@
 						       void *user_data)
 {
 	struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
-	int i, num;
+	unsigned int i, num = 0;
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
 
@@ -1451,7 +1468,8 @@
 	}
 
 	/* Add WPS vendor extensions attribute */
-	for (i = 0, num = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+	os_memset(vendor_extension, 0, sizeof(vendor_extension));
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 		if (info->wps_vendor_ext[i] == NULL)
 			continue;
 		vendor_extension[num] = info->wps_vendor_ext[i];
@@ -1470,11 +1488,145 @@
 dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
 					  DBusError *error, void *user_data)
 {
-	dbus_bool_t success;
-	/* struct peer_handler_args *peer_args = user_data; */
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
 
-	success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
-							 NULL, 0, error);
+	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+				  peer_args->p2p_device_addr, 0);
+	if (info == NULL) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "failed to find peer");
+		return FALSE;
+	}
+
+	if (info->wfd_subelems == NULL)
+		return wpas_dbus_simple_array_property_getter(iter,
+							      DBUS_TYPE_BYTE,
+							      NULL, 0, error);
+
+	return wpas_dbus_simple_array_property_getter(
+		iter, DBUS_TYPE_BYTE, (char *) info->wfd_subelems->buf,
+		info->wfd_subelems->used, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
+						     DBusError *error,
+						     void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+
+	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+				  peer_args->p2p_device_addr, 0);
+	if (info == NULL) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "failed to find peer");
+		return FALSE;
+	}
+
+	return wpas_dbus_simple_array_property_getter(
+		iter, DBUS_TYPE_BYTE, (char *) info->p2p_device_addr,
+		ETH_ALEN, error);
+}
+
+
+struct peer_group_data {
+	struct wpa_supplicant *wpa_s;
+	const struct p2p_peer_info *info;
+	char **paths;
+	unsigned int nb_paths;
+	int error;
+};
+
+
+static int match_group_where_peer_is_client(struct p2p_group *group,
+					    void *user_data)
+{
+	struct peer_group_data *data = user_data;
+	const struct p2p_group_config *cfg;
+	struct wpa_supplicant *wpa_s_go;
+	char **paths;
+
+	if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr))
+		return 1;
+
+	cfg = p2p_group_get_config(group);
+
+	wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid,
+					 cfg->ssid_len);
+	if (wpa_s_go == NULL)
+		return 1;
+
+	paths = os_realloc_array(data->paths, data->nb_paths + 1,
+				 sizeof(char *));
+	if (paths == NULL)
+		goto out_of_memory;
+
+	data->paths = paths;
+	data->paths[data->nb_paths] = wpa_s_go->dbus_groupobj_path;
+	data->nb_paths++;
+
+	return 1;
+
+out_of_memory:
+	data->error = ENOMEM;
+	return 0;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	struct peer_group_data data;
+	struct wpa_supplicant *wpa_s_go;
+	dbus_bool_t success = FALSE;
+
+	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+				  peer_args->p2p_device_addr, 0);
+	if (info == NULL) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "failed to find peer");
+		return FALSE;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	wpa_s_go = wpas_get_p2p_client_iface(peer_args->wpa_s,
+					     info->p2p_device_addr);
+	if (wpa_s_go) {
+		data.paths = os_calloc(1, sizeof(char *));
+		if (data.paths == NULL)
+			goto out_of_memory;
+		data.paths[0] = wpa_s_go->dbus_groupobj_path;
+		data.nb_paths = 1;
+	}
+
+	data.wpa_s = peer_args->wpa_s;
+	data.info = info;
+
+	p2p_loop_on_all_groups(peer_args->wpa_s->global->p2p,
+			       match_group_where_peer_is_client, &data);
+	if (data.error)
+		goto out_of_memory;
+
+	if (data.paths == NULL) {
+		return wpas_dbus_simple_array_property_getter(
+			iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
+	}
+
+	success = wpas_dbus_simple_array_property_getter(iter,
+							 DBUS_TYPE_OBJECT_PATH,
+							 data.paths,
+							 data.nb_paths, error);
+	goto out;
+
+out_of_memory:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+out:
+	os_free(data.paths);
 	return success;
 }
 
@@ -1828,9 +1980,9 @@
 		if (!paths[i])
 			goto out_of_memory;
 		os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
-			    "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART
+			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
 			    "/" COMPACT_MACSTR,
-			    wpa_s->dbus_groupobj_path, MAC2STR(addr));
+			    wpa_s->parent->dbus_new_path, MAC2STR(addr));
 		i++;
 	}
 
@@ -1964,8 +2116,9 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	struct hostapd_data *hapd;
 	struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
-	int num_vendor_ext = 0;
-	int i;
+	unsigned int i, num_vendor_ext = 0;
+
+	os_memset(vendor_ext, 0, sizeof(vendor_ext));
 
 	/* Verify correct role for this property */
 	if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) {
@@ -1976,11 +2129,9 @@
 		/* Parse WPS Vendor Extensions sent in Beacon/Probe Response */
 		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
 			if (hapd->conf->wps_vendor_ext[i] == NULL)
-				vendor_ext[i] = NULL;
-			else {
-				vendor_ext[num_vendor_ext++] =
-					hapd->conf->wps_vendor_ext[i];
-			}
+				continue;
+			vendor_ext[num_vendor_ext++] =
+				hapd->conf->wps_vendor_ext[i];
 		}
 	}
 
@@ -1989,7 +2140,7 @@
 							    DBUS_TYPE_BYTE,
 							    vendor_ext,
 							    num_vendor_ext,
-						 error);
+							    error);
 }
 
 
@@ -2407,7 +2558,7 @@
 	if (req == 0)
 		goto error;
 
-	if (!wpas_p2p_sd_cancel_request(wpa_s, req))
+	if (wpas_p2p_sd_cancel_request(wpa_s, req) < 0)
 		goto error;
 
 	return NULL;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
index a11b3c8..67dbfc9 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -14,11 +14,6 @@
 	u8 p2p_device_addr[ETH_ALEN];
 };
 
-struct groupmember_handler_args {
-	struct wpa_supplicant *wpa_s;
-	u8 member_addr[ETH_ALEN];
-};
-
 /*
  * P2P Device methods
  */
@@ -147,6 +142,14 @@
 					  DBusError *error,
 					  void *user_data);
 
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
+						     DBusError *error,
+						     void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
+					     DBusError *error,
+					     void *user_data);
+
 /*
  * P2P Group properties
  */
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index e26086d..750522d 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -38,27 +38,25 @@
 
 		if (!dbus_message_iter_open_container(dict_iter,
 						      DBUS_TYPE_DICT_ENTRY,
-						      NULL, &entry_iter)) {
-			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
-			                     "no memory");
-			return FALSE;
-		}
-		if (!dbus_message_iter_append_basic(&entry_iter,
+						      NULL, &entry_iter) ||
+		    !dbus_message_iter_append_basic(&entry_iter,
 						    DBUS_TYPE_STRING,
-						    &dsc->dbus_property)) {
-			dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
-			                     "no memory");
-			return FALSE;
-		}
+						    &dsc->dbus_property))
+			goto error;
 
 		/* An error getting a property fails the request entirely */
 		if (!dsc->getter(&entry_iter, error, user_data))
 			return FALSE;
 
-		dbus_message_iter_close_container(dict_iter, &entry_iter);
+		if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
+			goto error;
 	}
 
 	return TRUE;
+
+error:
+	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
 }
 
 
@@ -111,7 +109,12 @@
 		return reply;
 	}
 
-	wpa_dbus_dict_close_write(&iter, &dict_iter);
+	if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+		dbus_message_unref(reply);
+		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+					      "out of memory");
+	}
+
 	return reply;
 }
 
@@ -840,7 +843,6 @@
 		return;
 	eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
 
-	dsc = obj_desc->properties;
 	for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
 	     dsc++, i++) {
 		if (obj_desc->prop_changed_flags == NULL ||
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index e565de9..048158f 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -25,10 +25,6 @@
 #include "dbus_old_handlers.h"
 #include "dbus_dict_helpers.h"
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
 /**
  * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message
  * @message: Pointer to incoming dbus message this error refers to
@@ -117,24 +113,28 @@
 				goto error;
 			if (!strcmp(entry.key, "driver") &&
 			    (entry.type == DBUS_TYPE_STRING)) {
+				os_free(driver);
 				driver = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
 				if (driver == NULL)
 					goto error;
 			} else if (!strcmp(entry.key, "driver-params") &&
 				   (entry.type == DBUS_TYPE_STRING)) {
+				os_free(driver_param);
 				driver_param = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
 				if (driver_param == NULL)
 					goto error;
 			} else if (!strcmp(entry.key, "config-file") &&
 				   (entry.type == DBUS_TYPE_STRING)) {
+				os_free(confname);
 				confname = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
 				if (confname == NULL)
 					goto error;
 			} else if (!strcmp(entry.key, "bridge-ifname") &&
 				   (entry.type == DBUS_TYPE_STRING)) {
+				os_free(bridge_ifname);
 				bridge_ifname = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
 				if (bridge_ifname == NULL)
@@ -350,7 +350,7 @@
 DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
 					   struct wpa_supplicant *wpa_s)
 {
-	DBusMessage *reply = NULL;
+	DBusMessage *reply;
 	DBusMessageIter iter;
 	DBusMessageIter sub_iter;
 	struct wpa_bss *bss;
@@ -358,9 +358,10 @@
 	/* Create and initialize the return message */
 	reply = dbus_message_new_method_return(message);
 	dbus_message_iter_init_append(reply, &iter);
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-					 DBUS_TYPE_OBJECT_PATH_AS_STRING,
-					 &sub_iter);
+	if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_OBJECT_PATH_AS_STRING,
+					      &sub_iter))
+		goto error;
 
 	/* Loop through scan results and append each result's object path */
 	dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
@@ -374,13 +375,21 @@
 			    "%s/" WPAS_DBUS_BSSIDS_PART "/"
 			    WPAS_DBUS_BSSID_FORMAT,
 			    wpa_s->dbus_path, MAC2STR(bss->bssid));
-		dbus_message_iter_append_basic(&sub_iter,
-					       DBUS_TYPE_OBJECT_PATH, &path);
+		if (!dbus_message_iter_append_basic(&sub_iter,
+						    DBUS_TYPE_OBJECT_PATH,
+						    &path))
+			goto error;
 	}
 
-	dbus_message_iter_close_container(&iter, &sub_iter);
+	if (!dbus_message_iter_close_container(&iter, &sub_iter))
+		goto error;
 
 	return reply;
+
+error:
+	dbus_message_unref(reply);
+	return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
+				      "an internal error occurred returning scan results");
 }
 
 
@@ -419,7 +428,7 @@
 		if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
 						     (const char *) (ie + 2),
 						     ie[1]))
-		goto error;
+			goto error;
 	}
 
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
@@ -539,7 +548,7 @@
 			const char *args[] = {"CCMP", "TKIP", "NONE"};
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "pairwise", args,
-				    sizeof(args) / sizeof(char*)))
+				    ARRAY_SIZE(args)))
 				goto error;
 		}
 	} else {
@@ -582,7 +591,7 @@
 			};
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "group", args,
-				    sizeof(args) / sizeof(char*)))
+				    ARRAY_SIZE(args)))
 				goto error;
 		}
 	} else {
@@ -632,7 +641,7 @@
 			};
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "key_mgmt", args,
-				    sizeof(args) / sizeof(char*)))
+				    ARRAY_SIZE(args)))
 				goto error;
 		}
 	} else {
@@ -683,7 +692,7 @@
 			const char *args[] = { "RSN", "WPA" };
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "proto", args,
-				    sizeof(args) / sizeof(char*)))
+				    ARRAY_SIZE(args)))
 				goto error;
 		}
 	} else {
@@ -720,7 +729,7 @@
 			const char *args[] = { "OPEN", "SHARED", "LEAP" };
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "auth_alg", args,
-				    sizeof(args) / sizeof(char*)))
+				    ARRAY_SIZE(args)))
 				goto error;
 		}
 	} else {
@@ -1204,16 +1213,19 @@
 			goto error;
 		if (!strcmp(entry.key, "opensc_engine_path") &&
 		    (entry.type == DBUS_TYPE_STRING)) {
+			os_free(opensc_engine_path);
 			opensc_engine_path = os_strdup(entry.str_value);
 			if (opensc_engine_path == NULL)
 				goto error;
 		} else if (!strcmp(entry.key, "pkcs11_engine_path") &&
 			   (entry.type == DBUS_TYPE_STRING)) {
+			os_free(pkcs11_engine_path);
 			pkcs11_engine_path = os_strdup(entry.str_value);
 			if (pkcs11_engine_path == NULL)
 				goto error;
 		} else if (!strcmp(entry.key, "pkcs11_module_path") &&
 				 (entry.type == DBUS_TYPE_STRING)) {
+			os_free(pkcs11_module_path);
 			pkcs11_module_path = os_strdup(entry.str_value);
 			if (pkcs11_module_path == NULL)
 				goto error;
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index aa6005f..94c94b1 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -20,63 +20,6 @@
 # used to fix build issues on such systems (krb5.h not found).
 #CFLAGS += -I/usr/include/kerberos
 
-# Example configuration for various cross-compilation platforms
-
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
-
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-#	-I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
-
-
-# Driver interface for Host AP driver
-CONFIG_DRIVER_HOSTAP=y
-
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
 # Driver interface for generic Linux wireless extensions
 # Note: WEXT is deprecated in the current Linux kernel version and no new
 # functionality is added to it. nl80211-based interface is the new
@@ -88,6 +31,19 @@
 # Driver interface for Linux drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# driver_nl80211.c requires libnl. If you are compiling it yourself
+# you may need to point hostapd to your version of libnl.
+#
+#CFLAGS += -I$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
+
+# Use libnl v2.0 (or 3.0) libraries.
+#CONFIG_LIBNL20=y
+
+# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
+#CONFIG_LIBNL32=y
+
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -147,10 +103,9 @@
 CONFIG_EAP_TTLS=y
 
 # EAP-FAST
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch)
-# to add the needed functions.
+# 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-GTC
@@ -197,8 +152,6 @@
 
 # Wi-Fi Protected Setup (WPS)
 #CONFIG_WPS=y
-# Enable WSC 2.0 support
-#CONFIG_WPS2=y
 # Enable WPS external registrar functionality
 #CONFIG_WPS_ER=y
 # Disable credentials for an open network by default when acting as a WPS
@@ -237,8 +190,10 @@
 # Select control interface backend for external programs, e.g, wpa_cli:
 # unix = UNIX domain sockets (default for Linux/*BSD)
 # udp = UDP sockets using localhost (127.0.0.1)
+# udp6 = UDP IPv6 sockets using localhost (::1)
 # named_pipe = Windows Named Pipe (default for Windows)
 # udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose)
 # y = use default (backwards compatibility)
 # If this option is commented out, control interface is not included in the
 # build.
@@ -298,7 +253,7 @@
 # main_none = Very basic example (development use only)
 #CONFIG_MAIN=main
 
-# Select wrapper for operatins system and C library specific functions
+# Select wrapper for operating system and C library specific functions
 # unix = UNIX/POSIX like systems (default)
 # win32 = Windows systems
 # none = Empty template
@@ -307,12 +262,14 @@
 # Select event loop implementation
 # eloop = select() loop (default)
 # eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
 #CONFIG_ELOOP=eloop
 
 # Should we use poll instead of select? Select is used by default.
 #CONFIG_ELOOP_POLL=y
 
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -484,6 +441,10 @@
 # IEEE 802.11n (High Throughput) support (mainly for AP mode)
 #CONFIG_IEEE80211N=y
 
+# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode)
+# (depends on CONFIG_IEEE80211N)
+#CONFIG_IEEE80211AC=y
+
 # Wireless Network Management (IEEE Std 802.11v-2011)
 # Note: This is experimental and not complete implementation.
 #CONFIG_WNM=y
@@ -515,9 +476,14 @@
 # Enable TDLS support
 #CONFIG_TDLS=y
 
+# Wi-Fi Direct
+# This can be used to enable Wi-Fi Direct extensions for P2P using an external
+# program to control the additional information exchanges in the messages.
+#CONFIG_WIFI_DISPLAY=y
+
 # Autoscan
 # This can be used to enable automatic scan support in wpa_supplicant.
-# See wpa_supplicant.conf for more information on autoscan usage.
+# See wpa_supplicant.conf for more information on autoscan usage.
 #
 # Enabling directly a module will enable autoscan support.
 # For exponential module:
diff --git a/wpa_supplicant/doc/docbook/Makefile b/wpa_supplicant/doc/docbook/Makefile
index aaeee2e..82f9de3 100644
--- a/wpa_supplicant/doc/docbook/Makefile
+++ b/wpa_supplicant/doc/docbook/Makefile
@@ -7,6 +7,7 @@
 FILES += wpa_priv
 FILES += wpa_supplicant.conf
 FILES += wpa_supplicant
+FILES += eapol_test
 
 man:
 	for i in $(FILES); do docbook2man $$i.sgml; done
@@ -20,7 +21,7 @@
 
 
 clean:
-	rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8
+	rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 eapol_test.8
 	rm -f wpa_supplicant.conf.5
 	rm -f manpage.links manpage.refs
 	rm -f $(FILES:%=%.pdf)
diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml
new file mode 100644
index 0000000..fec174b
--- /dev/null
+++ b/wpa_supplicant/doc/docbook/eapol_test.sgml
@@ -0,0 +1,205 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>eapol_test</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>eapol_test</refname>
+
+    <refpurpose>EAP peer and RADIUS client testing</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>eapol_test</command>
+      <arg>-nWS</arg>
+      <arg>-c<replaceable>config file</replaceable></arg>
+      <arg>-a<replaceable>server IP address</replaceable></arg>
+      <arg>-A<replaceable>client IP address</replaceable></arg>
+      <arg>-p<replaceable>UDP port</replaceable></arg>
+      <arg>-s<replaceable>shared secret</replaceable></arg>
+      <arg>-r<replaceable>re-authentications</replaceable></arg>
+      <arg>-t<replaceable>timeout</replaceable></arg>
+      <arg>-C<replaceable>Connect-Info</replaceable></arg>
+      <arg>-M<replaceable>MAC address</replaceable></arg>
+      <arg>-o<replaceable>file</replaceable></arg>
+      <arg>-N<replaceable>attr spec</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>eapol_test scard</command>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>eapol_test sim</command>
+      <arg>PIN</arg>
+      <arg>num triplets</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para>eapol_test is a program that links together the same EAP
+    peer implementation that wpa_supplicant is using and the RADIUS
+    authentication client code from hostapd. In addition, it has
+    minimal glue code to combine these two components in similar
+    ways to IEEE 802.1X/EAPOL Authenticator state machines. In other
+    words, it integrates IEEE 802.1X Authenticator (normally, an
+    access point) and IEEE 802.1X Supplicant (normally, a wireless
+    client) together to generate a single program that can be used to
+    test EAP methods without having to setup an access point and a
+    wireless client.</para>
+
+    <para>The main uses for eapol_test are in interoperability testing
+    of EAP methods against RADIUS servers and in development testing
+    for new EAP methods. It can be easily used to automate EAP testing
+    for interoperability and regression since the program can be run
+    from shell scripts without require additional test components apart
+    from a RADIUS server. For example, the automated EAP tests described
+    in eap_testing.txt are implemented with eapol_test. Similarly,
+    eapol_test could be used to implement an automated regression
+    test suite for a RADIUS authentication server.</para>
+
+
+    <para>As an example:</para>
+
+<blockquote><programlisting>
+eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1
+</programlisting></blockquote>
+
+    <para>tries to complete EAP authentication based on the network
+    configuration from test.conf against the RADIUS server running
+    on the local host. A re-authentication is triggered to test fast
+    re-authentication. The configuration file uses the same format for
+    network blocks as wpa_supplicant.</para>
+
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+	<term>-c configuration file path</term>
+
+	<listitem><para>A configuration to use.  The configuration should
+	use the same format for network blocks as wpa_supplicant.
+	</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-a AS address</term>
+
+	<listitem><para>IP address of the authentication server.  The
+	default is '127.0.0.1'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-A client address</term>
+
+	<listitem><para>IP address of the client.  The default is to
+	select an address automatically.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-p AS port</term>
+
+	<listitem><para>UDP port of the authentication server. The
+	default is '1812'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-s AS secret</term>
+
+	<listitem><para>Shared secret with the authentication server.
+	The default is 'radius'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-r count</term>
+
+	<listitem><para>Number of reauthentications.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-t timeout</term>
+
+	<listitem><para>Timeout in seconds. The default is 30.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-C info</term>
+
+	<listitem><para>RADIUS Connect-Info.  The default is
+	'CONNECT 11Mbps 802.11b'.</para></listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+	<term>-M mac address</term>
+
+	<listitem><para>Client MAC address (Calling-Station-Id).  The
+	default is '02:00:00:00:00:01'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-o file</term>
+
+	<listitem><para>Location to write out server certificate.
+	</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-N attr spec</term>
+
+	<listitem><para>Send arbitrary attribute specific by
+	attr_id:syntax:value, or attr_id alone.  attr_id should be the numeric
+	ID of the attribute, and syntax should be one of 's' (string),
+	'd' (integer), or 'x' (octet string). The value is the attribute value
+	to send.  When attr_id is given alone, NULL is used as the attribute
+	value.  Multiple attributes can be specified by using the option
+	several times.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-n</term>
+
+	<listitem><para>Indicates that no MPPE keys are expected.
+	</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-W</term>
+
+	<listitem><para>Wait for a control interface monitor before starting.
+	</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-S</term>
+
+	<listitem><para>Save configuration after authentication.
+	</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+	<refentrytitle>wpa_supplicant</refentrytitle>
+	<manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2014,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml
index eb3a089..860b5a0 100644
--- a/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -90,7 +90,7 @@
 
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2014,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml
index c080c07..142e1ab 100644
--- a/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -15,10 +15,12 @@
     <cmdsynopsis>
       <command>wpa_cli</command>
       <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
+      <arg>-g <replaceable>path to global ctrl_interface socket</replaceable></arg>
       <arg>-i <replaceable>ifname</replaceable></arg>
       <arg>-hvB</arg>
       <arg>-a <replaceable>action file</replaceable></arg>
       <arg>-P <replaceable>pid file</replaceable></arg>
+      <arg>-G <replaceable>ping interval</replaceable></arg>
       <arg><replaceable>command ...</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -111,6 +113,14 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-g control socket path</term>
+
+	<listitem><para>Connect to the global control socket at the
+	indicated path rather than an interface-specific control
+	socket.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-i ifname</term>
 
         <listitem><para>Specify the interface that is being
@@ -161,6 +171,13 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-G ping interval</term>
+
+	<listitem><para>Set the interval (in seconds) at which
+	wpa_cli pings the supplicant.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>command</term>
 
 	<listitem><para>Run a command.  The available commands are
@@ -328,7 +345,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2014,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 0ab6419..f6ef8f1 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -74,7 +74,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2014,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
index 336c03b..3b4360b 100644
--- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -62,7 +62,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2014,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml
index eb907a8..9c114cc 100644
--- a/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -137,7 +137,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2014,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index aa20e57..182060d 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -12,7 +12,7 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>wpa_supplicant</command>
-      <arg>-BddfhKLqqtuvW</arg>
+      <arg>-BddfhKLqqsTtuvW</arg>
       <arg>-i<replaceable>ifname</replaceable></arg>
       <arg>-c<replaceable>config file</replaceable></arg>
       <arg>-D<replaceable>driver</replaceable></arg>
@@ -344,9 +344,20 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-e entropy file</term>
+	<listitem>
+	  <para>File for <command>wpa_supplicant</command> to use to
+	  maintain its internal entropy store in over restarts.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-f output file</term>
 	<listitem>
-	  <para>Log output to specified file instead of stdout.</para>
+	  <para>Log output to specified file instead of stdout. (This
+	  is only available if <command>wpa_supplicant</command> was
+	  built with the <literal>CONFIG_DEBUG_FILE</literal>
+	  option.)</para>
 	</listitem>
       </varlistentry>
 
@@ -387,6 +398,22 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-o override driver</term>
+	<listitem>
+	  <para>Override the driver parameter for new
+	  interfaces.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-O override ctrl_interface</term>
+	<listitem>
+	  <para>Override the ctrl_interface parameter for new
+	  interfaces.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-p</term>
 	<listitem>
 	  <para>Driver parameters. (Per interface)</para>
@@ -409,10 +436,40 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-s</term>
+	<listitem>
+	  <para>Log output to syslog instead of stdout. (This is only
+	  available if <command>wpa_supplicant</command> was built
+	  with the <literal>CONFIG_DEBUG_SYSLOG</literal>
+	  option.)</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-T</term>
+	<listitem>
+	  <para>Log output to Linux tracing in addition to any other
+	  destinations. (This is only available
+	  if <command>wpa_supplicant</command> was built with
+	  the <literal>CONFIG_DEBUG_LINUX_TRACING</literal>
+	  option.)</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
+	<term>-t</term>
+	<listitem>
+	  <para>Include timestamp in debug messages.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-u</term>
 	<listitem>
-	  <para>Enabled DBus control interface. If enabled, interface
-	  definitions may be omitted.</para>
+	  <para>Enable DBus control interface. If enabled, interface
+	  definitions may be omitted. (This is only available
+	  if <command>wpa_supplicant</command> was built with
+	  the <literal>CONFIG_DBUS</literal> option.)</para>0
 	</listitem>
       </varlistentry>
 
@@ -679,7 +736,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2014,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 5e7dbf7..00703d9 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -117,11 +117,16 @@
 static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
 				  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)
+				  const u8 *seq, size_t seq_len,
+				  const u8 *key, size_t key_len)
 {
+	if (alg != WPA_ALG_NONE) {
+		if (key_idx >= 0 && key_idx <= 6)
+			wpa_s->keys_cleared &= ~BIT(key_idx);
+		else
+			wpa_s->keys_cleared = 0;
+	}
 	if (wpa_s->driver->set_key) {
-		wpa_s->keys_cleared = 0;
 		return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv,
 					      alg, addr, key_idx, set_tx,
 					      seq, seq_len, key, key_len);
@@ -201,6 +206,14 @@
 	return NULL;
 }
 
+static inline const char *
+wpa_driver_get_radio_name(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->get_radio_name)
+		return wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+	return NULL;
+}
+
 static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->driver->get_mac_addr) {
@@ -380,7 +393,7 @@
 	if (wpa_s->driver->if_add)
 		return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
 					     addr, bss_ctx, NULL, force_ifname,
-					     if_addr, bridge);
+					     if_addr, bridge, 0);
 	return -1;
 }
 
@@ -516,143 +529,17 @@
 	return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
 }
 
-static inline int wpa_drv_p2p_find(struct wpa_supplicant *wpa_s,
-				   unsigned int timeout, int type)
-{
-	if (!wpa_s->driver->p2p_find)
-		return -1;
-	return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type);
-}
-
-static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s->driver->p2p_stop_find)
-		return -1;
-	return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv);
-}
-
-static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s,
-				     unsigned int timeout)
-{
-	if (!wpa_s->driver->p2p_listen)
-		return -1;
-	return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout);
-}
-
-static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s,
-				      const u8 *peer_addr, int wps_method,
-				      int go_intent,
-				      const u8 *own_interface_addr,
-				      unsigned int force_freq,
-				      int persistent_group)
-{
-	if (!wpa_s->driver->p2p_connect)
-		return -1;
-	return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr,
-					  wps_method, go_intent,
-					  own_interface_addr, force_freq,
-					  persistent_group);
-}
-
-static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s,
-					 const u8 *peer_addr)
-{
-	if (!wpa_s->driver->wps_success_cb)
-		return -1;
-	return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr);
-}
-
-static inline int
-wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s->driver->p2p_group_formation_failed)
-		return -1;
-	return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv);
-}
-
-static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s,
-					 const struct p2p_params *params)
-{
-	if (!wpa_s->driver->p2p_set_params)
-		return -1;
-	return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params);
-}
-
-static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s,
-					    const u8 *peer_addr,
-					    u16 config_methods, int join)
-{
-	if (!wpa_s->driver->p2p_prov_disc_req)
-		return -1;
-	return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr,
-						config_methods, join);
-}
-
-static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s,
-					 const u8 *dst,
-					 const struct wpabuf *tlvs)
-{
-	if (!wpa_s->driver->p2p_sd_request)
-		return 0;
-	return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs);
-}
-
-static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s,
-						u64 req)
-{
-	if (!wpa_s->driver->p2p_sd_cancel_request)
-		return -1;
-	return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req);
-}
-
-static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s,
-					  int freq, const u8 *dst,
-					  u8 dialog_token,
-					  const struct wpabuf *resp_tlvs)
-{
-	if (!wpa_s->driver->p2p_sd_response)
-		return -1;
-	return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst,
-					      dialog_token, resp_tlvs);
-}
-
-static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s->driver->p2p_service_update)
-		return -1;
-	return wpa_s->driver->p2p_service_update(wpa_s->drv_priv);
-}
-
-static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s,
-				     const u8 *addr)
-{
-	if (!wpa_s->driver->p2p_reject)
-		return -1;
-	return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr);
-}
-
-static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s,
-				     const u8 *peer, int role, const u8 *bssid,
-				     const u8 *ssid, size_t ssid_len,
-				     const u8 *go_dev_addr,
-				     int persistent_group)
-{
-	if (!wpa_s->driver->p2p_invite)
-		return -1;
-	return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid,
-					 ssid, ssid_len, go_dev_addr,
-					 persistent_group);
-}
-
 static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
 					 const u8 *dst, u8 action_code,
 					 u8 dialog_token, u16 status_code,
-					 const u8 *buf, size_t len)
+					 u32 peer_capab, const u8 *buf,
+					 size_t len)
 {
 	if (wpa_s->driver->send_tdls_mgmt) {
 		return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
 						     action_code, dialog_token,
-						     status_code, buf, len);
+						     status_code, peer_capab,
+						     buf, len);
 	}
 	return -1;
 }
@@ -673,7 +560,7 @@
 		return -1;
 	return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len);
 }
-#endif
+#endif /* ANDROID */
 
 static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
 					  const u8 *kek, const u8 *kck,
@@ -693,11 +580,11 @@
 }
 
 static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
-					 unsigned int freq)
+					 struct csa_settings *settings)
 {
 	if (!wpa_s->driver->switch_channel)
 		return -1;
-	return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq);
+	return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings);
 }
 
 static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
@@ -710,4 +597,234 @@
 				       buf_len);
 }
 
+static inline int wpa_drv_status(struct wpa_supplicant *wpa_s,
+				 char *buf, size_t buflen)
+{
+	if (!wpa_s->driver->status)
+		return -1;
+	return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen);
+}
+
+static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s,
+				      const u8 *qos_map_set, u8 qos_map_set_len)
+{
+	if (!wpa_s->driver->set_qos_map)
+		return -1;
+	return wpa_s->driver->set_qos_map(wpa_s->drv_priv, qos_map_set,
+					  qos_map_set_len);
+}
+
+static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s,
+				 const struct wowlan_triggers *triggers)
+{
+	if (!wpa_s->driver->set_wowlan)
+		return -1;
+	return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers);
+}
+
+static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
+				     int vendor_id, int subcmd, const u8 *data,
+				     size_t data_len, struct wpabuf *buf)
+{
+	if (!wpa_s->driver->vendor_cmd)
+		return -1;
+	return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
+					 data, data_len, buf);
+}
+
+
+#ifdef CONFIG_MACSEC
+
+static inline int wpa_drv_macsec_init(struct wpa_supplicant *wpa_s,
+				      struct macsec_init_params *params)
+{
+	if (!wpa_s->driver->macsec_init)
+		return -1;
+	return wpa_s->driver->macsec_init(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->driver->macsec_deinit)
+		return -1;
+	return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
+						Boolean enabled)
+{
+	if (!wpa_s->driver->enable_protect_frames)
+		return -1;
+	return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled);
+}
+
+static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s,
+					     Boolean enabled, u32 window)
+{
+	if (!wpa_s->driver->set_replay_protect)
+		return -1;
+	return wpa_s->driver->set_replay_protect(wpa_s->drv_priv, enabled,
+						 window);
+}
+
+static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s,
+						   const u8 *cs, size_t cs_len)
+{
+	if (!wpa_s->driver->set_current_cipher_suite)
+		return -1;
+	return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs,
+						       cs_len);
+}
+
+static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s,
+						 Boolean enabled)
+{
+	if (!wpa_s->driver->enable_controlled_port)
+		return -1;
+	return wpa_s->driver->enable_controlled_port(wpa_s->drv_priv, enabled);
+}
+
+static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s,
+						u32 channel, u8 an,
+						u32 *lowest_pn)
+{
+	if (!wpa_s->driver->get_receive_lowest_pn)
+		return -1;
+	return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel,
+						    an, lowest_pn);
+}
+
+static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s,
+						u32 channel, u8 an,
+						u32 *next_pn)
+{
+	if (!wpa_s->driver->get_transmit_next_pn)
+		return -1;
+	return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel,
+						    an, next_pn);
+}
+
+static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
+						u32 channel, u8 an,
+						u32 next_pn)
+{
+	if (!wpa_s->driver->set_transmit_next_pn)
+		return -1;
+	return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel,
+						    an, next_pn);
+}
+
+static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s,
+						   u32 *channel)
+{
+	if (!wpa_s->driver->get_available_receive_sc)
+		return -1;
+	return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv,
+						       channel);
+}
+
+static inline int
+wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel,
+			  const u8 *sci_addr, u16 sci_port,
+			  unsigned int conf_offset, int validation)
+{
+	if (!wpa_s->driver->create_receive_sc)
+		return -1;
+	return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel,
+						sci_addr, sci_port, conf_offset,
+						validation);
+}
+
+static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s,
+					    u32 channel)
+{
+	if (!wpa_s->driver->delete_receive_sc)
+		return -1;
+	return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s,
+					    u32 channel, u8 an,
+					    u32 lowest_pn, const u8 *sak)
+{
+	if (!wpa_s->driver->create_receive_sa)
+		return -1;
+	return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an,
+						lowest_pn, sak);
+}
+
+static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
+					    u32 channel, u8 an)
+{
+	if (!wpa_s->driver->enable_receive_sa)
+		return -1;
+	return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
+					     u32 channel, u8 an)
+{
+	if (!wpa_s->driver->disable_receive_sa)
+		return -1;
+	return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int
+wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel)
+{
+	if (!wpa_s->driver->get_available_transmit_sc)
+		return -1;
+	return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv,
+							channel);
+}
+
+static inline int
+wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel,
+			   const u8 *sci_addr, u16 sci_port,
+			   unsigned int conf_offset)
+{
+	if (!wpa_s->driver->create_transmit_sc)
+		return -1;
+	return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel,
+						 sci_addr, sci_port,
+						 conf_offset);
+}
+
+static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s,
+					     u32 channel)
+{
+	if (!wpa_s->driver->delete_transmit_sc)
+		return -1;
+	return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s,
+					     u32 channel, u8 an,
+					     u32 next_pn,
+					     Boolean confidentiality,
+					     const u8 *sak)
+{
+	if (!wpa_s->driver->create_transmit_sa)
+		return -1;
+	return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an,
+						 next_pn, confidentiality, sak);
+}
+
+static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s,
+					     u32 channel, u8 an)
+{
+	if (!wpa_s->driver->enable_transmit_sa)
+		return -1;
+	return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s,
+					      u32 channel, u8 an)
+{
+	if (!wpa_s->driver->disable_transmit_sa)
+		return -1;
+	return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an);
+}
+#endif /* CONFIG_MACSEC */
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c
index 6cd2fc5..ece5716 100644
--- a/wpa_supplicant/eap_register.c
+++ b/wpa_supplicant/eap_register.c
@@ -40,6 +40,13 @@
 		ret = eap_peer_unauth_tls_register();
 #endif /* EAP_UNAUTH_TLS */
 
+#ifdef EAP_TLS
+#ifdef CONFIG_HS20
+	if (ret == 0)
+		ret = eap_peer_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_TLS */
+
 #ifdef EAP_MSCHAPv2
 	if (ret == 0)
 		ret = eap_peer_mschapv2_register();
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index dad2765..e19782f 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - test code
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,11 +27,9 @@
 #include "common/wpa_ctrl.h"
 #include "ctrl_iface.h"
 #include "pcsc_funcs.h"
+#include "wpas_glue.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
 struct wpa_driver_ops *wpa_drivers[] = { NULL };
 
 
@@ -48,6 +46,7 @@
 	int eapol_test_num_reauths;
 	int no_mppe_keys;
 	int num_mppe_ok, num_mppe_mismatch;
+	int req_eap_key_name;
 
 	u8 radius_identifier;
 	struct radius_msg *last_recv_radius;
@@ -60,6 +59,8 @@
 
 	u8 authenticator_pmk[PMK_LEN];
 	size_t authenticator_pmk_len;
+	u8 authenticator_eap_key_name[256];
+	size_t authenticator_eap_key_name_len;
 	int radius_access_accept_received;
 	int radius_access_reject_received;
 	int auth_timed_out;
@@ -72,6 +73,9 @@
 	struct extra_radius_attr *extra_attrs;
 
 	FILE *server_cert_file;
+
+	const char *pcsc_reader;
+	const char *pcsc_pin;
 };
 
 static struct eapol_test_data eapol_test;
@@ -210,6 +214,13 @@
 		goto fail;
 	}
 
+	if (e->req_eap_key_name &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, (u8 *) "\0",
+				 1)) {
+		printf("Could not add EAP-Key-Name\n");
+		goto fail;
+	}
+
 	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
 				 (u8 *) &e->own_ip_addr, 4)) {
@@ -335,6 +346,8 @@
 {
 	u8 pmk[PMK_LEN];
 	int ret = 1;
+	const u8 *sess_id;
+	size_t sess_id_len;
 
 	if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) {
 		wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN);
@@ -363,14 +376,37 @@
 	else if (!e->no_mppe_keys)
 		e->num_mppe_ok++;
 
+	sess_id = eapol_sm_get_session_id(e->wpa_s->eapol, &sess_id_len);
+	if (!sess_id)
+		return ret;
+	if (e->authenticator_eap_key_name_len == 0) {
+		wpa_printf(MSG_INFO, "No EAP-Key-Name received from server");
+		return ret;
+	}
+
+	if (e->authenticator_eap_key_name_len != sess_id_len ||
+	    os_memcmp(e->authenticator_eap_key_name, sess_id, sess_id_len) != 0)
+	{
+		wpa_printf(MSG_INFO,
+			   "Locally derived EAP Session-Id does not match EAP-Key-Name from server");
+		wpa_hexdump(MSG_DEBUG, "EAP Session-Id", sess_id, sess_id_len);
+		wpa_hexdump(MSG_DEBUG, "EAP-Key-Name from server",
+			    e->authenticator_eap_key_name,
+			    e->authenticator_eap_key_name_len);
+	} else {
+		wpa_printf(MSG_INFO,
+			   "Locally derived EAP Session-Id matches EAP-Key-Name from server");
+	}
+
 	return ret;
 }
 
 
-static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
+static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result,
+			void *ctx)
 {
 	struct eapol_test_data *e = ctx;
-	printf("eapol_sm_cb: success=%d\n", success);
+	printf("eapol_sm_cb: result=%d\n", result);
 	e->eapol_test_num_reauths--;
 	if (e->eapol_test_num_reauths < 0)
 		eloop_terminate();
@@ -395,6 +431,54 @@
 }
 
 
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
+					const char *default_txt)
+{
+	struct eapol_test_data *e = ctx;
+	struct wpa_supplicant *wpa_s = e->wpa_s;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	const char *field_name, *txt = NULL;
+	char *buf;
+	size_t buflen;
+	int len;
+
+	if (ssid == NULL)
+		return;
+
+	field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
+						       &txt);
+	if (field_name == NULL) {
+		wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
+			   field);
+		return;
+	}
+
+	buflen = 100 + os_strlen(txt) + ssid->ssid_len;
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	len = os_snprintf(buf, buflen,
+			  WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
+			  field_name, ssid->id, txt);
+	if (len < 0 || (size_t) len >= buflen) {
+		os_free(buf);
+		return;
+	}
+	if (ssid->ssid && buflen > len + ssid->ssid_len) {
+		os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+		len += ssid->ssid_len;
+		buf[len] = '\0';
+	}
+	buf[buflen - 1] = '\0';
+	wpa_msg(wpa_s, MSG_INFO, "%s", buf);
+	os_free(buf);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eapol_test_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
 static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
 			       const char *cert_hash,
 			       const struct wpabuf *cert)
@@ -484,6 +568,7 @@
 	ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
 	ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
 	ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+	ctx->eap_param_needed = eapol_test_eap_param_needed;
 	ctx->cert_cb = eapol_test_cert_cb;
 	ctx->cert_in_cb = 1;
 	ctx->set_anon_id = eapol_test_set_anon_id;
@@ -501,6 +586,7 @@
 	eapol_conf.required_keys = 0;
 	eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
 	eapol_conf.workaround = ssid->eap_workaround;
+	eapol_conf.external_sim = wpa_s->conf->external_sim;
 	eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
 	eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
 
@@ -700,6 +786,8 @@
 				size_t shared_secret_len)
 {
 	struct radius_ms_mppe_keys *keys;
+	u8 *buf;
+	size_t len;
 
 	keys = radius_msg_get_ms_keys(msg, req, shared_secret,
 				      shared_secret_len);
@@ -738,6 +826,14 @@
 		os_free(keys->recv);
 		os_free(keys);
 	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
+				    NULL) == 0) {
+		os_memcpy(e->authenticator_eap_key_name, buf, len);
+		e->authenticator_eap_key_name_len = len;
+	} else {
+		e->authenticator_eap_key_name_len = 0;
+	}
 }
 
 
@@ -861,7 +957,7 @@
 }
 
 
-static int scard_test(void)
+static int scard_test(struct eapol_test_data *e)
 {
 	struct scard_data *scard;
 	size_t len;
@@ -892,10 +988,10 @@
 	unsigned char aka_ik[IK_LEN];
 	unsigned char aka_ck[CK_LEN];
 
-	scard = scard_init(SCARD_TRY_BOTH, NULL);
+	scard = scard_init(e->pcsc_reader);
 	if (scard == NULL)
 		return -1;
-	if (scard_set_pin(scard, "1234")) {
+	if (scard_set_pin(scard, e->pcsc_pin)) {
 		wpa_printf(MSG_WARNING, "PIN validation failed");
 		scard_deinit(scard);
 		return -1;
@@ -970,7 +1066,7 @@
 }
 
 
-static int scard_get_triplets(int argc, char *argv[])
+static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[])
 {
 	struct scard_data *scard;
 	size_t len;
@@ -992,7 +1088,7 @@
 		wpa_debug_level = 99;
 	}
 
-	scard = scard_init(SCARD_GSM_SIM_ONLY, NULL);
+	scard = scard_init(e->pcsc_reader);
 	if (scard == NULL) {
 		printf("Failed to open smartcard connection\n");
 		return -1;
@@ -1046,11 +1142,12 @@
 static void usage(void)
 {
 	printf("usage:\n"
-	       "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
+	       "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
 	       "[-s<AS secret>]\\\n"
 	       "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
 	       "           [-M<client MAC address>] [-o<server cert file] \\\n"
-	       "           [-N<attr spec>] \\\n"
+	       "           [-N<attr spec>] [-R<PC/SC reader>] "
+	       "[-P<PC/SC PIN>] \\\n"
 	       "           [-A<client IP>]\n"
 	       "eapol_test scard\n"
 	       "eapol_test sim <PIN> <num triplets> [debug]\n"
@@ -1066,6 +1163,7 @@
 	       "  -A<client IP> = IP address of the client, default: select "
 	       "automatically\n"
 	       "  -r<count> = number of re-authentications\n"
+	       "  -e = Request EAP-Key-Name\n"
 	       "  -W = wait for a control interface monitor before starting\n"
 	       "  -S = save configuration after authentication\n"
 	       "  -n = no MPPE keys expected\n"
@@ -1094,6 +1192,7 @@
 
 int main(int argc, char *argv[])
 {
+	struct wpa_global global;
 	struct wpa_supplicant wpa_s;
 	int c, ret = 1, wait_for_monitor = 0, save_config = 0;
 	char *as_addr = "127.0.0.1";
@@ -1113,12 +1212,13 @@
 	os_memset(&eapol_test, 0, sizeof(eapol_test));
 	eapol_test.connect_info = "CONNECT 11Mbps 802.11b";
 	os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN);
+	eapol_test.pcsc_pin = "1234";
 
 	wpa_debug_level = 0;
 	wpa_debug_show_keys = 1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W");
+		c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1134,6 +1234,9 @@
 		case 'C':
 			eapol_test.connect_info = optarg;
 			break;
+		case 'e':
+			eapol_test.req_eap_key_name = 1;
+			break;
 		case 'M':
 			if (hwaddr_aton(optarg, eapol_test.own_addr)) {
 				usage();
@@ -1156,9 +1259,15 @@
 		case 'p':
 			as_port = atoi(optarg);
 			break;
+		case 'P':
+			eapol_test.pcsc_pin = optarg;
+			break;
 		case 'r':
 			eapol_test.eapol_test_num_reauths = atoi(optarg);
 			break;
+		case 'R':
+			eapol_test.pcsc_reader = optarg;
+			break;
 		case 's':
 			as_secret = optarg;
 			break;
@@ -1206,11 +1315,11 @@
 	}
 
 	if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
-		return scard_test();
+		return scard_test(&eapol_test);
 	}
 
 	if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
-		return scard_get_triplets(argc - optind - 1,
+		return scard_get_triplets(&eapol_test, argc - optind - 1,
 					  &argv[optind + 1]);
 	}
 
@@ -1230,8 +1339,12 @@
 		return -1;
 	}
 
+	os_memset(&global, 0, sizeof(global));
 	os_memset(&wpa_s, 0, sizeof(wpa_s));
+	wpa_s.global = &global;
 	eapol_test.wpa_s = &wpa_s;
+	dl_list_init(&wpa_s.bss);
+	dl_list_init(&wpa_s.bss_id);
 	wpa_s.conf = wpa_config_read(conf, NULL);
 	if (wpa_s.conf == NULL) {
 		printf("Failed to parse configuration file '%s'.\n", conf);
@@ -1242,6 +1355,11 @@
 		return -1;
 	}
 
+	if (eapol_test.pcsc_reader) {
+		os_free(wpa_s.conf->pcsc_reader);
+		wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
+	}
+
 	wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
 		      cli_addr);
 	wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 9de8d7f..ba9e083 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -46,19 +46,19 @@
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
 static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
-					      int new_scan);
+					      int new_scan, int own_request);
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
 static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
 			      struct wpa_ssid *ssid)
 {
-	struct os_time now;
+	struct os_reltime now;
 
 	if (ssid == NULL || ssid->disabled_until.sec == 0)
 		return 0;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 	if (ssid->disabled_until.sec > now.sec)
 		return ssid->disabled_until.sec - now.sec;
 
@@ -68,13 +68,46 @@
 }
 
 
+static struct wpa_bss * wpa_supplicant_get_new_bss(
+	struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wpa_bss *bss = NULL;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (ssid->ssid_len > 0)
+		bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+	if (!bss)
+		bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+	return bss;
+}
+
+
+static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+
+	if (!bss) {
+		wpa_supplicant_update_scan_results(wpa_s);
+
+		/* Get the BSS from the new scan results */
+		bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+	}
+
+	if (bss)
+		wpa_s->current_bss = bss;
+}
+
+
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid, *old_ssid;
 	int res;
 
-	if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
+	if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
+		wpa_supplicant_update_current_bss(wpa_s);
 		return 0;
+	}
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
 		"information");
@@ -119,6 +152,9 @@
 		eapol_sm_invalidate_cached_session(wpa_s->eapol);
 	old_ssid = wpa_s->current_ssid;
 	wpa_s->current_ssid = ssid;
+
+	wpa_supplicant_update_current_bss(wpa_s);
+
 	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
 	wpa_supplicant_initiate_eapol(wpa_s);
 	if (old_ssid != wpa_s->current_ssid)
@@ -160,9 +196,6 @@
 		return;
 
 	wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
-#ifdef ANDROID
-	wpa_s->conf->ap_scan = DEFAULT_AP_SCAN;
-#endif
 	bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
 	os_memset(wpa_s->bssid, 0, ETH_ALEN);
 	os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
@@ -278,10 +311,11 @@
 #ifdef PCSC_FUNCS
 	int aka = 0, sim = 0;
 
-	if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
+	if ((ssid != NULL && ssid->eap.pcsc == NULL) ||
+	    wpa_s->scard != NULL || wpa_s->conf->external_sim)
 		return 0;
 
-	if (ssid->eap.eap_methods == NULL) {
+	if (ssid == NULL || ssid->eap.eap_methods == NULL) {
 		sim = 1;
 		aka = 1;
 	} else {
@@ -316,8 +350,7 @@
 	wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
 		"(sim=%d aka=%d) - initialize PCSC", sim, aka);
 
-	wpa_s->scard = scard_init((!sim && aka) ? SCARD_USIM_ONLY :
-				  SCARD_TRY_BOTH, NULL);
+	wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
 	if (wpa_s->scard == NULL) {
 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
 			"(pcsc-lite)");
@@ -373,6 +406,9 @@
 	if (wpa_key_mgmt_wpa(ssid->key_mgmt))
 		privacy = 1;
 
+	if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)
+		privacy = 1;
+
 	if (bss->caps & IEEE80211_CAP_PRIVACY)
 		return privacy;
 	return !privacy;
@@ -514,6 +550,12 @@
 		return 0;
 	}
 
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
+	    wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   allow in OSEN");
+		return 1;
+	}
+
 	if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "   allow in non-WPA/WPA2");
 		return 1;
@@ -679,12 +721,6 @@
 }
 
 
-static int bss_is_dmg(struct wpa_bss *bss)
-{
-	return bss->freq > 45000;
-}
-
-
 /*
  * Test whether BSS is in an ESS.
  * This is done differently in DMG (60 GHz) and non-DMG bands
@@ -703,13 +739,15 @@
 
 static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 					    int i, struct wpa_bss *bss,
-					    struct wpa_ssid *group)
+					    struct wpa_ssid *group,
+					    int only_first_ssid)
 {
 	u8 wpa_ie_len, rsn_ie_len;
 	int wpa;
 	struct wpa_blacklist *e;
 	const u8 *ie;
 	struct wpa_ssid *ssid;
+	int osen;
 
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	wpa_ie_len = ie ? ie[1] : 0;
@@ -717,11 +755,18 @@
 	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 	rsn_ie_len = ie ? ie[1] : 0;
 
+	ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+	osen = ie != NULL;
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
-		"wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
+		"wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s",
 		i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
 		wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
-		wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
+		wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
+		(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+		 wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
+		" p2p" : "",
+		osen ? " osen=1" : "");
 
 	e = wpa_blacklist_get(wpa_s, bss->bssid);
 	if (e) {
@@ -761,7 +806,7 @@
 
 	wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
 
-	for (ssid = group; ssid; ssid = ssid->pnext) {
+	for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
 		int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
 		int res;
 
@@ -819,7 +864,7 @@
 		if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
 			continue;
 
-		if (!wpa &&
+		if (!osen && !wpa &&
 		    !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
 		    !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
 		    !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
@@ -834,6 +879,12 @@
 			continue;
 		}
 
+		if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - non-OSEN network "
+				"not allowed");
+			continue;
+		}
+
 		if (!wpa_supplicant_match_privacy(bss, ssid)) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - privacy "
 				"mismatch");
@@ -858,6 +909,39 @@
 		}
 
 #ifdef CONFIG_P2P
+		if (ssid->p2p_group &&
+		    !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+		    !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no P2P IE seen");
+			continue;
+		}
+
+		if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
+			struct wpabuf *p2p_ie;
+			u8 dev_addr[ETH_ALEN];
+
+			ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+			if (ie == NULL) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no P2P element");
+				continue;
+			}
+			p2p_ie = wpa_bss_get_vendor_ie_multi(
+				bss, P2P_IE_VENDOR_TYPE);
+			if (p2p_ie == NULL) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - could not fetch P2P element");
+				continue;
+			}
+
+			if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
+			    || os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
+					 ETH_ALEN) != 0) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no matching GO P2P Device Address in P2P element");
+				wpabuf_free(p2p_ie);
+				continue;
+			}
+			wpabuf_free(p2p_ie);
+		}
+
 		/*
 		 * TODO: skip the AP if its P2P IE has Group Formation
 		 * bit set in the P2P Group Capability Bitmap and we
@@ -877,16 +961,22 @@
 static struct wpa_bss *
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
 			  struct wpa_ssid *group,
-			  struct wpa_ssid **selected_ssid)
+			  struct wpa_ssid **selected_ssid,
+			  int only_first_ssid)
 {
 	unsigned int i;
 
-	wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
-		group->priority);
+	if (only_first_ssid)
+		wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
+			group->id);
+	else
+		wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+			group->priority);
 
 	for (i = 0; i < wpa_s->last_scan_res_used; i++) {
 		struct wpa_bss *bss = wpa_s->last_scan_res[i];
-		*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
+		*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
+						    only_first_ssid);
 		if (!*selected_ssid)
 			continue;
 		wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
@@ -905,16 +995,36 @@
 {
 	struct wpa_bss *selected = NULL;
 	int prio;
+	struct wpa_ssid *next_ssid = NULL;
 
 	if (wpa_s->last_scan_res == NULL ||
 	    wpa_s->last_scan_res_used == 0)
 		return NULL; /* no scan results from last update */
 
+	if (wpa_s->next_ssid) {
+		struct wpa_ssid *ssid;
+
+		/* check that next_ssid is still valid */
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+			if (ssid == wpa_s->next_ssid)
+				break;
+		}
+		next_ssid = ssid;
+		wpa_s->next_ssid = NULL;
+	}
+
 	while (selected == NULL) {
 		for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+			if (next_ssid && next_ssid->priority ==
+			    wpa_s->conf->pssid[prio]->priority) {
+				selected = wpa_supplicant_select_bss(
+					wpa_s, next_ssid, selected_ssid, 1);
+				if (selected)
+					break;
+			}
 			selected = wpa_supplicant_select_bss(
 				wpa_s, wpa_s->conf->pssid[prio],
-				selected_ssid);
+				selected_ssid, 0);
 			if (selected)
 				break;
 		}
@@ -945,9 +1055,6 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request "
 			"since there are no enabled networks");
 		wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
-#ifdef CONFIG_P2P
-		wpa_s->sta_scan_pending = 0;
-#endif /* CONFIG_P2P */
 		return;
 	}
 
@@ -964,8 +1071,12 @@
 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
 			"PBC session overlap");
 #ifdef CONFIG_P2P
-		if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1)
+		if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+		    wpa_s->p2p_in_provisioning) {
+			eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb,
+					       wpa_s, NULL);
 			return -1;
+		}
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS
@@ -1148,7 +1259,8 @@
 					      union wpa_event_data *data,
 					      int own_request)
 {
-	struct wpa_scan_results *scan_res;
+	struct wpa_scan_results *scan_res = NULL;
+	int ret = 0;
 	int ap = 0;
 #ifndef CONFIG_NO_RANDOM_POOL
 	size_t i, num;
@@ -1161,23 +1273,6 @@
 
 	wpa_supplicant_notify_scanning(wpa_s, 0);
 
-#ifdef CONFIG_P2P
-	if (own_request && wpa_s->global->p2p_cb_on_scan_complete &&
-	    !wpa_s->global->p2p_disabled &&
-	    wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending &&
-	    !wpa_s->scan_res_handler) {
-		wpa_s->global->p2p_cb_on_scan_complete = 0;
-		if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
-				"stopped scan processing");
-			wpa_s->sta_scan_pending = 1;
-			wpa_supplicant_req_scan(wpa_s, 5, 0);
-			return -1;
-		}
-	}
-	wpa_s->sta_scan_pending = 0;
-#endif /* CONFIG_P2P */
-
 	scan_res = wpa_supplicant_get_scan_results(wpa_s,
 						   data ? &data->scan_info :
 						   NULL, 1);
@@ -1190,7 +1285,8 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
 			"scanning again");
 		wpa_supplicant_req_new_scan(wpa_s, 1, 0);
-		return -1;
+		ret = -1;
+		goto scan_work_done;
 	}
 
 #ifndef CONFIG_NO_RANDOM_POOL
@@ -1209,16 +1305,16 @@
 	}
 #endif /* CONFIG_NO_RANDOM_POOL */
 
-	if (own_request && wpa_s->scan_res_handler) {
+	if (own_request && wpa_s->scan_res_handler &&
+	    (wpa_s->own_scan_running || !wpa_s->external_scan_running)) {
 		void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 					 struct wpa_scan_results *scan_res);
 
 		scan_res_handler = wpa_s->scan_res_handler;
 		wpa_s->scan_res_handler = NULL;
 		scan_res_handler(wpa_s, scan_res);
-
-		wpa_scan_results_free(scan_res);
-		return -2;
+		ret = -2;
+		goto scan_work_done;
 	}
 
 	if (ap) {
@@ -1227,53 +1323,72 @@
 		if (wpa_s->ap_iface->scan_cb)
 			wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
 #endif /* CONFIG_AP */
-		wpa_scan_results_free(scan_res);
-		return 0;
+		goto scan_work_done;
 	}
 
-	wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
-	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+	wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
+		wpa_s->own_scan_running, wpa_s->external_scan_running);
+	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+			     wpa_s->manual_scan_id);
+		wpa_s->manual_scan_use_id = 0;
+	} else {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+	}
 	wpas_notify_scan_results(wpa_s);
 
 	wpas_notify_scan_done(wpa_s, 1);
 
-	if (sme_proc_obss_scan(wpa_s) > 0) {
+	if (!wpa_s->own_scan_running && wpa_s->external_scan_running) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
 		wpa_scan_results_free(scan_res);
 		return 0;
 	}
 
-	if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
-		wpa_scan_results_free(scan_res);
-		return 0;
-	}
+	if (sme_proc_obss_scan(wpa_s) > 0)
+		goto scan_work_done;
 
-	if (autoscan_notify_scan(wpa_s, scan_res)) {
-		wpa_scan_results_free(scan_res);
-		return 0;
-	}
+	if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
+		goto scan_work_done;
+
+	if (autoscan_notify_scan(wpa_s, scan_res))
+		goto scan_work_done;
 
 	if (wpa_s->disconnected) {
 		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
-		wpa_scan_results_free(scan_res);
-		return 0;
+		goto scan_work_done;
 	}
 
 	if (!wpas_driver_bss_selection(wpa_s) &&
-	    bgscan_notify_scan(wpa_s, scan_res) == 1) {
-		wpa_scan_results_free(scan_res);
-		return 0;
-	}
+	    bgscan_notify_scan(wpa_s, scan_res) == 1)
+		goto scan_work_done;
 
 	wpas_wps_update_ap_info(wpa_s, scan_res);
 
 	wpa_scan_results_free(scan_res);
 
-	return wpas_select_network_from_last_scan(wpa_s, 1);
+	if (wpa_s->scan_work) {
+		struct wpa_radio_work *work = wpa_s->scan_work;
+		wpa_s->scan_work = NULL;
+		radio_work_done(work);
+	}
+
+	return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
+
+scan_work_done:
+	wpa_scan_results_free(scan_res);
+	if (wpa_s->scan_work) {
+		struct wpa_radio_work *work = wpa_s->scan_work;
+		wpa_s->scan_work = NULL;
+		radio_work_done(work);
+	}
+	return ret;
 }
 
 
 static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
-					      int new_scan)
+					      int new_scan, int own_request)
 {
 	struct wpa_bss *selected;
 	struct wpa_ssid *ssid = NULL;
@@ -1308,17 +1423,25 @@
 			wpa_supplicant_associate(wpa_s, NULL, ssid);
 			if (new_scan)
 				wpa_supplicant_rsn_preauth_scan_results(wpa_s);
-		} else {
+		} else if (own_request) {
+			/*
+			 * No SSID found. If SCAN results are as a result of
+			 * own scan request and not due to a scan request on
+			 * another shared interface, try another scan.
+			 */
 			int timeout_sec = wpa_s->scan_interval;
 			int timeout_usec = 0;
 #ifdef CONFIG_P2P
 			if (wpas_p2p_scan_no_go_seen(wpa_s) == 1)
 				return 0;
 
-			if (wpa_s->p2p_in_provisioning) {
+			if (wpa_s->p2p_in_provisioning ||
+			    wpa_s->show_group_started ||
+			    wpa_s->p2p_in_invitation) {
 				/*
 				 * Use shorter wait during P2P Provisioning
-				 * state to speed up group formation.
+				 * state and during P2P join-a-group operation
+				 * to speed up group formation.
 				 */
 				timeout_sec = 0;
 				timeout_usec = 250000;
@@ -1340,6 +1463,16 @@
 				return 1;
 			}
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_WPS
+			if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) {
+				wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing");
+				timeout_sec = 0;
+				timeout_usec = 500000;
+				wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+							    timeout_usec);
+				return 0;
+			}
+#endif /* CONFIG_WPS */
 			if (wpa_supplicant_req_sched_scan(wpa_s))
 				wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
 							    timeout_usec);
@@ -1352,7 +1485,6 @@
 static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 					      union wpa_event_data *data)
 {
-	const char *rn, *rn2;
 	struct wpa_supplicant *ifs;
 
 	if (_wpa_supplicant_event_scan_results(wpa_s, data, 1) != 0) {
@@ -1367,25 +1499,12 @@
 	}
 
 	/*
-	 * Check other interfaces to see if they have the same radio-name. If
+	 * Check other interfaces to see if they share the same radio. If
 	 * so, they get updated with this same scan info.
 	 */
-	if (!wpa_s->driver->get_radio_name)
-		return;
-
-	rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-	if (rn == NULL || rn[0] == '\0')
-		return;
-
-	wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces "
-		"sharing same radio (%s) in event_scan_results", rn);
-
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		if (ifs == wpa_s || !ifs->driver->get_radio_name)
-			continue;
-
-		rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-		if (rn2 && os_strcmp(rn, rn2) == 0) {
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (ifs != wpa_s) {
 			wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
 				   "sibling", ifs->ifname);
 			_wpa_supplicant_event_scan_results(ifs, data, 0);
@@ -1401,18 +1520,18 @@
 #ifdef CONFIG_NO_SCAN_PROCESSING
 	return -1;
 #else /* CONFIG_NO_SCAN_PROCESSING */
-	struct os_time now;
+	struct os_reltime now;
 
 	if (wpa_s->last_scan_res_used <= 0)
 		return -1;
 
-	os_get_time(&now);
-	if (now.sec - wpa_s->last_scan.sec > 5) {
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) {
 		wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
 		return -1;
 	}
 
-	return wpas_select_network_from_last_scan(wpa_s, 0);
+	return wpas_select_network_from_last_scan(wpa_s, 0, 1);
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 }
 
@@ -1496,6 +1615,43 @@
 }
 
 
+#ifdef CONFIG_INTERWORKING
+
+static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
+			    size_t len)
+{
+	int res;
+
+	wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len);
+	res = wpa_drv_set_qos_map(wpa_s, qos_map, len);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver");
+	}
+
+	return res;
+}
+
+
+static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
+					    const u8 *ies, size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+
+	if (ies == NULL)
+		return;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+		return;
+
+	if (elems.qos_map_set) {
+		wpas_qos_map_set(wpa_s, elems.qos_map_set,
+				 elems.qos_map_set_len);
+	}
+}
+
+#endif /* CONFIG_INTERWORKING */
+
+
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 					  union wpa_event_data *data)
 {
@@ -1520,6 +1676,10 @@
 		wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
 				       data->assoc_info.resp_ies_len);
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+		interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+						data->assoc_info.resp_ies_len);
+#endif /* CONFIG_INTERWORKING */
 	}
 	if (data->assoc_info.beacon_ies)
 		wpa_hexdump(MSG_DEBUG, "beacon_ies",
@@ -1688,21 +1848,6 @@
 }
 
 
-static struct wpa_bss * wpa_supplicant_get_new_bss(
-	struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
-	struct wpa_bss *bss = NULL;
-	struct wpa_ssid *ssid = wpa_s->current_ssid;
-
-	if (ssid->ssid_len > 0)
-		bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
-	if (!bss)
-		bss = wpa_bss_get_bssid(wpa_s, bssid);
-
-	return bss;
-}
-
-
 static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
 {
 	const u8 *bss_wpa = NULL, *bss_rsn = NULL;
@@ -1735,6 +1880,8 @@
 
 #ifdef CONFIG_AP
 	if (wpa_s->ap_iface) {
+		if (!data)
+			return;
 		hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
 				    data->assoc_info.addr,
 				    data->assoc_info.req_ies,
@@ -1772,20 +1919,6 @@
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 			return;
 		}
-		if (wpa_s->current_ssid) {
-			struct wpa_bss *bss = NULL;
-
-			bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
-			if (!bss) {
-				wpa_supplicant_update_scan_results(wpa_s);
-
-				/* Get the BSS from the new scan results */
-				bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
-			}
-
-			if (bss)
-				wpa_s->current_bss = bss;
-		}
 
 #ifdef ANDROID
 		if (wpa_s->conf->ap_scan == 1) {
@@ -1802,6 +1935,7 @@
 #ifdef CONFIG_SME
 	os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
 	wpa_s->sme.prev_bssid_set = 1;
+	wpa_s->sme.last_unprot_disconnect.sec = 0;
 #endif /* CONFIG_SME */
 
 	wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
@@ -1837,7 +1971,8 @@
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
 	    (wpa_s->current_ssid &&
 	     wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
-		if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
+		if (wpa_s->current_ssid &&
+		    wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
 		    (wpa_s->drv_flags &
 		     WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
 			/*
@@ -1887,9 +2022,9 @@
 	wpa_s->last_eapol_matches_bssid = 0;
 
 	if (wpa_s->pending_eapol_rx) {
-		struct os_time now, age;
-		os_get_time(&now);
-		os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
+		struct os_reltime now, age;
+		os_get_reltime(&now);
+		os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
 		if (age.sec == 0 && age.usec < 100000 &&
 		    os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
 		    0) {
@@ -2002,9 +2137,7 @@
 	int authenticating;
 	u8 prev_pending_bssid[ETH_ALEN];
 	struct wpa_bss *fast_reconnect = NULL;
-#ifndef CONFIG_NO_SCAN_PROCESSING
 	struct wpa_ssid *fast_reconnect_ssid = NULL;
-#endif /* CONFIG_NO_SCAN_PROCESSING */
 	struct wpa_ssid *last_ssid;
 
 	authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
@@ -2026,7 +2159,7 @@
 			"pre-shared key may be incorrect");
 		if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
 			return; /* P2P group removed */
-		wpas_auth_failed(wpa_s);
+		wpas_auth_failed(wpa_s, "WRONG_KEY");
 	}
 	if (!wpa_s->disconnected &&
 	    (!wpa_s->auto_reconnect_disabled ||
@@ -2047,15 +2180,9 @@
 			 * time for some common cases.
 			 */
 			fast_reconnect = wpa_s->current_bss;
-#ifndef CONFIG_NO_SCAN_PROCESSING
 			fast_reconnect_ssid = wpa_s->current_ssid;
-#endif /* CONFIG_NO_SCAN_PROCESSING */
 		} else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
-#ifdef ANDROID
-			wpa_supplicant_req_scan(wpa_s, 0, 500000);
-#else
 			wpa_supplicant_req_scan(wpa_s, 0, 100000);
-#endif
 		else
 			wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
 				"immediate scan");
@@ -2079,7 +2206,6 @@
 	wpas_notify_disconnect_reason(wpa_s);
 	if (wpa_supplicant_dynamic_keys(wpa_s)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
-		wpa_s->keys_cleared = 0;
 		wpa_clear_keys(wpa_s, wpa_s->bssid);
 	}
 	last_ssid = wpa_s->current_ssid;
@@ -2090,7 +2216,12 @@
 		wpa_s->current_ssid = last_ssid;
 	}
 
-	if (fast_reconnect) {
+	if (fast_reconnect &&
+	    !wpas_network_disabled(wpa_s, fast_reconnect_ssid) &&
+	    !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
+	    !disallowed_ssid(wpa_s, fast_reconnect->ssid,
+			     fast_reconnect->ssid_len) &&
+	    !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
 #ifndef CONFIG_NO_SCAN_PROCESSING
 		wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
 		if (wpa_supplicant_connect(wpa_s, fast_reconnect,
@@ -2099,6 +2230,14 @@
 			wpa_supplicant_req_scan(wpa_s, 0, 100000);
 		}
 #endif /* CONFIG_NO_SCAN_PROCESSING */
+	} else if (fast_reconnect) {
+		/*
+		 * Could not reconnect to the same BSS due to network being
+		 * disabled. Use a new scan to match the alternative behavior
+		 * above, i.e., to continue automatic reconnection attempt in a
+		 * way that enforces disabled network rules.
+		 */
+		wpa_supplicant_req_scan(wpa_s, 0, 100000);
 	}
 }
 
@@ -2123,13 +2262,13 @@
 					 union wpa_event_data *data)
 {
 	int pairwise;
-	struct os_time t;
+	struct os_reltime t;
 
 	wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
 	pairwise = (data && data->michael_mic_failure.unicast);
-	os_get_time(&t);
-	if ((wpa_s->last_michael_mic_error &&
-	     t.sec - wpa_s->last_michael_mic_error <= 60) ||
+	os_get_reltime(&t);
+	if ((wpa_s->last_michael_mic_error.sec &&
+	     !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) ||
 	    wpa_s->pending_mic_error_report) {
 		if (wpa_s->pending_mic_error_report) {
 			/*
@@ -2207,7 +2346,7 @@
 		wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
 #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
 	}
-	wpa_s->last_michael_mic_error = t.sec;
+	wpa_s->last_michael_mic_error = t;
 	wpa_s->mic_errors_seen++;
 }
 
@@ -2242,7 +2381,6 @@
 			wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
 				"driver after interface was added");
 		}
-		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 		break;
 	case EVENT_INTERFACE_REMOVED:
 		wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
@@ -2251,10 +2389,6 @@
 		wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
 		l2_packet_deinit(wpa_s->l2);
 		wpa_s->l2 = NULL;
-#ifdef CONFIG_IBSS_RSN
-		ibss_rsn_deinit(wpa_s->ibss_rsn);
-		wpa_s->ibss_rsn = NULL;
-#endif /* CONFIG_IBSS_RSN */
 #ifdef CONFIG_TERMINATE_ONLASTIF
 		/* check if last interface */
 		if (!any_interfaces(wpa_s->global->ifaces))
@@ -2478,11 +2612,12 @@
 
 	wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated);
 
-	if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
-	    ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
-	      (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
-	     eapol_sm_failed(wpa_s->eapol)))
-		wpas_auth_failed(wpa_s);
+	if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
+	      ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+		(wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
+	       eapol_sm_failed(wpa_s->eapol))) &&
+	     !wpa_s->eap_expected_failure))
+		wpas_auth_failed(wpa_s, "AUTH_FAILED");
 
 #ifdef CONFIG_P2P
 	if (deauth && reason_code > 0) {
@@ -2591,6 +2726,212 @@
 }
 
 
+static const char * reg_init_str(enum reg_change_initiator init)
+{
+	switch (init) {
+	case REGDOM_SET_BY_CORE:
+		return "CORE";
+	case REGDOM_SET_BY_USER:
+		return "USER";
+	case REGDOM_SET_BY_DRIVER:
+		return "DRIVER";
+	case REGDOM_SET_BY_COUNTRY_IE:
+		return "COUNTRY_IE";
+	case REGDOM_BEACON_HINT:
+		return "BEACON_HINT";
+	}
+	return "?";
+}
+
+
+static const char * reg_type_str(enum reg_type type)
+{
+	switch (type) {
+	case REGDOM_TYPE_UNKNOWN:
+		return "UNKNOWN";
+	case REGDOM_TYPE_COUNTRY:
+		return "COUNTRY";
+	case REGDOM_TYPE_WORLD:
+		return "WORLD";
+	case REGDOM_TYPE_CUSTOM_WORLD:
+		return "CUSTOM_WORLD";
+	case REGDOM_TYPE_INTERSECTION:
+		return "INTERSECTION";
+	}
+	return "?";
+}
+
+
+static void wpa_supplicant_update_channel_list(
+	struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
+{
+	struct wpa_supplicant *ifs;
+
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
+		reg_init_str(info->initiator), reg_type_str(info->type),
+		info->alpha2[0] ? " alpha2=" : "",
+		info->alpha2[0] ? info->alpha2 : "");
+
+	if (wpa_s->drv_priv == NULL)
+		return; /* Ignore event during drv initialization */
+
+	free_hw_features(wpa_s);
+	wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+		wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
+	wpas_p2p_update_channel_list(wpa_s);
+
+	/*
+	 * Check other interfaces to see if they share the same radio. If
+	 * so, they get updated with this same hw mode info.
+	 */
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (ifs != wpa_s) {
+			wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
+				   ifs->ifname);
+			free_hw_features(ifs);
+			ifs->hw.modes = wpa_drv_get_hw_feature_data(
+				ifs, &ifs->hw.num_modes, &ifs->hw.flags);
+		}
+	}
+}
+
+
+static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
+				      const u8 *frame, size_t len, int freq)
+{
+	const struct ieee80211_mgmt *mgmt;
+	const u8 *payload;
+	size_t plen;
+	u8 category;
+
+	if (len < IEEE80211_HDRLEN + 2)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	payload = frame + IEEE80211_HDRLEN;
+	category = *payload++;
+	plen = len - IEEE80211_HDRLEN - 1;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
+		" Category=%u DataLen=%d freq=%d MHz",
+		MAC2STR(mgmt->sa), category, (int) plen, freq);
+
+#ifdef CONFIG_IEEE80211R
+	if (category == WLAN_ACTION_FT) {
+		ft_rx_action(wpa_s, payload, plen);
+		return;
+	}
+#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) {
+		ieee802_11_rx_wnm_action(wpa_s, mgmt, len);
+		return;
+	}
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_GAS
+	if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
+	     mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
+	    gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid,
+			 mgmt->u.action.category,
+			 payload, plen, freq) == 0)
+		return;
+#endif /* CONFIG_GAS */
+
+#ifdef CONFIG_TDLS
+	if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
+	    payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"TDLS: Received Discovery Response from " MACSTR,
+			MAC2STR(mgmt->sa));
+		return;
+	}
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_INTERWORKING
+	if (category == WLAN_ACTION_QOS && plen >= 1 &&
+	    payload[0] == QOS_QOS_MAP_CONFIG) {
+		const u8 *pos = payload + 1;
+		size_t qlen = plen - 1;
+		wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from "
+			MACSTR, MAC2STR(mgmt->sa));
+		if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 &&
+		    qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET &&
+		    pos[1] <= qlen - 2 && pos[1] >= 16)
+			wpas_qos_map_set(wpa_s, pos + 2, pos[1]);
+		return;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+			   category, payload, plen, freq);
+}
+
+
+static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *event)
+{
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *ifs;
+#endif /* CONFIG_P2P */
+	struct wpa_freq_range_list *list;
+	char *str = NULL;
+
+	list = &event->freq_range;
+
+	if (list->num)
+		str = freq_range_list_str(list);
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s",
+		str ? str : "");
+
+#ifdef CONFIG_P2P
+	if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) {
+		wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range",
+			__func__);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
+		wpas_p2p_update_channel_list(wpa_s);
+	}
+
+	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+		int freq;
+		if (!ifs->current_ssid ||
+		    !ifs->current_ssid->p2p_group ||
+		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+			continue;
+
+		freq = ifs->current_ssid->frequency;
+		if (!freq_range_list_includes(list, freq)) {
+			wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
+				freq);
+			continue;
+		}
+
+		wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
+			freq);
+		/* TODO: Consider using CSA or removing the group within
+		 * wpa_supplicant */
+		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+	}
+#endif /* CONFIG_P2P */
+
+	os_free(str);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -2644,11 +2985,44 @@
 		wpa_supplicant_event_michael_mic_failure(wpa_s, data);
 		break;
 #ifndef CONFIG_NO_SCAN_PROCESSING
+	case EVENT_SCAN_STARTED:
+		os_get_reltime(&wpa_s->scan_start_time);
+		if (wpa_s->own_scan_requested) {
+			struct os_reltime diff;
+
+			os_reltime_sub(&wpa_s->scan_start_time,
+				       &wpa_s->scan_trigger_time, &diff);
+			wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
+				diff.sec, diff.usec);
+			wpa_s->own_scan_requested = 0;
+			wpa_s->own_scan_running = 1;
+			if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+			    wpa_s->manual_scan_use_id) {
+				wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED "id=%u",
+					wpa_s->manual_scan_id);
+			} else {
+				wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
+			}
+		} else {
+			wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan");
+			wpa_s->external_scan_running = 1;
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
+		}
+		break;
 	case EVENT_SCAN_RESULTS:
+		if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+			struct os_reltime now, diff;
+			os_get_reltime(&now);
+			os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
+			wpa_s->scan_start_time.sec = 0;
+			wpa_s->scan_start_time.usec = 0;
+			wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
+				diff.sec, diff.usec);
+		}
 		wpa_supplicant_event_scan_results(wpa_s, data);
-		if (wpa_s->wpa_state != WPA_AUTHENTICATING &&
-		    wpa_s->wpa_state != WPA_ASSOCIATING)
-			wpas_p2p_continue_after_scan(wpa_s);
+		wpa_s->own_scan_running = 0;
+		wpa_s->external_scan_running = 0;
+		radio_work_check_next(wpa_s);
 		break;
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 	case EVENT_ASSOCINFO:
@@ -2698,52 +3072,11 @@
 		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
 			sme_event_assoc_reject(wpa_s, data);
 		else {
-#ifdef ANDROID_P2P
-			if(!wpa_s->current_ssid) {
-				wpa_printf(MSG_ERROR, "current_ssid == NULL");
-				break;
-			}
-			/* If assoc reject is reported by the driver, then avoid
-			 * waiting for  the authentication timeout. Cancel the
-			 * authentication timeout and retry the assoc.
-			 */
-			if(wpa_s->current_ssid->assoc_retry++ < 10) {
-				wpa_printf(MSG_ERROR, "Retrying assoc: %d ",
-								wpa_s->current_ssid->assoc_retry);
-
-				wpa_supplicant_cancel_auth_timeout(wpa_s);
-
-				/* Clear the states */
-				wpa_sm_notify_disassoc(wpa_s->wpa);
-				wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
-
-				wpa_s->reassociate = 1;
-				if (wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE) {
-					const u8 *bl_bssid = data->assoc_reject.bssid;
-					if (!bl_bssid || is_zero_ether_addr(bl_bssid))
-						bl_bssid = wpa_s->pending_bssid;
-					wpa_blacklist_add(wpa_s, bl_bssid);
-					wpa_supplicant_req_scan(wpa_s, 0, 0);
-				} else {
-					wpa_supplicant_req_scan(wpa_s, 1, 0);
-				}
-			} else if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
-				/* If we ASSOC_REJECT's hits threshold, disable the 
-			 	 * network
-			 	 */
-				wpa_printf(MSG_ERROR, "Assoc retry threshold reached. "
-				"Disabling the network");
-				wpa_s->current_ssid->assoc_retry = 0;
-				wpa_supplicant_disable_network(wpa_s, wpa_s->current_ssid);
-				wpas_p2p_group_remove(wpa_s, wpa_s->ifname);
-			}
-#else
 			const u8 *bssid = data->assoc_reject.bssid;
 			if (bssid == NULL || is_zero_ether_addr(bssid))
 				bssid = wpa_s->pending_bssid;
 			wpas_connection_failed(wpa_s, bssid);
 			wpa_supplicant_mark_disassoc(wpa_s);
-#endif /* ANDROID_P2P */
 		}
 		break;
 	case EVENT_AUTH_TIMED_OUT:
@@ -2840,14 +3173,33 @@
 
 		wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
 				  data->ch_switch.ht_enabled,
-				  data->ch_switch.ch_offset);
+				  data->ch_switch.ch_offset,
+				  data->ch_switch.ch_width,
+				  data->ch_switch.cf1,
+				  data->ch_switch.cf2);
 		break;
 #endif /* CONFIG_AP */
-#if defined(CONFIG_AP) || defined(CONFIG_IBSS_RSN)
 	case EVENT_RX_MGMT: {
 		u16 fc, stype;
 		const struct ieee80211_mgmt *mgmt;
 
+#ifdef CONFIG_TESTING_OPTIONS
+		if (wpa_s->ext_mgmt_frame_handling) {
+			struct rx_mgmt *rx = &data->rx_mgmt;
+			size_t hex_len = 2 * rx->frame_len + 1;
+			char *hex = os_malloc(hex_len);
+			if (hex) {
+				wpa_snprintf_hex(hex, hex_len,
+						 rx->frame, rx->frame_len);
+				wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s",
+					rx->freq, rx->datarate, rx->ssi_signal,
+					hex);
+				os_free(hex);
+			}
+			break;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 		mgmt = (const struct ieee80211_mgmt *)
 			data->rx_mgmt.frame;
 		fc = le_to_host16(mgmt->frame_control);
@@ -2878,6 +3230,15 @@
 				break;
 			}
 #endif /* CONFIG_IBSS_RSN */
+
+			if (stype == WLAN_FC_STYPE_ACTION) {
+				wpas_event_rx_mgmt_action(
+					wpa_s, data->rx_mgmt.frame,
+					data->rx_mgmt.frame_len,
+					data->rx_mgmt.freq);
+				break;
+			}
+
 			wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
 				"management frame in non-AP mode");
 			break;
@@ -2900,63 +3261,6 @@
 #endif /* CONFIG_AP */
 		break;
 		}
-#endif /* CONFIG_AP || CONFIG_IBSS_RSN */
-	case EVENT_RX_ACTION:
-		wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
-			" Category=%u DataLen=%d freq=%d MHz",
-			MAC2STR(data->rx_action.sa),
-			data->rx_action.category, (int) data->rx_action.len,
-			data->rx_action.freq);
-#ifdef CONFIG_IEEE80211R
-		if (data->rx_action.category == WLAN_ACTION_FT) {
-			ft_rx_action(wpa_s, data->rx_action.data,
-				     data->rx_action.len);
-			break;
-		}
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
-#ifdef CONFIG_SME
-		if (data->rx_action.category == WLAN_ACTION_SA_QUERY) {
-			sme_sa_query_rx(wpa_s, data->rx_action.sa,
-					data->rx_action.data,
-					data->rx_action.len);
-			break;
-		}
-#endif /* CONFIG_SME */
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
-		if (data->rx_action.category == WLAN_ACTION_WNM) {
-			ieee802_11_rx_wnm_action(wpa_s, &data->rx_action);
-			break;
-		}
-#endif /* CONFIG_WNM */
-#ifdef CONFIG_GAS
-		if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
-		    gas_query_rx(wpa_s->gas, data->rx_action.da,
-				 data->rx_action.sa, data->rx_action.bssid,
-				 data->rx_action.data, data->rx_action.len,
-				 data->rx_action.freq) == 0)
-			break;
-#endif /* CONFIG_GAS */
-#ifdef CONFIG_TDLS
-		if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
-		    data->rx_action.len >= 4 &&
-		    data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery "
-				"Response from " MACSTR,
-				MAC2STR(data->rx_action.sa));
-			break;
-		}
-#endif /* CONFIG_TDLS */
-#ifdef CONFIG_P2P
-		wpas_p2p_rx_action(wpa_s, data->rx_action.da,
-				   data->rx_action.sa,
-				   data->rx_action.bssid,
-				   data->rx_action.category,
-				   data->rx_action.data,
-				   data->rx_action.len, data->rx_action.freq);
-#endif /* CONFIG_P2P */
-		break;
 	case EVENT_RX_PROBE_REQ:
 		if (data->rx_probe_req.sa == NULL ||
 		    data->rx_probe_req.ie == NULL)
@@ -2973,14 +3277,12 @@
 			break;
 		}
 #endif /* CONFIG_AP */
-#ifdef CONFIG_P2P
 		wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa,
 				      data->rx_probe_req.da,
 				      data->rx_probe_req.bssid,
 				      data->rx_probe_req.ie,
 				      data->rx_probe_req.ie_len,
 				      data->rx_probe_req.ssi_signal);
-#endif /* CONFIG_P2P */
 		break;
 	case EVENT_REMAIN_ON_CHANNEL:
 #ifdef CONFIG_OFFCHANNEL
@@ -2988,93 +3290,30 @@
 			wpa_s, data->remain_on_channel.freq,
 			data->remain_on_channel.duration);
 #endif /* CONFIG_OFFCHANNEL */
-#ifdef CONFIG_P2P
 		wpas_p2p_remain_on_channel_cb(
 			wpa_s, data->remain_on_channel.freq,
 			data->remain_on_channel.duration);
-#endif /* CONFIG_P2P */
 		break;
 	case EVENT_CANCEL_REMAIN_ON_CHANNEL:
 #ifdef CONFIG_OFFCHANNEL
 		offchannel_cancel_remain_on_channel_cb(
 			wpa_s, data->remain_on_channel.freq);
 #endif /* CONFIG_OFFCHANNEL */
-#ifdef CONFIG_P2P
 		wpas_p2p_cancel_remain_on_channel_cb(
 			wpa_s, data->remain_on_channel.freq);
-#endif /* CONFIG_P2P */
 		break;
-#ifdef CONFIG_P2P
-	case EVENT_P2P_DEV_FOUND: {
-		struct p2p_peer_info peer_info;
-
-		os_memset(&peer_info, 0, sizeof(peer_info));
-		if (data->p2p_dev_found.dev_addr)
-			os_memcpy(peer_info.p2p_device_addr,
-				  data->p2p_dev_found.dev_addr, ETH_ALEN);
-		if (data->p2p_dev_found.pri_dev_type)
-			os_memcpy(peer_info.pri_dev_type,
-				  data->p2p_dev_found.pri_dev_type,
-				  sizeof(peer_info.pri_dev_type));
-		if (data->p2p_dev_found.dev_name)
-			os_strlcpy(peer_info.device_name,
-				   data->p2p_dev_found.dev_name,
-				   sizeof(peer_info.device_name));
-		peer_info.config_methods = data->p2p_dev_found.config_methods;
-		peer_info.dev_capab = data->p2p_dev_found.dev_capab;
-		peer_info.group_capab = data->p2p_dev_found.group_capab;
-
-		/*
-		 * FIX: new_device=1 is not necessarily correct. We should
-		 * maintain a P2P peer database in wpa_supplicant and update
-		 * this information based on whether the peer is truly new.
-		 */
-		wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1);
-		break;
-		}
-	case EVENT_P2P_GO_NEG_REQ_RX:
-		wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src,
-				   data->p2p_go_neg_req_rx.dev_passwd_id);
-		break;
-	case EVENT_P2P_GO_NEG_COMPLETED:
-		wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res);
-		break;
-	case EVENT_P2P_PROV_DISC_REQUEST:
-		wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer,
-				   data->p2p_prov_disc_req.config_methods,
-				   data->p2p_prov_disc_req.dev_addr,
-				   data->p2p_prov_disc_req.pri_dev_type,
-				   data->p2p_prov_disc_req.dev_name,
-				   data->p2p_prov_disc_req.supp_config_methods,
-				   data->p2p_prov_disc_req.dev_capab,
-				   data->p2p_prov_disc_req.group_capab,
-				   NULL, 0);
-		break;
-	case EVENT_P2P_PROV_DISC_RESPONSE:
-		wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer,
-				    data->p2p_prov_disc_resp.config_methods);
-		break;
-	case EVENT_P2P_SD_REQUEST:
-		wpas_sd_request(wpa_s, data->p2p_sd_req.freq,
-				data->p2p_sd_req.sa,
-				data->p2p_sd_req.dialog_token,
-				data->p2p_sd_req.update_indic,
-				data->p2p_sd_req.tlvs,
-				data->p2p_sd_req.tlvs_len);
-		break;
-	case EVENT_P2P_SD_RESPONSE:
-		wpas_sd_response(wpa_s, data->p2p_sd_resp.sa,
-				 data->p2p_sd_resp.update_indic,
-				 data->p2p_sd_resp.tlvs,
-				 data->p2p_sd_resp.tlvs_len);
-		break;
-#endif /* CONFIG_P2P */
 	case EVENT_EAPOL_RX:
 		wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
 					data->eapol_rx.data,
 					data->eapol_rx.data_len);
 		break;
 	case EVENT_SIGNAL_CHANGE:
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
+			"above=%d signal=%d noise=%d txrate=%d",
+			data->signal_change.above_threshold,
+			data->signal_change.current_signal,
+			data->signal_change.current_noise,
+			data->signal_change.current_txrate);
 		bgscan_notify_signal_change(
 			wpa_s, data->signal_change.above_threshold,
 			data->signal_change.current_signal,
@@ -3085,6 +3324,12 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
 			wpa_supplicant_update_mac_addr(wpa_s);
+			if (wpa_s->p2p_mgmt) {
+				wpa_supplicant_set_state(wpa_s,
+							 WPA_DISCONNECTED);
+				break;
+			}
+
 #ifdef CONFIG_AP
 			if (!wpa_s->ap_iface) {
 				wpa_supplicant_set_state(wpa_s,
@@ -3101,25 +3346,46 @@
 		break;
 	case EVENT_INTERFACE_DISABLED:
 		wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled");
+#ifdef CONFIG_P2P
+		if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
+		    (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group &&
+		     wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) {
+			/*
+			 * The interface was externally disabled. Remove
+			 * it assuming an external entity will start a
+			 * new session if needed.
+			 */
+			wpas_p2p_disconnect(wpa_s);
+			break;
+		}
+		if (wpa_s->p2p_scan_work && wpa_s->global->p2p &&
+		    p2p_in_progress(wpa_s->global->p2p) > 1) {
+			/* This radio work will be cancelled, so clear P2P
+			 * state as well.
+			 */
+			p2p_stop_find(wpa_s->global->p2p);
+		}
+#endif /* CONFIG_P2P */
+
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+			/*
+			 * Indicate disconnection to keep ctrl_iface events
+			 * consistent.
+			 */
+			wpa_supplicant_event_disassoc(
+				wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1);
+		}
 		wpa_supplicant_mark_disassoc(wpa_s);
+		radio_remove_works(wpa_s, NULL, 0);
+
 		wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
 		break;
 	case EVENT_CHANNEL_LIST_CHANGED:
-		if (wpa_s->drv_priv == NULL)
-			break; /* Ignore event during drv initialization */
-
-		free_hw_features(wpa_s);
-		wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
-			wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
-
-#ifdef CONFIG_P2P
-		wpas_p2p_update_channel_list(wpa_s);
-#endif /* CONFIG_P2P */
+		wpa_supplicant_update_channel_list(
+			wpa_s, &data->channel_list_changed);
 		break;
 	case EVENT_INTERFACE_UNAVAILABLE:
-#ifdef CONFIG_P2P
 		wpas_p2p_interface_unavailable(wpa_s);
-#endif /* CONFIG_P2P */
 		break;
 	case EVENT_BEST_CHANNEL:
 		wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
@@ -3129,11 +3395,9 @@
 		wpa_s->best_24_freq = data->best_chan.freq_24;
 		wpa_s->best_5_freq = data->best_chan.freq_5;
 		wpa_s->best_overall_freq = data->best_chan.freq_overall;
-#ifdef CONFIG_P2P
 		wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
 					      data->best_chan.freq_5,
 					      data->best_chan.freq_overall);
-#endif /* CONFIG_P2P */
 		break;
 	case EVENT_UNPROT_DEAUTH:
 		wpa_supplicant_event_unprot_deauth(wpa_s,
@@ -3151,7 +3415,8 @@
 #endif /* CONFIG_AP */
 #ifdef CONFIG_TDLS
 		if (data)
-			wpa_tdls_disable_link(wpa_s->wpa, data->low_ack.addr);
+			wpa_tdls_disable_unreachable_link(wpa_s->wpa,
+							  data->low_ack.addr);
 #endif /* CONFIG_TDLS */
 		break;
 	case EVENT_IBSS_PEER_LOST:
@@ -3169,6 +3434,7 @@
 					 data->driver_gtk_rekey.replay_ctr);
 		break;
 	case EVENT_SCHED_SCAN_STOPPED:
+		wpa_s->pno = 0;
 		wpa_s->sched_scanning = 0;
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 
@@ -3176,17 +3442,25 @@
 			break;
 
 		/*
-		 * If we timed out, start a new sched scan to continue
-		 * searching for more SSIDs.
+		 * Start a new sched scan to continue searching for more SSIDs
+		 * either if timed out or PNO schedule scan is pending.
 		 */
-		if (wpa_s->sched_scan_timed_out)
+		if (wpa_s->sched_scan_timed_out) {
 			wpa_supplicant_req_sched_scan(wpa_s);
+		} else if (wpa_s->pno_sched_pending) {
+			wpa_s->pno_sched_pending = 0;
+			wpas_start_pno(wpa_s);
+		}
+
 		break;
 	case EVENT_WPS_BUTTON_PUSHED:
 #ifdef CONFIG_WPS
 		wpas_wps_start_pbc(wpa_s, NULL, 0);
 #endif /* CONFIG_WPS */
 		break;
+	case EVENT_AVOID_FREQUENCIES:
+		wpa_supplicant_notify_avoid_freq(wpa_s, data);
+		break;
 	case EVENT_CONNECT_FAILED_REASON:
 #ifdef CONFIG_AP
 		if (!wpa_s->ap_iface || !data)
diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh
index 8759f54..797d43a 100755
--- a/wpa_supplicant/examples/p2p-action.sh
+++ b/wpa_supplicant/examples/p2p-action.sh
@@ -34,13 +34,26 @@
 	    # start with -z to avoid that
 	    dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
 		-i $GIFNAME \
-		-F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z
+		-F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z -p 0
 	fi
     fi
     if [ "$4" = "client" ]; then
 	kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
 	rm /var/run/dhclient.leases-$GIFNAME
 	kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+	ipaddr=`echo "$*" | sed 's/.* ip_addr=\([^ ]*\).*/\1/'`
+	ipmask=`echo "$*" | sed 's/.* ip_mask=\([^ ]*\).*/\1/'`
+	goipaddr=`echo "$*" | sed 's/.* go_ip_addr=\([^ ]*\).*/\1/'`
+	if echo "$ipaddr$ipmask$goipaddr" | grep -q ' '; then
+	    ipaddr=""
+	    ipmask=""
+	    goipaddr=""
+	fi
+	if [ -n "$ipaddr" ]; then
+	    sudo ifconfig $GIFNAME "$ipaddr" netmask "$ipmask"
+	    sudo ip ro re default via "$goipaddr"
+	    exit 0
+	fi
 	dhclient -pf /var/run/dhclient-$GIFNAME.pid \
 	    -lf /var/run/dhclient.leases-$GIFNAME \
 	    -nw \
diff --git a/wpa_supplicant/examples/p2p-nfc.py b/wpa_supplicant/examples/p2p-nfc.py
new file mode 100644
index 0000000..91eba28
--- /dev/null
+++ b/wpa_supplicant/examples/p2p-nfc.py
@@ -0,0 +1,654 @@
+#!/usr/bin/python
+#
+# Example nfcpy to wpa_supplicant wrapper for P2P NFC operations
+# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import time
+import random
+import threading
+import argparse
+
+import nfc
+import nfc.ndef
+import nfc.llcp
+import nfc.handover
+
+import logging
+
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+ifname = None
+init_on_touch = False
+in_raw_mode = False
+prev_tcgetattr = 0
+include_wps_req = True
+include_p2p_req = True
+no_input = False
+srv = None
+continue_loop = True
+terminate_now = False
+summary_file = None
+success_file = None
+
+def summary(txt):
+    print txt
+    if summary_file:
+        with open(summary_file, 'a') as f:
+            f.write(txt + "\n")
+
+def success_report(txt):
+    summary(txt)
+    if success_file:
+        with open(success_file, 'a') as f:
+            f.write(txt + "\n")
+
+def wpas_connect():
+    ifaces = []
+    if os.path.isdir(wpas_ctrl):
+        try:
+            ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+        except OSError, error:
+            print "Could not find wpa_supplicant: ", error
+            return None
+
+    if len(ifaces) < 1:
+        print "No wpa_supplicant control interface found"
+        return None
+
+    for ctrl in ifaces:
+        if ifname:
+            if ifname not in ctrl:
+                continue
+        try:
+            print "Trying to use control interface " + ctrl
+            wpas = wpaspy.Ctrl(ctrl)
+            return wpas
+        except Exception, e:
+            pass
+    return None
+
+
+def wpas_tag_read(message):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return False
+    cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex")
+    global force_freq
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    if "FAIL" in wpas.request(cmd):
+        return False
+    return True
+
+
+def wpas_get_handover_req():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in res:
+        return None
+    return res.decode("hex")
+
+def wpas_get_handover_req_wps():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in res:
+        return None
+    return res.decode("hex")
+
+
+def wpas_get_handover_sel(tag=False):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    if tag:
+        res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    else:
+	res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in res:
+        return None
+    return res.decode("hex")
+
+
+def wpas_get_handover_sel_wps():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR");
+    if "FAIL" in res:
+        return None
+    return res.rstrip().decode("hex")
+
+
+def wpas_report_handover(req, sel, type):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex")
+    global force_freq
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    return wpas.request(cmd)
+
+
+def wpas_report_handover_wsc(req, sel, type):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex")
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    return wpas.request(cmd)
+
+
+def p2p_handover_client(llc):
+    message = nfc.ndef.HandoverRequestMessage(version="1.2")
+    message.nonce = random.randint(0, 0xffff)
+
+    global include_p2p_req
+    if include_p2p_req:
+        data = wpas_get_handover_req()
+        if (data == None):
+            summary("Could not get handover request carrier record from wpa_supplicant")
+            return
+        print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
+        datamsg = nfc.ndef.Message(data)
+        message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+    global include_wps_req
+    if include_wps_req:
+        print "Handover request (pre-WPS):"
+        try:
+            print message.pretty()
+        except Exception, e:
+            print e
+
+        data = wpas_get_handover_req_wps()
+        if data:
+            print "Add WPS request in addition to P2P"
+            datamsg = nfc.ndef.Message(data)
+            message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+    print "Handover request:"
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
+
+    client = nfc.handover.HandoverClient(llc)
+    try:
+        summary("Trying to initiate NFC connection handover")
+        client.connect()
+        summary("Connected for handover")
+    except nfc.llcp.ConnectRefused:
+        summary("Handover connection refused")
+        client.close()
+        return
+    except Exception, e:
+        summary("Other exception: " + str(e))
+        client.close()
+        return
+
+    summary("Sending handover request")
+
+    if not client.send(message):
+        summary("Failed to send handover request")
+        client.close()
+        return
+
+    summary("Receiving handover response")
+    message = client._recv()
+    if message is None:
+        summary("No response received")
+        client.close()
+        return
+    if message.type != "urn:nfc:wkt:Hs":
+        summary("Response was not Hs - received: " + message.type)
+        client.close()
+        return
+
+    print "Received message"
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
+    message = nfc.ndef.HandoverSelectMessage(message)
+    summary("Handover select received")
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+
+    for carrier in message.carriers:
+        print "Remote carrier type: " + carrier.type
+        if carrier.type == "application/vnd.wfa.p2p":
+            print "P2P carrier type match - send to wpa_supplicant"
+            if "OK" in wpas_report_handover(data, carrier.record, "INIT"):
+                success_report("P2P handover reported successfully (initiator)")
+            else:
+                summary("P2P handover report rejected")
+            break
+
+    print "Remove peer"
+    client.close()
+    print "Done with handover"
+    global only_one
+    if only_one:
+        print "only_one -> stop loop"
+        global continue_loop
+        continue_loop = False
+
+    global no_wait
+    if no_wait:
+        print "Trying to exit.."
+        global terminate_now
+        terminate_now = True
+
+
+class HandoverServer(nfc.handover.HandoverServer):
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.sent_carrier = None
+        self.ho_server_processing = False
+        self.success = False
+
+    # override to avoid parser error in request/response.pretty() in nfcpy
+    # due to new WSC handover format
+    def _process_request(self, request):
+        summary("received handover request {}".format(request.type))
+        response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+        if not request.type == 'urn:nfc:wkt:Hr':
+            summary("not a handover request")
+        else:
+            try:
+                request = nfc.ndef.HandoverRequestMessage(request)
+            except nfc.ndef.DecodeError as e:
+                summary("error decoding 'Hr' message: {}".format(e))
+            else:
+                response = self.process_request(request)
+        summary("send handover response {}".format(response.type))
+        return response
+
+    def process_request(self, request):
+        self.ho_server_processing = True
+        clear_raw_mode()
+        print "HandoverServer - request received"
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
+
+        sel = nfc.ndef.HandoverSelectMessage(version="1.2")
+
+        found = False
+
+        for carrier in request.carriers:
+            print "Remote carrier type: " + carrier.type
+            if carrier.type == "application/vnd.wfa.p2p":
+                print "P2P carrier type match - add P2P carrier record"
+                found = True
+                self.received_carrier = carrier.record
+                print "Carrier record:"
+                try:
+                    print carrier.record.pretty()
+                except Exception, e:
+                    print e
+                data = wpas_get_handover_sel()
+                if data is None:
+                    print "Could not get handover select carrier record from wpa_supplicant"
+                    continue
+                print "Handover select carrier record from wpa_supplicant:"
+                print data.encode("hex")
+                self.sent_carrier = data
+                if "OK" in wpas_report_handover(self.received_carrier, self.sent_carrier, "RESP"):
+                    success_report("P2P handover reported successfully (responder)")
+                else:
+                    summary("P2P handover report rejected")
+                    break
+
+                message = nfc.ndef.Message(data);
+                sel.add_carrier(message[0], "active", message[1:])
+                break
+
+        for carrier in request.carriers:
+            if found:
+                break
+            print "Remote carrier type: " + carrier.type
+            if carrier.type == "application/vnd.wfa.wsc":
+                print "WSC carrier type match - add WSC carrier record"
+                found = True
+                self.received_carrier = carrier.record
+                print "Carrier record:"
+                try:
+                    print carrier.record.pretty()
+                except Exception, e:
+                    print e
+                data = wpas_get_handover_sel_wps()
+                if data is None:
+                    print "Could not get handover select carrier record from wpa_supplicant"
+                    continue
+                print "Handover select carrier record from wpa_supplicant:"
+                print data.encode("hex")
+                self.sent_carrier = data
+                if "OK" in wpas_report_handover_wsc(self.received_carrier, self.sent_carrier, "RESP"):
+                    success_report("WSC handover reported successfully")
+                else:
+                    summary("WSC handover report rejected")
+                    break
+
+                message = nfc.ndef.Message(data);
+                sel.add_carrier(message[0], "active", message[1:])
+                found = True
+                break
+
+        print "Handover select:"
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
+        print str(sel).encode("hex")
+
+        summary("Sending handover select")
+        self.success = True
+        return sel
+
+
+def clear_raw_mode():
+    import sys, tty, termios
+    global prev_tcgetattr, in_raw_mode
+    if not in_raw_mode:
+        return
+    fd = sys.stdin.fileno()
+    termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+    in_raw_mode = False
+
+
+def getch():
+    import sys, tty, termios, select
+    global prev_tcgetattr, in_raw_mode
+    fd = sys.stdin.fileno()
+    prev_tcgetattr = termios.tcgetattr(fd)
+    ch = None
+    try:
+        tty.setraw(fd)
+        in_raw_mode = True
+        [i, o, e] = select.select([fd], [], [], 0.05)
+        if i:
+            ch = sys.stdin.read(1)
+    finally:
+        termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+        in_raw_mode = False
+    return ch
+
+
+def p2p_tag_read(tag):
+    success = False
+    if len(tag.ndef.message):
+        for record in tag.ndef.message:
+            print "record type " + record.type
+            if record.type == "application/vnd.wfa.wsc":
+                summary("WPS tag - send to wpa_supplicant")
+                success = wpas_tag_read(tag.ndef.message)
+                break
+            if record.type == "application/vnd.wfa.p2p":
+                summary("P2P tag - send to wpa_supplicant")
+                success = wpas_tag_read(tag.ndef.message)
+                break
+    else:
+        summary("Empty tag")
+
+    if success:
+        success_report("Tag read succeeded")
+
+    return success
+
+
+def rdwr_connected_p2p_write(tag):
+    summary("Tag found - writing - " + str(tag))
+    global p2p_sel_data
+    tag.ndef.message = str(p2p_sel_data)
+    success_report("Tag write succeeded")
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global p2p_sel_wait_remove
+    return p2p_sel_wait_remove
+
+def wps_write_p2p_handover_sel(clf, wait_remove=True):
+    print "Write P2P handover select"
+    data = wpas_get_handover_sel(tag=True)
+    if (data == None):
+        summary("Could not get P2P handover select from wpa_supplicant")
+        return
+
+    global p2p_sel_wait_remove
+    p2p_sel_wait_remove = wait_remove
+    global p2p_sel_data
+    p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2")
+    message = nfc.ndef.Message(data);
+    p2p_sel_data.add_carrier(message[0], "active", message[1:])
+    print "Handover select:"
+    try:
+        print p2p_sel_data.pretty()
+    except Exception, e:
+        print e
+    print str(p2p_sel_data).encode("hex")
+
+    print "Touch an NFC tag"
+    clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write})
+
+
+def rdwr_connected(tag):
+    global only_one, no_wait
+    summary("Tag connected: " + str(tag))
+
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = p2p_tag_read(tag)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        summary("Not an NDEF tag - remove tag")
+        return True
+
+    return not no_wait
+
+
+def llcp_worker(llc):
+    global init_on_touch
+    if init_on_touch:
+            print "Starting handover client"
+            p2p_handover_client(llc)
+            return
+
+    global no_input
+    if no_input:
+        print "Wait for handover to complete"
+    else:
+        print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)"
+    global srv
+    global wait_connection
+    while not wait_connection and srv.sent_carrier is None:
+        if srv.ho_server_processing:
+            time.sleep(0.025)
+        elif no_input:
+            time.sleep(0.5)
+        else:
+            global include_wps_req, include_p2p_req
+            res = getch()
+            if res == 'i':
+                include_wps_req = True
+                include_p2p_req = True
+            elif res == 'p':
+                include_wps_req = False
+                include_p2p_req = True
+            elif res == 'w':
+                include_wps_req = True
+                include_p2p_req = False
+            else:
+                continue
+            clear_raw_mode()
+            print "Starting handover client"
+            p2p_handover_client(llc)
+            return
+            
+    clear_raw_mode()
+    print "Exiting llcp_worker thread"
+
+def llcp_startup(clf, llc):
+    print "Start LLCP server"
+    global srv
+    srv = HandoverServer(llc)
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global init_on_touch
+    if not init_on_touch:
+        global srv
+        srv.start()
+    if init_on_touch or not no_input:
+        threading.Thread(target=llcp_worker, args=(llc,)).start()
+    return True
+
+def terminate_loop():
+    global terminate_now
+    return terminate_now
+
+def main():
+    clf = nfc.ContactlessFrontend()
+
+    parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--init-on-touch', '-I', action='store_true',
+                        help='initiate handover on touch')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--ifname', '-i',
+                        help='network interface name')
+    parser.add_argument('--no-wps-req', '-N', action='store_true',
+                        help='do not include WPS carrier record in request')
+    parser.add_argument('--no-input', '-a', action='store_true',
+                        help='do not use stdout input to initiate handover')
+    parser.add_argument('--tag-read-only', '-t', action='store_true',
+                        help='tag read only (do not allow connection handover)')
+    parser.add_argument('--handover-only', action='store_true',
+                        help='connection handover only (do not allow tag read)')
+    parser.add_argument('--freq', '-f',
+                        help='forced frequency of operating channel in MHz')
+    parser.add_argument('--summary',
+                        help='summary file for writing status updates')
+    parser.add_argument('--success',
+                        help='success file for writing success update')
+    parser.add_argument('command', choices=['write-p2p-sel'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    global force_freq
+    force_freq = args.freq
+
+    logging.basicConfig(level=args.loglevel)
+
+    global init_on_touch
+    init_on_touch = args.init_on_touch
+
+    if args.ifname:
+        global ifname
+        ifname = args.ifname
+        print "Selected ifname " + ifname
+
+    if args.no_wps_req:
+        global include_wps_req
+        include_wps_req = False
+
+    if args.summary:
+        global summary_file
+        summary_file = args.summary
+
+    if args.success:
+        global success_file
+        success_file = args.success
+
+    if args.no_input:
+        global no_input
+        no_input = True
+
+    clf = nfc.ContactlessFrontend()
+    global wait_connection
+
+    try:
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
+            raise SystemExit
+
+        if args.command == "write-p2p-sel":
+            wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        global continue_loop
+        while continue_loop:
+            print "Waiting for a tag or peer to be touched"
+            wait_connection = True
+            try:
+                if args.tag_read_only:
+                    if not clf.connect(rdwr={'on-connect': rdwr_connected}):
+                        break
+                elif args.handover_only:
+                    if not clf.connect(llcp={'on-startup': llcp_startup,
+                                             'on-connect': llcp_connected},
+                                       terminate=terminate_loop):
+                        break
+                else:
+                    if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                       llcp={'on-startup': llcp_startup,
+                                             'on-connect': llcp_connected},
+                                       terminate=terminate_loop):
+                        break
+            except Exception, e:
+                print "clf.connect failed"
+
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
+
+    except KeyboardInterrupt:
+        raise SystemExit
+    finally:
+        clf.close()
+
+    raise SystemExit
+
+if __name__ == '__main__':
+    main()
diff --git a/wpa_supplicant/examples/wps-nfc.py b/wpa_supplicant/examples/wps-nfc.py
index d6dec85..7459eb9 100755
--- a/wpa_supplicant/examples/wps-nfc.py
+++ b/wpa_supplicant/examples/wps-nfc.py
@@ -10,7 +10,8 @@
 import sys
 import time
 import random
-import StringIO
+import threading
+import argparse
 
 import nfc
 import nfc.ndef
@@ -18,11 +19,27 @@
 import nfc.handover
 
 import logging
-logging.basicConfig()
 
 import wpaspy
 
 wpas_ctrl = '/var/run/wpa_supplicant'
+srv = None
+continue_loop = True
+terminate_now = False
+summary_file = None
+success_file = None
+
+def summary(txt):
+    print txt
+    if summary_file:
+        with open(summary_file, 'a') as f:
+            f.write(txt + "\n")
+
+def success_report(txt):
+    summary(txt)
+    if success_file:
+        with open(success_file, 'a') as f:
+            f.write(txt + "\n")
 
 def wpas_connect():
     ifaces = []
@@ -50,7 +67,7 @@
     wpas = wpas_connect()
     if (wpas == None):
         return False
-    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + message.encode("hex")):
+    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
         return False
     return True
 
@@ -71,21 +88,29 @@
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid).rstrip().decode("hex")
+    ret = wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid)
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_password_token():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
-
+    ret = wpas.request("WPS_NFC_TOKEN NDEF")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 def wpas_get_handover_req():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
+    ret = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_handover_sel(uuid):
@@ -93,8 +118,12 @@
     if (wpas == None):
         return None
     if uuid is None:
-        return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
-    return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip().decode("hex")
+        res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    else:
+	res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip()
+    if "FAIL" in res:
+	return None
+    return res.decode("hex")
 
 
 def wpas_report_handover(req, sel, type):
@@ -107,159 +136,176 @@
 
 
 class HandoverServer(nfc.handover.HandoverServer):
-    def __init__(self):
-        super(HandoverServer, self).__init__()
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.sent_carrier = None
+        self.ho_server_processing = False
+        self.success = False
+
+    # override to avoid parser error in request/response.pretty() in nfcpy
+    # due to new WSC handover format
+    def _process_request(self, request):
+        summary("received handover request {}".format(request.type))
+        response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+        if not request.type == 'urn:nfc:wkt:Hr':
+            summary("not a handover request")
+        else:
+            try:
+                request = nfc.ndef.HandoverRequestMessage(request)
+            except nfc.ndef.DecodeError as e:
+                summary("error decoding 'Hr' message: {}".format(e))
+            else:
+                response = self.process_request(request)
+        summary("send handover response {}".format(response.type))
+        return response
 
     def process_request(self, request):
-        print "HandoverServer - request received"
-        print "Parsed handover request: " + request.pretty()
+        self.ho_server_processing = True
+        summary("HandoverServer - request received")
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
 
         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
 
         for carrier in request.carriers:
             print "Remote carrier type: " + carrier.type
             if carrier.type == "application/vnd.wfa.wsc":
-                print "WPS carrier type match - add WPS carrier record"
-                self.received_carrier = carrier.record
+                summary("WPS carrier type match - add WPS carrier record")
                 data = wpas_get_handover_sel(self.uuid)
                 if data is None:
-                    print "Could not get handover select carrier record from wpa_supplicant"
+                    summary("Could not get handover select carrier record from wpa_supplicant")
                     continue
                 print "Handover select carrier record from wpa_supplicant:"
                 print data.encode("hex")
                 self.sent_carrier = data
+                if "OK" in wpas_report_handover(carrier.record, self.sent_carrier, "RESP"):
+                    success_report("Handover reported successfully (responder)")
+                else:
+                    summary("Handover report rejected (responder)")
 
                 message = nfc.ndef.Message(data);
                 sel.add_carrier(message[0], "active", message[1:])
 
         print "Handover select:"
-        print sel.pretty()
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
         print str(sel).encode("hex")
 
-        print "Sending handover select"
+        summary("Sending handover select")
+        self.success = True
         return sel
 
 
-def wps_handover_resp(peer, uuid):
-    if uuid is None:
-        print "Trying to handle WPS handover"
-    else:
-        print "Trying to handle WPS handover with AP " + uuid
-
-    srv = HandoverServer()
-    srv.sent_carrier = None
-    srv.uuid = uuid
-
-    nfc.llcp.activate(peer);
-
-    try:
-        print "Trying handover";
-        srv.start()
-        print "Wait for disconnect"
-        while nfc.llcp.connected():
-            time.sleep(0.1)
-        print "Disconnected after handover"
-    except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
-        return
-
-    if srv.sent_carrier:
-        wpas_report_handover(srv.received_carrier, srv.sent_carrier, "RESP")
-
-    print "Remove peer"
-    nfc.llcp.shutdown()
-    print "Done with handover"
-    time.sleep(1)
-
-
-def wps_handover_init(peer):
-    print "Trying to initiate WPS handover"
+def wps_handover_init(llc):
+    summary("Trying to initiate WPS handover")
 
     data = wpas_get_handover_req()
     if (data == None):
-        print "Could not get handover request carrier record from wpa_supplicant"
+        summary("Could not get handover request carrier record from wpa_supplicant")
         return
     print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
-    record = nfc.ndef.Record()
-    f = StringIO.StringIO(data)
-    record._read(f)
-    record = nfc.ndef.HandoverCarrierRecord(record)
-    print "Parsed handover request carrier record:"
-    print record.pretty()
 
     message = nfc.ndef.HandoverRequestMessage(version="1.2")
     message.nonce = random.randint(0, 0xffff)
-    message.add_carrier(record, "active")
+    datamsg = nfc.ndef.Message(data)
+    message.add_carrier(datamsg[0], "active", datamsg[1:])
 
     print "Handover request:"
-    print message.pretty()
-
-    nfc.llcp.activate(peer);
-
-    client = nfc.handover.HandoverClient()
     try:
-        print "Trying handover";
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
+
+    client = nfc.handover.HandoverClient(llc)
+    try:
+        summary("Trying to initiate NFC connection handover")
         client.connect()
-        print "Connected for handover"
+        summary("Connected for handover")
     except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
+        summary("Handover connection refused")
+        client.close()
+        return
+    except Exception, e:
+        summary("Other exception: " + str(e))
         client.close()
         return
 
-    print "Sending handover request"
+    summary("Sending handover request")
 
     if not client.send(message):
-        print "Failed to send handover request"
+        summary("Failed to send handover request")
+        client.close()
+        return
 
-    print "Receiving handover response"
+    summary("Receiving handover response")
     message = client._recv()
     if message is None:
-        print "No response received"
-        nfc.llcp.shutdown()
+        summary("No response received")
         client.close()
         return
     if message.type != "urn:nfc:wkt:Hs":
-        print "Response was not Hs - received: " + message.type
-        nfc.llcp.shutdown()
+        summary("Response was not Hs - received: " + message.type)
         client.close()
         return
 
     print "Received message"
-    print message.pretty()
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
     message = nfc.ndef.HandoverSelectMessage(message)
-    print "Handover select received"
-    print message.pretty()
+    summary("Handover select received")
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
 
     for carrier in message.carriers:
         print "Remote carrier type: " + carrier.type
         if carrier.type == "application/vnd.wfa.wsc":
             print "WPS carrier type match - send to wpa_supplicant"
-            wpas_report_handover(data, carrier.record, "INIT")
-            wifi = nfc.ndef.WifiConfigRecord(carrier.record)
-            print wifi.pretty()
+            if "OK" in wpas_report_handover(data, carrier.record, "INIT"):
+                success_report("Handover reported successfully (initiator)")
+            else:
+                summary("Handover report rejected (initiator)")
+            # nfcpy does not support the new format..
+            #wifi = nfc.ndef.WifiConfigRecord(carrier.record)
+            #print wifi.pretty()
 
     print "Remove peer"
-    nfc.llcp.shutdown()
     client.close()
     print "Done with handover"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
 
+    global no_wait
+    if no_wait:
+        print "Trying to exit.."
+        global terminate_now
+        terminate_now = True
 
 def wps_tag_read(tag, wait_remove=True):
     success = False
     if len(tag.ndef.message):
-        message = nfc.ndef.Message(tag.ndef.message)
-        print "message type " + message.type
-
-        for record in message:
+        for record in tag.ndef.message:
             print "record type " + record.type
             if record.type == "application/vnd.wfa.wsc":
-                print "WPS tag - send to wpa_supplicant"
+                summary("WPS tag - send to wpa_supplicant")
                 success = wpas_tag_read(tag.ndef.message)
                 break
     else:
-        print "Empty tag"
+        summary("Empty tag")
+
+    if success:
+        success_report("Tag read succeeded")
 
     if wait_remove:
         print "Remove tag"
@@ -269,166 +315,204 @@
     return success
 
 
+def rdwr_connected_write(tag):
+    summary("Tag found - writing - " + str(tag))
+    global write_data
+    tag.ndef.message = str(write_data)
+    success_report("Tag write succeeded")
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global write_wait_remove
+    while write_wait_remove and tag.is_present:
+        time.sleep(0.1)
+
 def wps_write_config_tag(clf, id=None, wait_remove=True):
     print "Write WPS config token"
-    data = wpas_get_config_token(id)
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_config_token(id)
+    if write_data == None:
         print "Could not get WPS config token from wpa_supplicant"
         sys.exit(1)
         return
-
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while wait_remove and tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def wps_write_er_config_tag(clf, uuid):
+def wps_write_er_config_tag(clf, uuid, wait_remove=True):
     print "Write WPS ER config token"
-    data = wpas_get_er_config_token(uuid)
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_er_config_token(uuid)
+    if write_data == None:
         print "Could not get WPS config token from wpa_supplicant"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
 def wps_write_password_tag(clf, wait_remove=True):
     print "Write WPS password token"
-    data = wpas_get_password_token()
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_password_token()
+    if write_data == None:
         print "Could not get WPS password token from wpa_supplicant"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while wait_remove and tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def find_peer(clf):
-    while True:
-        if nfc.llcp.connected():
-            print "LLCP connected"
-        general_bytes = nfc.llcp.startup({})
-        peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "listen -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+def rdwr_connected(tag):
+    global only_one, no_wait
+    summary("Tag connected: " + str(tag))
 
-        peer = clf.poll(general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "poll -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = wps_tag_read(tag, not only_one)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        summary("Not an NDEF tag - remove tag")
+        return True
 
-        if peer:
-            print "Found tag"
-            return peer
+    return not no_wait
 
 
+def llcp_worker(llc):
+    global arg_uuid
+    if arg_uuid is None:
+        wps_handover_init(llc)
+        print "Exiting llcp_worker thread"
+        return
+
+    global srv
+    global wait_connection
+    while not wait_connection and srv.sent_carrier is None:
+        if srv.ho_server_processing:
+            time.sleep(0.025)
+
+def llcp_startup(clf, llc):
+    global arg_uuid
+    if arg_uuid:
+        print "Start LLCP server"
+        global srv
+        srv = HandoverServer(llc)
+        if arg_uuid is "ap":
+            print "Trying to handle WPS handover"
+            srv.uuid = None
+        else:
+            print "Trying to handle WPS handover with AP " + arg_uuid
+            srv.uuid = arg_uuid
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global arg_uuid
+    if arg_uuid:
+        global srv
+        srv.start()
+    else:
+        threading.Thread(target=llcp_worker, args=(llc,)).start()
+    print "llcp_connected returning"
+    return True
+
+
+def terminate_loop():
+    global terminate_now
+    return terminate_now
+
 def main():
     clf = nfc.ContactlessFrontend()
 
+    parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--uuid',
+                        help='UUID of an AP (used for WPS ER operations)')
+    parser.add_argument('--id',
+                        help='network id (used for WPS ER operations)')
+    parser.add_argument('--summary',
+                        help='summary file for writing status updates')
+    parser.add_argument('--success',
+                        help='success file for writing success update')
+    parser.add_argument('command', choices=['write-config',
+                                            'write-er-config',
+                                            'write-password'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global arg_uuid
+    arg_uuid = args.uuid
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    if args.summary:
+        global summary_file
+        summary_file = args.summary
+
+    if args.success:
+        global success_file
+        success_file = args.success
+
+    logging.basicConfig(level=args.loglevel)
+
     try:
-        arg_uuid = None
-        if len(sys.argv) > 1 and sys.argv[1] != '-1':
-            arg_uuid = sys.argv[1]
-
-        if len(sys.argv) > 1 and sys.argv[1] == '-1':
-            only_one = True
-        else:
-            only_one = False
-
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config":
-            wps_write_config_tag(clf)
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config-no-wait":
-            wps_write_config_tag(clf, wait_remove=False)
+        if args.command == "write-config":
+            wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 2 and sys.argv[1] == "write-config-id":
-            wps_write_config_tag(clf, sys.argv[2])
+        if args.command == "write-er-config":
+            wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 2 and sys.argv[1] == "write-er-config":
-            wps_write_er_config_tag(clf, sys.argv[2])
+        if args.command == "write-password":
+            wps_write_password_tag(clf, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password":
-            wps_write_password_tag(clf)
-            raise SystemExit
-
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password-no-wait":
-            wps_write_password_tag(clf, wait_remove=False)
-            raise SystemExit
-
-        while True:
+        global continue_loop
+        while continue_loop:
             print "Waiting for a tag or peer to be touched"
-
-            tag = find_peer(clf)
-            if isinstance(tag, nfc.DEP):
-                if arg_uuid is None:
-                    wps_handover_init(tag)
-                elif arg_uuid is "ap":
-                    wps_handover_resp(tag, None)
-                else:
-                    wps_handover_resp(tag, arg_uuid)
-                if only_one:
+            wait_connection = True
+            try:
+                if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                   llcp={'on-startup': llcp_startup,
+                                         'on-connect': llcp_connected},
+                                   terminate=terminate_loop):
                     break
-                continue
+            except Exception, e:
+                print "clf.connect failed"
 
-            if tag.ndef:
-                success = wps_tag_read(tag, not only_one)
-                if only_one:
-                    if not success:
-                        sys.exit(1)
-                    break
-                continue
-
-            print "Not an NDEF tag - remove tag"
-            if only_one:
-                sys.exit(1)
-            while tag.is_present:
-                time.sleep(0.1)
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
 
     except KeyboardInterrupt:
         raise SystemExit
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 06a97d3..3986268 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -1,7 +1,8 @@
 /*
  * Generic advertisement service (GAS) query
  * Copyright (c) 2009, Atheros Communications
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,6 +14,8 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "offchannel.h"
@@ -28,6 +31,7 @@
  */
 struct gas_query_pending {
 	struct dl_list list;
+	struct gas_query *gas;
 	u8 addr[ETH_ALEN];
 	u8 dialog_token;
 	u8 next_frag_id;
@@ -35,8 +39,10 @@
 	unsigned int offchannel_tx_started:1;
 	int freq;
 	u16 status_code;
+	struct wpabuf *req;
 	struct wpabuf *adv_proto;
 	struct wpabuf *resp;
+	struct os_reltime last_oper;
 	void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
 		   enum gas_query_result result,
 		   const struct wpabuf *adv_proto,
@@ -50,6 +56,8 @@
 struct gas_query {
 	struct wpa_supplicant *wpa_s;
 	struct dl_list pending; /* struct gas_query_pending */
+	struct gas_query_pending *current;
+	struct wpa_radio_work *work;
 };
 
 
@@ -57,6 +65,16 @@
 static void gas_query_timeout(void *eloop_data, void *user_ctx);
 
 
+static int ms_from_time(struct os_reltime *last)
+{
+	struct os_reltime now, res;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, last, &res);
+	return res.sec * 1000 + res.usec / 1000;
+}
+
+
 /**
  * gas_query_init - Initialize GAS query component
  * @wpa_s: Pointer to wpa_supplicant data
@@ -77,10 +95,58 @@
 }
 
 
+static const char * gas_result_txt(enum gas_query_result result)
+{
+	switch (result) {
+	case GAS_QUERY_SUCCESS:
+		return "SUCCESS";
+	case GAS_QUERY_FAILURE:
+		return "FAILURE";
+	case GAS_QUERY_TIMEOUT:
+		return "TIMEOUT";
+	case GAS_QUERY_PEER_ERROR:
+		return "PEER_ERROR";
+	case GAS_QUERY_INTERNAL_ERROR:
+		return "INTERNAL_ERROR";
+	case GAS_QUERY_CANCELLED:
+		return "CANCELLED";
+	case GAS_QUERY_DELETED_AT_DEINIT:
+		return "DELETED_AT_DEINIT";
+	}
+
+	return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+	struct gas_query *gas = query->gas;
+
+	if (del_list)
+		dl_list_del(&query->list);
+
+	if (gas->work && gas->work->ctx == query) {
+		radio_work_done(gas->work);
+		gas->work = NULL;
+	}
+
+	wpabuf_free(query->req);
+	wpabuf_free(query->adv_proto);
+	wpabuf_free(query->resp);
+	os_free(query);
+}
+
+
 static void gas_query_done(struct gas_query *gas,
 			   struct gas_query_pending *query,
 			   enum gas_query_result result)
 {
+	wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+		" dialog_token=%u freq=%d status_code=%u result=%s",
+		MAC2STR(query->addr), query->dialog_token, query->freq,
+		query->status_code, gas_result_txt(result));
+	if (gas->current == query)
+		gas->current = NULL;
 	if (query->offchannel_tx_started)
 		offchannel_send_action_done(gas->wpa_s);
 	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
@@ -88,9 +154,7 @@
 	dl_list_del(&query->list);
 	query->cb(query->ctx, query->addr, query->dialog_token, result,
 		  query->adv_proto, query->resp, query->status_code);
-	wpabuf_free(query->adv_proto);
-	wpabuf_free(query->resp);
-	os_free(query);
+	gas_query_free(query, 0);
 }
 
 
@@ -138,17 +202,79 @@
 }
 
 
+static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
+				unsigned int freq, const u8 *dst,
+				const u8 *src, const u8 *bssid,
+				const u8 *data, size_t data_len,
+				enum offchannel_send_action_result result)
+{
+	struct gas_query_pending *query;
+	struct gas_query *gas = wpa_s->gas;
+	int dur;
+
+	if (gas->current == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
+			   MACSTR " result=%d - no query in progress",
+			   freq, MAC2STR(dst), result);
+		return;
+	}
+
+	query = gas->current;
+
+	dur = ms_from_time(&query->last_oper);
+	wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+		   " result=%d query=%p dialog_token=%u dur=%d ms",
+		   freq, MAC2STR(dst), result, query, query->dialog_token, dur);
+	if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+		return;
+	}
+	os_get_reltime(&query->last_oper);
+
+	if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
+		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+				       gas_query_timeout, gas, query);
+	}
+	if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
+		eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
+	}
+}
+
+
+static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	if (wpa_s->current_ssid == NULL ||
+	    wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
+	    os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
+		return 0;
+	return wpa_sm_pmf_enabled(wpa_s->wpa);
+}
+
+
 static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
 			struct wpabuf *req)
 {
-	int res;
+	unsigned int wait_time;
+	int res, prot = pmf_in_use(gas->wpa_s, query->addr);
+
 	wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
-		   "freq=%d", MAC2STR(query->addr),
-		   (unsigned int) wpabuf_len(req), query->freq);
+		   "freq=%d prot=%d", MAC2STR(query->addr),
+		   (unsigned int) wpabuf_len(req), query->freq, prot);
+	if (prot) {
+		u8 *categ = wpabuf_mhead_u8(req);
+		*categ = WLAN_ACTION_PROTECTED_DUAL;
+	}
+	os_get_reltime(&query->last_oper);
+	wait_time = 1000;
+	if (gas->wpa_s->max_remain_on_chan &&
+	    wait_time > gas->wpa_s->max_remain_on_chan)
+		wait_time = gas->wpa_s->max_remain_on_chan;
 	res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
 				     gas->wpa_s->own_addr, query->addr,
-				     wpabuf_head(req), wpabuf_len(req), 1000,
-				     NULL, 0);
+				     wpabuf_head(req), wpabuf_len(req),
+				     wait_time, gas_query_tx_status, 0);
 	if (res == 0)
 		query->offchannel_tx_started = 1;
 	return res;
@@ -296,27 +422,41 @@
 
 
 /**
- * gas_query_rx - Indicate reception of a Public Action frame
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
  * @gas: GAS query data from gas_query_init()
  * @da: Destination MAC address of the Action frame
  * @sa: Source MAC address of the Action frame
  * @bssid: BSSID of the Action frame
+ * @categ: Category of the Action frame
  * @data: Payload of the Action frame
  * @len: Length of @data
  * @freq: Frequency (in MHz) on which the frame was received
  * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
  */
 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
-		 const u8 *bssid, const u8 *data, size_t len, int freq)
+		 const u8 *bssid, u8 categ, const u8 *data, size_t len,
+		 int freq)
 {
 	struct gas_query_pending *query;
 	u8 action, dialog_token, frag_id = 0, more_frags = 0;
 	u16 comeback_delay, resp_len;
 	const u8 *pos, *adv_proto;
+	int prot, pmf;
 
 	if (gas == NULL || len < 4)
 		return -1;
 
+	prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+	pmf = pmf_in_use(gas->wpa_s, bssid);
+	if (prot && !pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+		return 0;
+	}
+	if (!prot && pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+		return 0;
+	}
+
 	pos = data;
 	action = *pos++;
 	dialog_token = *pos++;
@@ -332,6 +472,9 @@
 		return -1;
 	}
 
+	wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+		   ms_from_time(&query->last_oper), MAC2STR(sa));
+
 	if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
 		wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
 			   MACSTR " dialog token %u when waiting for comeback "
@@ -349,7 +492,10 @@
 	query->status_code = WPA_GET_LE16(pos);
 	pos += 2;
 
-	if (query->status_code != WLAN_STATUS_SUCCESS) {
+	if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+	    action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+	} else if (query->status_code != WLAN_STATUS_SUCCESS) {
 		wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
 			   "%u failed - status code %u",
 			   MAC2STR(sa), dialog_token, query->status_code);
@@ -426,8 +572,9 @@
 	struct gas_query *gas = eloop_data;
 	struct gas_query_pending *query = user_ctx;
 
-	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
-		   MAC2STR(query->addr));
+	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+		   " dialog token %u",
+		   MAC2STR(query->addr), query->dialog_token);
 	gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
 }
 
@@ -446,12 +593,47 @@
 }
 
 
+static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct gas_query_pending *query = work->ctx;
+	struct gas_query *gas = query->gas;
+
+	if (deinit) {
+		if (work->started) {
+			gas->work = NULL;
+			gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+			return;
+		}
+
+		gas_query_free(query, 1);
+		return;
+	}
+
+	gas->work = work;
+
+	if (gas_query_tx(gas, query, query->req) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_free(query, 1);
+		return;
+	}
+	gas->current = query;
+
+	wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+		   query->dialog_token);
+	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+			       gas_query_timeout, gas, query);
+
+}
+
+
 /**
  * gas_query_req - Request a GAS query
  * @gas: GAS query data from gas_query_init()
  * @dst: Destination MAC address for the query
  * @freq: Frequency (in MHz) for the channel on which to send the query
- * @req: GAS query payload
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ *	return)
  * @cb: Callback function for reporting GAS query result and response
  * @ctx: Context pointer to use with the @cb call
  * Returns: dialog token (>= 0) on success or -1 on failure
@@ -485,28 +667,27 @@
 	if (query == NULL)
 		return -1;
 
+	query->gas = gas;
 	os_memcpy(query->addr, dst, ETH_ALEN);
 	query->dialog_token = dialog_token;
 	query->freq = freq;
 	query->cb = cb;
 	query->ctx = ctx;
+	query->req = req;
 	dl_list_add(&gas->pending, &query->list);
 
 	*(wpabuf_mhead_u8(req) + 2) = dialog_token;
 
-	wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
-		   " dialog_token %u", MAC2STR(dst), dialog_token);
-	if (gas_query_tx(gas, query, req) < 0) {
-		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
-			   MACSTR, MAC2STR(query->addr));
-		dl_list_del(&query->list);
-		os_free(query);
+	wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+		" dialog_token=%u freq=%d",
+		MAC2STR(query->addr), query->dialog_token, query->freq);
+
+	if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
+			   query) < 0) {
+		gas_query_free(query, 1);
 		return -1;
 	}
 
-	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
-			       gas, query);
-
 	return dialog_token;
 }
 
diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h
index 5c3d161..ad13490 100644
--- a/wpa_supplicant/gas_query.h
+++ b/wpa_supplicant/gas_query.h
@@ -17,7 +17,8 @@
 struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
 void gas_query_deinit(struct gas_query *gas);
 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
-		 const u8 *bssid, const u8 *data, size_t len, int freq);
+		 const u8 *bssid, u8 categ, const u8 *data, size_t len,
+		 int freq);
 
 /**
  * enum gas_query_result - GAS query result
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 4048cf7..257aa6d 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,22 +14,65 @@
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
 #include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "config.h"
+#include "scan.h"
 #include "bss.h"
+#include "blacklist.h"
 #include "gas_query.h"
 #include "interworking.h"
 #include "hs20_supplicant.h"
 
 
-void wpas_hs20_add_indication(struct wpabuf *buf)
+#define OSU_MAX_ITEMS 10
+
+struct osu_lang_string {
+	char lang[4];
+	char text[253];
+};
+
+struct osu_icon {
+	u16 width;
+	u16 height;
+	char lang[4];
+	char icon_type[256];
+	char filename[256];
+	unsigned int id;
+	unsigned int failed:1;
+};
+
+struct osu_provider {
+	u8 bssid[ETH_ALEN];
+	u8 osu_ssid[32];
+	u8 osu_ssid_len;
+	char server_uri[256];
+	u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
+	char osu_nai[256];
+	struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
+	size_t friendly_name_count;
+	struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
+	size_t serv_desc_count;
+	struct osu_icon icon[OSU_MAX_ITEMS];
+	size_t icon_count;
+};
+
+
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
 {
+	u8 conf;
+
 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
-	wpabuf_put_u8(buf, 5);
+	wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5);
 	wpabuf_put_be24(buf, OUI_WFA);
 	wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
-	wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */
+	conf = HS20_VERSION;
+	if (pps_mo_id >= 0)
+		conf |= HS20_PPS_MO_ID_PRESENT;
+	wpabuf_put_u8(buf, conf);
+	if (pps_mo_id >= 0)
+		wpabuf_put_le16(buf, pps_mo_id);
 }
 
 
@@ -62,15 +105,35 @@
 }
 
 
-struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
-				    size_t payload_len)
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
-	struct wpabuf *buf;
+	struct wpa_cred *cred;
+
+	if (ssid == NULL)
+		return 0;
+
+	if (ssid->update_identifier)
+		return ssid->update_identifier;
+
+	if (ssid->parent_cred == NULL)
+		return 0;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (ssid->parent_cred == cred)
+			return cred->update_identifier;
+	}
+
+	return 0;
+}
+
+
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+		       struct wpabuf *buf)
+{
 	u8 *len_pos;
 
-	buf = gas_anqp_build_initial_req(0, 100 + payload_len);
 	if (buf == NULL)
-		return NULL;
+		return;
 
 	len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
 	wpabuf_put_be24(buf, OUI_WFA);
@@ -80,6 +143,11 @@
 		wpabuf_put_u8(buf, 0); /* Reserved */
 		if (payload)
 			wpabuf_put_data(buf, payload, payload_len);
+	} else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) {
+		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		if (payload)
+			wpabuf_put_data(buf, payload, payload_len);
 	} else {
 		u8 i;
 		wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
@@ -92,6 +160,19 @@
 	gas_anqp_set_element_len(buf, len_pos);
 
 	gas_anqp_set_len(buf);
+}
+
+
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+				    size_t payload_len)
+{
+	struct wpabuf *buf;
+
+	buf = gas_anqp_build_initial_req(0, 100 + payload_len);
+	if (buf == NULL)
+		return NULL;
+
+	hs20_put_anqp_req(stypes, payload, payload_len, buf);
 
 	return buf;
 }
@@ -125,16 +206,126 @@
 	res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+		wpabuf_free(buf);
 		ret = -1;
 	} else
 		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
 			   "%u", res);
 
-	wpabuf_free(buf);
 	return ret;
 }
 
 
+static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
+					 const u8 *sa, const u8 *pos,
+					 size_t slen)
+{
+	char fname[256];
+	int png;
+	FILE *f;
+	u16 data_len;
+
+	wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
+		MAC2STR(sa));
+
+	if (slen < 4) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+			"value from " MACSTR, MAC2STR(sa));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
+	if (*pos != 0)
+		return -1;
+	pos++;
+	slen--;
+
+	if ((size_t) 1 + pos[0] > slen) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+			"value from " MACSTR, MAC2STR(sa));
+		return -1;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
+	png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
+	slen -= 1 + pos[0];
+	pos += 1 + pos[0];
+
+	if (slen < 2) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+			"value from " MACSTR, MAC2STR(sa));
+		return -1;
+	}
+	data_len = WPA_GET_LE16(pos);
+	pos += 2;
+	slen -= 2;
+
+	if (data_len > slen) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+			"value from " MACSTR, MAC2STR(sa));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
+	if (wpa_s->conf->osu_dir == NULL)
+		return -1;
+
+	wpa_s->osu_icon_id++;
+	if (wpa_s->osu_icon_id == 0)
+		wpa_s->osu_icon_id++;
+	snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
+		 wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
+		 png ? "png" : "icon");
+	f = fopen(fname, "wb");
+	if (f == NULL)
+		return -1;
+	if (fwrite(pos, slen, 1, f) != 1) {
+		fclose(f);
+		unlink(fname);
+		return -1;
+	}
+	fclose(f);
+
+	wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+	return 0;
+}
+
+
+static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	if (wpa_s->fetch_osu_icon_in_progress)
+		hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
+{
+	size_t i, j;
+	struct os_reltime now, tmp;
+	int dur;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
+	dur = tmp.sec * 1000 + tmp.usec / 1000;
+	wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
+		   dur, res);
+
+	for (i = 0; i < wpa_s->osu_prov_count; i++) {
+		struct osu_provider *osu = &wpa_s->osu_prov[i];
+		for (j = 0; j < osu->icon_count; j++) {
+			struct osu_icon *icon = &osu->icon[j];
+			if (icon->id || icon->failed)
+				continue;
+			if (res < 0)
+				icon->failed = 1;
+			else
+				icon->id = wpa_s->osu_icon_id;
+			return;
+		}
+	}
+}
+
+
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
 				  const u8 *sa, const u8 *data, size_t slen)
 {
@@ -142,6 +333,7 @@
 	u8 subtype;
 	struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
 	struct wpa_bss_anqp *anqp = NULL;
+	int ret;
 
 	if (slen < 2)
 		return;
@@ -207,8 +399,537 @@
 				wpabuf_alloc_copy(pos, slen);
 		}
 		break;
+	case HS20_STYPE_OSU_PROVIDERS_LIST:
+		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+			" OSU Providers list", MAC2STR(sa));
+		wpa_s->num_prov_found++;
+		if (anqp) {
+			wpabuf_free(anqp->hs20_osu_providers_list);
+			anqp->hs20_osu_providers_list =
+				wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+	case HS20_STYPE_ICON_BINARY_FILE:
+		ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen);
+		if (wpa_s->fetch_osu_icon_in_progress) {
+			hs20_osu_icon_fetch_result(wpa_s, ret);
+			eloop_cancel_timeout(hs20_continue_icon_fetch,
+					     wpa_s, NULL);
+			eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
+					       wpa_s, NULL);
+		}
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
 		break;
 	}
 }
+
+
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->fetch_osu_icon_in_progress)
+		return;
+	if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
+		return;
+	/*
+	 * We are going through icon fetch, but no icon response was received.
+	 * Assume this means the current AP could not provide an answer to avoid
+	 * getting stuck in fetch iteration.
+	 */
+	hs20_icon_fetch_failed(wpa_s);
+}
+
+
+static void hs20_free_osu_prov_entry(struct osu_provider *prov)
+{
+}
+
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
+{
+	size_t i;
+	for (i = 0; i < wpa_s->osu_prov_count; i++)
+		hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
+	os_free(wpa_s->osu_prov);
+	wpa_s->osu_prov = NULL;
+	wpa_s->osu_prov_count = 0;
+}
+
+
+static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
+{
+	char fname[256];
+	FILE *f;
+	size_t i, j;
+
+	wpa_s->fetch_osu_info = 0;
+	wpa_s->fetch_osu_icon_in_progress = 0;
+
+	if (wpa_s->conf->osu_dir == NULL) {
+		hs20_free_osu_prov(wpa_s);
+		wpa_s->fetch_anqp_in_progress = 0;
+		return;
+	}
+
+	snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
+		 wpa_s->conf->osu_dir);
+	f = fopen(fname, "w");
+	if (f == NULL) {
+		hs20_free_osu_prov(wpa_s);
+		return;
+	}
+	for (i = 0; i < wpa_s->osu_prov_count; i++) {
+		struct osu_provider *osu = &wpa_s->osu_prov[i];
+		if (i > 0)
+			fprintf(f, "\n");
+		fprintf(f, "OSU-PROVIDER " MACSTR "\n"
+			"uri=%s\n"
+			"methods=%08x\n",
+			MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
+		if (osu->osu_ssid_len) {
+			fprintf(f, "osu_ssid=%s\n",
+				wpa_ssid_txt(osu->osu_ssid,
+					     osu->osu_ssid_len));
+		}
+		if (osu->osu_nai[0])
+			fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+		for (j = 0; j < osu->friendly_name_count; j++) {
+			fprintf(f, "friendly_name=%s:%s\n",
+				osu->friendly_name[j].lang,
+				osu->friendly_name[j].text);
+		}
+		for (j = 0; j < osu->serv_desc_count; j++) {
+			fprintf(f, "desc=%s:%s\n",
+				osu->serv_desc[j].lang,
+				osu->serv_desc[j].text);
+		}
+		for (j = 0; j < osu->icon_count; j++) {
+			struct osu_icon *icon = &osu->icon[j];
+			if (icon->failed)
+				continue; /* could not fetch icon */
+			fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
+				icon->id, icon->width, icon->height, icon->lang,
+				icon->icon_type, icon->filename);
+		}
+	}
+	fclose(f);
+	hs20_free_osu_prov(wpa_s);
+
+	wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
+	wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
+{
+	size_t i, j;
+
+	wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
+
+	for (i = 0; i < wpa_s->osu_prov_count; i++) {
+		struct osu_provider *osu = &wpa_s->osu_prov[i];
+		for (j = 0; j < osu->icon_count; j++) {
+			struct osu_icon *icon = &osu->icon[j];
+			if (icon->id || icon->failed)
+				continue;
+
+			wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
+				   "from " MACSTR, icon->filename,
+				   MAC2STR(osu->bssid));
+			os_get_reltime(&wpa_s->osu_icon_fetch_start);
+			if (hs20_anqp_send_req(wpa_s, osu->bssid,
+					       BIT(HS20_STYPE_ICON_REQUEST),
+					       (u8 *) icon->filename,
+					       os_strlen(icon->filename)) < 0) {
+				icon->failed = 1;
+				continue;
+			}
+			return;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
+	hs20_osu_fetch_done(wpa_s);
+}
+
+
+static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			      const u8 *osu_ssid, u8 osu_ssid_len,
+			      const u8 *pos, size_t len)
+{
+	struct osu_provider *prov;
+	const u8 *end = pos + len;
+	u16 len2;
+	const u8 *pos2;
+
+	wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
+	prov = os_realloc_array(wpa_s->osu_prov,
+				wpa_s->osu_prov_count + 1,
+				sizeof(*prov));
+	if (prov == NULL)
+		return;
+	wpa_s->osu_prov = prov;
+	prov = &prov[wpa_s->osu_prov_count];
+	os_memset(prov, 0, sizeof(*prov));
+
+	os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
+	os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
+	prov->osu_ssid_len = osu_ssid_len;
+
+	/* OSU Friendly Name Length */
+	if (pos + 2 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+			   "Friendly Name Length");
+		return;
+	}
+	len2 = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + len2 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+			   "Friendly Name Duples");
+		return;
+	}
+	pos2 = pos;
+	pos += len2;
+
+	/* OSU Friendly Name Duples */
+	while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+		struct osu_lang_string *f;
+		if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+			wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
+			break;
+		}
+		f = &prov->friendly_name[prov->friendly_name_count++];
+		os_memcpy(f->lang, pos2 + 1, 3);
+		os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+		pos2 += 1 + pos2[0];
+	}
+
+	/* OSU Server URI */
+	if (pos + 1 > end || pos + 1 + pos[0] > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
+			   "URI");
+		return;
+	}
+	os_memcpy(prov->server_uri, pos + 1, pos[0]);
+	pos += 1 + pos[0];
+
+	/* OSU Method list */
+	if (pos + 1 > end || pos + 1 + pos[0] > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+			   "list");
+		return;
+	}
+	pos2 = pos + 1;
+	pos += 1 + pos[0];
+	while (pos2 < pos) {
+		if (*pos2 < 32)
+			prov->osu_methods |= BIT(*pos2);
+		pos2++;
+	}
+
+	/* Icons Available Length */
+	if (pos + 2 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+			   "Available Length");
+		return;
+	}
+	len2 = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + len2 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+			   "Available");
+		return;
+	}
+	pos2 = pos;
+	pos += len2;
+
+	/* Icons Available */
+	while (pos2 < pos) {
+		struct osu_icon *icon = &prov->icon[prov->icon_count];
+		if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
+			break;
+		}
+
+		icon->width = WPA_GET_LE16(pos2);
+		pos2 += 2;
+		icon->height = WPA_GET_LE16(pos2);
+		pos2 += 2;
+		os_memcpy(icon->lang, pos2, 3);
+		pos2 += 3;
+
+		if (pos2 + 1 + pos2[0] > pos) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
+			break;
+		}
+		os_memcpy(icon->icon_type, pos2 + 1, pos2[0]);
+		pos2 += 1 + pos2[0];
+
+		if (pos2 + 1 + pos2[0] > pos) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+				   "Filename");
+			break;
+		}
+		os_memcpy(icon->filename, pos2 + 1, pos2[0]);
+		pos2 += 1 + pos2[0];
+
+		prov->icon_count++;
+	}
+
+	/* OSU_NAI */
+	if (pos + 1 > end || pos + 1 + pos[0] > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+		return;
+	}
+	os_memcpy(prov->osu_nai, pos + 1, pos[0]);
+	pos += 1 + pos[0];
+
+	/* OSU Service Description Length */
+	if (pos + 2 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+			   "Service Description Length");
+		return;
+	}
+	len2 = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + len2 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+			   "Service Description Duples");
+		return;
+	}
+	pos2 = pos;
+	pos += len2;
+
+	/* OSU Service Description Duples */
+	while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+		struct osu_lang_string *f;
+		if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+			wpa_printf(MSG_DEBUG, "Invalid OSU Service "
+				   "Description");
+			break;
+		}
+		f = &prov->serv_desc[prov->serv_desc_count++];
+		os_memcpy(f->lang, pos2 + 1, 3);
+		os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+		pos2 += 1 + pos2[0];
+	}
+
+	wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
+		   MAC2STR(bss->bssid));
+	wpa_s->osu_prov_count++;
+}
+
+
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss;
+	struct wpabuf *prov_anqp;
+	const u8 *pos, *end;
+	u16 len;
+	const u8 *osu_ssid;
+	u8 osu_ssid_len;
+	u8 num_providers;
+
+	hs20_free_osu_prov(wpa_s);
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (bss->anqp == NULL)
+			continue;
+		prov_anqp = bss->anqp->hs20_osu_providers_list;
+		if (prov_anqp == NULL)
+			continue;
+		wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
+			   MACSTR, MAC2STR(bss->bssid));
+		wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
+				prov_anqp);
+		pos = wpabuf_head(prov_anqp);
+		end = pos + wpabuf_len(prov_anqp);
+
+		/* OSU SSID */
+		if (pos + 1 > end)
+			continue;
+		if (pos + 1 + pos[0] > end) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+				   "OSU SSID");
+			continue;
+		}
+		osu_ssid_len = *pos++;
+		if (osu_ssid_len > 32) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
+				   "Length %u", osu_ssid_len);
+			continue;
+		}
+		osu_ssid = pos;
+		pos += osu_ssid_len;
+
+		if (pos + 1 > end) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+				   "Number of OSU Providers");
+			continue;
+		}
+		num_providers = *pos++;
+		wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
+			   num_providers);
+
+		/* OSU Providers */
+		while (pos + 2 < end && num_providers > 0) {
+			num_providers--;
+			len = WPA_GET_LE16(pos);
+			pos += 2;
+			if (pos + len > end)
+				break;
+			hs20_osu_add_prov(wpa_s, bss, osu_ssid,
+					  osu_ssid_len, pos, len);
+			pos += len;
+		}
+
+		if (pos != end) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
+				   "extra data after OSU Providers",
+				   (int) (end - pos));
+		}
+	}
+
+	wpa_s->fetch_osu_icon_in_progress = 1;
+	hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
+				      struct wpa_scan_results *scan_res)
+{
+	wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
+	wpa_s->network_select = 0;
+	wpa_s->fetch_all_anqp = 1;
+	wpa_s->fetch_osu_info = 1;
+	wpa_s->fetch_osu_icon_in_progress = 0;
+
+	interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+			   "interface disabled");
+		return -1;
+	}
+
+	if (wpa_s->scanning) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+			   "scanning");
+		return -1;
+	}
+
+	if (wpa_s->conf->osu_dir == NULL) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+			   "osu_dir not configured");
+		return -1;
+	}
+
+	if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+			   "fetch in progress (%d, %d)",
+			   wpa_s->fetch_anqp_in_progress,
+			   wpa_s->network_select);
+		return -1;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
+	wpa_s->num_osu_scans = 0;
+	wpa_s->num_prov_found = 0;
+	hs20_start_osu_scan(wpa_s);
+
+	return 0;
+}
+
+
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->num_osu_scans++;
+	wpa_s->scan_req = MANUAL_SCAN_REQ;
+	wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
+	interworking_stop_fetch_anqp(wpa_s);
+	wpa_s->network_select = 0;
+	wpa_s->fetch_osu_info = 0;
+	wpa_s->fetch_osu_icon_in_progress = 0;
+}
+
+
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
+{
+	hs20_osu_icon_fetch_result(wpa_s, -1);
+	eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+	eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
+}
+
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+				      const char *url, u8 osu_method)
+{
+	if (url)
+		wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
+			osu_method, url);
+	else
+		wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
+}
+
+
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+				    u16 reauth_delay, const char *url)
+{
+	if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled");
+		return;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s",
+		code, reauth_delay, url);
+
+	if (code == HS20_DEAUTH_REASON_CODE_BSS) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to blacklist");
+		wpa_blacklist_add(wpa_s, wpa_s->bssid);
+		/* TODO: For now, disable full ESS since some drivers may not
+		 * support disabling per BSS. */
+		if (wpa_s->current_ssid) {
+			struct os_reltime now;
+			os_get_reltime(&now);
+			if (now.sec + reauth_delay <=
+			    wpa_s->current_ssid->disabled_until.sec)
+				return;
+			wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)",
+				   reauth_delay);
+			wpa_s->current_ssid->disabled_until.sec =
+				now.sec + reauth_delay;
+		}
+	}
+
+	if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) {
+		struct os_reltime now;
+		os_get_reltime(&now);
+		if (now.sec + reauth_delay <=
+		    wpa_s->current_ssid->disabled_until.sec)
+			return;
+		wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds",
+			   reauth_delay);
+		wpa_s->current_ssid->disabled_until.sec =
+			now.sec + reauth_delay;
+	}
+}
+
+
+void hs20_deinit(struct wpa_supplicant *wpa_s)
+{
+	eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+	hs20_free_osu_prov(wpa_s);
+}
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 1c8481b..06739f5 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -8,15 +8,33 @@
 #ifndef HS20_SUPPLICANT_H
 #define HS20_SUPPLICANT_H
 
-void wpas_hs20_add_indication(struct wpabuf *buf);
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id);
 
 int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
 		       const u8 *payload, size_t payload_len);
 struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
 				    size_t payload_len);
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+		       struct wpabuf *buf);
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
 				  const u8 *sa, const u8 *data, size_t slen);
 int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 		    struct wpa_bss *bss);
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s);
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+				      const char *url, u8 osu_method);
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+				    u16 reauth_delay, const char *url);
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
+void hs20_deinit(struct wpa_supplicant *wpa_s);
 
 #endif /* HS20_SUPPLICANT_H */
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 47ef35e..3083dd8 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -590,7 +590,7 @@
 		peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
 		return ibss_rsn_auth_init(ibss_rsn, peer);
 	} else {
-		os_get_time(&peer->own_auth_tx);
+		os_get_reltime(&peer->own_auth_tx);
 		eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL);
 	}
 
@@ -834,9 +834,9 @@
 	if (peer &&
 	    peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
 		if (peer->own_auth_tx.sec) {
-			struct os_time now, diff;
-			os_get_time(&now);
-			os_time_sub(&now, &peer->own_auth_tx, &diff);
+			struct os_reltime now, diff;
+			os_get_reltime(&now);
+			os_reltime_sub(&now, &peer->own_auth_tx, &diff);
 			if (diff.sec == 0 && diff.usec < 500000) {
 				wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX",
 					   (int) diff.usec);
diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h
index 3089283..67fae2d 100644
--- a/wpa_supplicant/ibss_rsn.h
+++ b/wpa_supplicant/ibss_rsn.h
@@ -40,7 +40,7 @@
 	struct wpa_state_machine *auth;
 	int authentication_status;
 
-	struct os_time own_auth_tx;
+	struct os_reltime own_auth_tx;
 };
 
 struct ibss_rsn {
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 36f75a1..19b6e38 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -1,6 +1,7 @@
 /*
  * Interworking (IEEE 802.11u)
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,12 +19,15 @@
 #include "eap_common/eap_defs.h"
 #include "eap_peer/eap.h"
 #include "eap_peer/eap_methods.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
 #include "config.h"
 #include "config_ssid.h"
 #include "bss.h"
 #include "scan.h"
 #include "notify.h"
+#include "driver_i.h"
 #include "gas_query.h"
 #include "hs20_supplicant.h"
 #include "interworking.h"
@@ -43,9 +47,28 @@
 
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
 static struct wpa_cred * interworking_credentials_available_realm(
-	struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+	int *excluded);
 static struct wpa_cred * interworking_credentials_available_3gpp(
-	struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+	int *excluded);
+
+
+static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b)
+{
+	if (a->priority > b->priority)
+		return 1;
+	if (a->priority < b->priority)
+		return -1;
+	if (a->provisioning_sp == NULL || b->provisioning_sp == NULL ||
+	    os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0)
+		return 0;
+	if (a->sp_priority < b->sp_priority)
+		return 1;
+	if (a->sp_priority > b->sp_priority)
+		return -1;
+	return 0;
+}
 
 
 static void interworking_reconnect(struct wpa_supplicant *wpa_s)
@@ -99,6 +122,9 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
+	wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
+		   " dialog_token=%u result=%d status_code=%u",
+		   MAC2STR(dst), dialog_token, result, status_code);
 	anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
 		     status_code);
 	interworking_next_anqp_fetch(wpa_s);
@@ -112,6 +138,8 @@
 	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
 		if (cred->roaming_consortium_len)
 			return 1;
+		if (cred->required_roaming_consortium_len)
+			return 1;
 	}
 	return 0;
 }
@@ -150,13 +178,45 @@
 	struct wpa_cred *cred;
 
 	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
-		if (cred->domain || cred->pcsc || cred->imsi)
+		if (cred->domain || cred->pcsc || cred->imsi ||
+		    cred->roaming_partner)
 			return 1;
 	}
 	return 0;
 }
 
 
+#ifdef CONFIG_HS20
+
+static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->min_dl_bandwidth_home ||
+		    cred->min_ul_bandwidth_home ||
+		    cred->min_dl_bandwidth_roaming ||
+		    cred->min_ul_bandwidth_roaming)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->num_req_conn_capab)
+			return 1;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
 static int additional_roaming_consortiums(struct wpa_bss *bss)
 {
 	const u8 *ie;
@@ -201,8 +261,10 @@
 		info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
 	if (all || cred_with_nai_realm(wpa_s))
 		info_ids[num_info_ids++] = ANQP_NAI_REALM;
-	if (all || cred_with_3gpp(wpa_s))
+	if (all || cred_with_3gpp(wpa_s)) {
 		info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
+		wpa_supplicant_scard_init(wpa_s, NULL);
+	}
 	if (all || cred_with_domain(wpa_s))
 		info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
 	wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
@@ -222,13 +284,17 @@
 		wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
 		wpabuf_put_u8(extra, 0); /* Reserved */
 		wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
-		if (all) {
+		if (all)
 			wpabuf_put_u8(extra,
 				      HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+		if (all || cred_with_min_backhaul(wpa_s))
 			wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+		if (all || cred_with_conn_capab(wpa_s))
 			wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+		if (all)
 			wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
-		}
+		if (all)
+			wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
 		gas_anqp_set_element_len(extra, len_pos);
 	}
 #endif /* CONFIG_HS20 */
@@ -242,6 +308,7 @@
 			    interworking_anqp_resp_cb, wpa_s);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+		wpabuf_free(buf);
 		ret = -1;
 		eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
 				       NULL);
@@ -249,7 +316,6 @@
 		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
 			   "%u", res);
 
-	wpabuf_free(buf);
 	return ret;
 }
 
@@ -516,12 +582,13 @@
 	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
 		return 0; /* method not supported */
 
-	if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
+	if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
+	    eap->method != EAP_TYPE_FAST) {
 		/* Only tunneled methods with username/password supported */
 		return 0;
 	}
 
-	if (eap->method == EAP_TYPE_PEAP) {
+	if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) {
 		if (eap->inner_method &&
 		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
 			return 0;
@@ -729,8 +796,8 @@
 		*pos++ = imsi[4];
 		*pos++ = imsi[5];
 	}
-	pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
-			   imsi[0], imsi[1], imsi[2]);
+	os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+		    imsi[0], imsi[1], imsi[2]);
 
 	return 0;
 }
@@ -747,12 +814,85 @@
 #endif /* INTERWORKING_3GPP */
 
 
+static int already_connected(struct wpa_supplicant *wpa_s,
+			     struct wpa_cred *cred, struct wpa_bss *bss)
+{
+	struct wpa_ssid *ssid, *sel_ssid;
+	struct wpa_bss *selected;
+
+	if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL)
+		return 0;
+
+	ssid = wpa_s->current_ssid;
+	if (ssid->parent_cred != cred)
+		return 0;
+
+	if (ssid->ssid_len != bss->ssid_len ||
+	    os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+		return 0;
+
+	sel_ssid = NULL;
+	selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid);
+	if (selected && sel_ssid && sel_ssid->priority > ssid->priority)
+		return 0; /* higher priority network in scan results */
+
+	return 1;
+}
+
+
+static void remove_duplicate_network(struct wpa_supplicant *wpa_s,
+				     struct wpa_cred *cred,
+				     struct wpa_bss *bss)
+{
+	struct wpa_ssid *ssid;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid->parent_cred != cred)
+			continue;
+		if (ssid->ssid_len != bss->ssid_len ||
+		    os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+			continue;
+
+		break;
+	}
+
+	if (ssid == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential");
+
+	if (ssid == wpa_s->current_ssid) {
+		wpa_sm_set_config(wpa_s->wpa, NULL);
+		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+	}
+
+	wpas_notify_network_removed(wpa_s, ssid);
+	wpa_config_remove_network(wpa_s->conf, ssid->id);
+}
+
+
 static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
 					struct wpa_ssid *ssid)
 {
-	if (wpa_config_set(ssid, "key_mgmt",
-			   wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
-			   "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0)
+	const char *key_mgmt = NULL;
+#ifdef CONFIG_IEEE80211R
+	int res;
+	struct wpa_driver_capa capa;
+
+	res = wpa_drv_get_capa(wpa_s, &capa);
+	if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+		key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+			"WPA-EAP WPA-EAP-SHA256 FT-EAP" :
+			"WPA-EAP FT-EAP";
+	}
+#endif /* CONFIG_IEEE80211R */
+
+	if (!key_mgmt)
+		key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+			"WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
+	if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
 		return -1;
 	if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
 		return -1;
@@ -768,7 +908,6 @@
 {
 #ifdef INTERWORKING_3GPP
 	struct wpa_ssid *ssid;
-	const u8 *ie;
 	int eap_type;
 	int res;
 	char prefix;
@@ -776,12 +915,17 @@
 	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
 		return -1;
 
-	ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
-	if (ie == NULL)
-		return -1;
 	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
 		   MAC2STR(bss->bssid));
 
+	if (already_connected(wpa_s, cred, bss)) {
+		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+			MAC2STR(bss->bssid));
+		return 0;
+	}
+
+	remove_duplicate_network(wpa_s, cred, bss);
+
 	ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL)
 		return -1;
@@ -791,11 +935,12 @@
 	wpa_config_set_network_defaults(ssid);
 	ssid->priority = cred->priority;
 	ssid->temporary = 1;
-	ssid->ssid = os_zalloc(ie[1] + 1);
+	ssid->ssid = os_zalloc(bss->ssid_len + 1);
 	if (ssid->ssid == NULL)
 		goto fail;
-	os_memcpy(ssid->ssid, ie + 2, ie[1]);
-	ssid->ssid_len = ie[1];
+	os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+	ssid->ssid_len = bss->ssid_len;
+	ssid->eap.sim_num = cred->sim_num;
 
 	if (interworking_set_hs20_params(wpa_s, ssid) < 0)
 		goto fail;
@@ -851,10 +996,7 @@
 			goto fail;
 	}
 
-	if (cred->password && cred->password[0] &&
-	    wpa_config_set_quoted(ssid, "password", cred->password) < 0)
-		goto fail;
-
+	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
 	interworking_reconnect(wpa_s);
 
@@ -944,6 +1086,27 @@
 }
 
 
+static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+	const u8 *ie;
+
+	if (cred->required_roaming_consortium_len == 0)
+		return 0;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+	if (ie == NULL &&
+	    (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+		return 1;
+
+	return !roaming_consortium_match(ie,
+					 bss->anqp ?
+					 bss->anqp->roaming_consortium : NULL,
+					 cred->required_roaming_consortium,
+					 cred->required_roaming_consortium_len);
+}
+
+
 static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
 {
 	size_t i;
@@ -962,11 +1125,164 @@
 }
 
 
+static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
+				   struct wpa_cred *cred, struct wpa_bss *bss)
+{
+	int res;
+	unsigned int dl_bandwidth, ul_bandwidth;
+	const u8 *wan;
+	u8 wan_info, dl_load, ul_load;
+	u16 lmd;
+	u32 ul_speed, dl_speed;
+
+	if (!cred->min_dl_bandwidth_home &&
+	    !cred->min_ul_bandwidth_home &&
+	    !cred->min_dl_bandwidth_roaming &&
+	    !cred->min_ul_bandwidth_roaming)
+		return 0; /* No bandwidth constraint specified */
+
+	if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
+		return 0; /* No WAN Metrics known - ignore constraint */
+
+	wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
+	wan_info = wan[0];
+	if (wan_info & BIT(3))
+		return 1; /* WAN link at capacity */
+	lmd = WPA_GET_LE16(wan + 11);
+	if (lmd == 0)
+		return 0; /* Downlink/Uplink Load was not measured */
+	dl_speed = WPA_GET_LE32(wan + 1);
+	ul_speed = WPA_GET_LE32(wan + 5);
+	dl_load = wan[9];
+	ul_load = wan[10];
+
+	if (dl_speed >= 0xffffff)
+		dl_bandwidth = dl_speed / 255 * (255 - dl_load);
+	else
+		dl_bandwidth = dl_speed * (255 - dl_load) / 255;
+
+	if (ul_speed >= 0xffffff)
+		ul_bandwidth = ul_speed / 255 * (255 - ul_load);
+	else
+		ul_bandwidth = ul_speed * (255 - ul_load) / 255;
+
+	res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+					bss->anqp->domain_name : NULL);
+	if (res > 0) {
+		if (cred->min_dl_bandwidth_home > dl_bandwidth)
+			return 1;
+		if (cred->min_ul_bandwidth_home > ul_bandwidth)
+			return 1;
+	} else {
+		if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
+			return 1;
+		if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
+				  struct wpa_cred *cred, struct wpa_bss *bss)
+{
+	const u8 *ie;
+	int res;
+
+	if (!cred->max_bss_load)
+		return 0; /* No BSS Load constraint specified */
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
+	if (ie == NULL || ie[1] < 3)
+		return 0; /* No BSS Load advertised */
+
+	res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+					bss->anqp->domain_name : NULL);
+	if (res <= 0)
+		return 0; /* Not a home network */
+
+	return ie[4] > cred->max_bss_load;
+}
+
+
+static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
+{
+	while (pos + 4 <= end) {
+		if (pos[0] == proto && pos[3] == 1 /* Open */)
+			return 1;
+		pos += 4;
+	}
+
+	return 0;
+}
+
+
+static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
+				u16 port)
+{
+	while (pos + 4 <= end) {
+		if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
+		    pos[3] == 1 /* Open */)
+			return 1;
+		pos += 4;
+	}
+
+	return 0;
+}
+
+
+static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
+				   struct wpa_cred *cred, struct wpa_bss *bss)
+{
+	int res;
+	const u8 *capab, *end;
+	unsigned int i, j;
+	int *ports;
+
+	if (!cred->num_req_conn_capab)
+		return 0; /* No connection capability constraint specified */
+
+	if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
+		return 0; /* No Connection Capability known - ignore constraint
+			   */
+
+	res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+					bss->anqp->domain_name : NULL);
+	if (res > 0)
+		return 0; /* No constraint in home network */
+
+	capab = wpabuf_head(bss->anqp->hs20_connection_capability);
+	end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
+
+	for (i = 0; i < cred->num_req_conn_capab; i++) {
+		ports = cred->req_conn_capab_port[i];
+		if (!ports) {
+			if (!has_proto_match(capab, end,
+					     cred->req_conn_capab_proto[i]))
+				return 1;
+		} else {
+			for (j = 0; ports[j] > -1; j++) {
+				if (!has_proto_port_match(
+					    capab, end,
+					    cred->req_conn_capab_proto[i],
+					    ports[j]))
+					return 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
 static struct wpa_cred * interworking_credentials_available_roaming_consortium(
-	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+	int *excluded)
 {
 	struct wpa_cred *cred, *selected = NULL;
 	const u8 *ie;
+	int is_excluded = 0;
 
 	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
 
@@ -989,14 +1305,33 @@
 					      cred->roaming_consortium_len))
 			continue;
 
-		if (cred_excluded_ssid(cred, bss))
+		if (cred_no_required_oi_match(cred, bss))
 			continue;
-
-		if (selected == NULL ||
-		    selected->priority < cred->priority)
-			selected = cred;
+		if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+			continue;
+		if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
+			continue;
+		if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
+			continue;
+		if (cred_excluded_ssid(cred, bss)) {
+			if (excluded == NULL)
+				continue;
+			if (selected == NULL) {
+				selected = cred;
+				is_excluded = 1;
+			}
+		} else {
+			if (selected == NULL || is_excluded ||
+			    cred_prio_cmp(selected, cred) < 0) {
+				selected = cred;
+				is_excluded = 0;
+			}
+		}
 	}
 
+	if (excluded)
+		*excluded = is_excluded;
+
 	return selected;
 }
 
@@ -1100,19 +1435,34 @@
 	    wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
 		return -1;
 
+	if (cred->domain_suffix_match && cred->domain_suffix_match[0] &&
+	    wpa_config_set_quoted(ssid, "domain_suffix_match",
+				  cred->domain_suffix_match) < 0)
+		return -1;
+
+	ssid->eap.ocsp = cred->ocsp;
+
 	return 0;
 }
 
 
 static int interworking_connect_roaming_consortium(
 	struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
-	struct wpa_bss *bss, const u8 *ssid_ie)
+	struct wpa_bss *bss)
 {
 	struct wpa_ssid *ssid;
 
 	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
 		   "roaming consortium match", MAC2STR(bss->bssid));
 
+	if (already_connected(wpa_s, cred, bss)) {
+		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+			MAC2STR(bss->bssid));
+		return 0;
+	}
+
+	remove_duplicate_network(wpa_s, cred, bss);
+
 	ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL)
 		return -1;
@@ -1121,11 +1471,11 @@
 	wpa_config_set_network_defaults(ssid);
 	ssid->priority = cred->priority;
 	ssid->temporary = 1;
-	ssid->ssid = os_zalloc(ssid_ie[1] + 1);
+	ssid->ssid = os_zalloc(bss->ssid_len + 1);
 	if (ssid->ssid == NULL)
 		goto fail;
-	os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]);
-	ssid->ssid_len = ssid_ie[1];
+	os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+	ssid->ssid_len = bss->ssid_len;
 
 	if (interworking_set_hs20_params(wpa_s, ssid) < 0)
 		goto fail;
@@ -1142,6 +1492,7 @@
 		    cred->eap_method->method == EAP_TYPE_TTLS) < 0)
 		goto fail;
 
+	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
 	interworking_reconnect(wpa_s);
 
@@ -1154,7 +1505,8 @@
 }
 
 
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
+				       struct wpa_bss *bss, int allow_excluded)
 {
 	struct wpa_cred *cred, *cred_rc, *cred_3gpp;
 	struct wpa_ssid *ssid;
@@ -1162,17 +1514,22 @@
 	struct nai_realm_eap *eap = NULL;
 	u16 count, i;
 	char buf[100];
-	const u8 *ie;
+	int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
+	const char *name;
 
 	if (wpa_s->conf->cred == NULL || bss == NULL)
 		return -1;
-	ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
-	if (ie == NULL || ie[1] == 0) {
-		wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
+	if (disallowed_bssid(wpa_s, bss->bssid) ||
+	    disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+		wpa_printf(MSG_DEBUG, "Interworking: Reject connection to disallowed BSS "
 			   MACSTR, MAC2STR(bss->bssid));
 		return -1;
 	}
 
+	wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
+		   " for connection (allow_excluded=%d)",
+		   MAC2STR(bss->bssid), allow_excluded);
+
 	if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
 		/*
 		 * We currently support only HS 2.0 networks and those are
@@ -1183,35 +1540,80 @@
 		return -1;
 	}
 
-	cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
-									bss);
+	cred_rc = interworking_credentials_available_roaming_consortium(
+		wpa_s, bss, 0, excl);
 	if (cred_rc) {
 		wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
-			   "consortium matching credential priority %d",
-			   cred_rc->priority);
+			   "consortium matching credential priority %d "
+			   "sp_priority %d",
+			   cred_rc->priority, cred_rc->sp_priority);
+		if (allow_excluded && excl && !(*excl))
+			excl = NULL;
 	}
 
-	cred = interworking_credentials_available_realm(wpa_s, bss);
+	cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
 	if (cred) {
 		wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
-			   "matching credential priority %d",
-			   cred->priority);
+			   "matching credential priority %d sp_priority %d",
+			   cred->priority, cred->sp_priority);
+		if (allow_excluded && excl && !(*excl))
+			excl = NULL;
 	}
 
-	cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+	cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
+							    excl);
 	if (cred_3gpp) {
 		wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
-			   "credential priority %d", cred_3gpp->priority);
+			   "credential priority %d sp_priority %d",
+			   cred_3gpp->priority, cred_3gpp->sp_priority);
+		if (allow_excluded && excl && !(*excl))
+			excl = NULL;
+	}
+
+	if (!cred_rc && !cred && !cred_3gpp) {
+		wpa_printf(MSG_DEBUG, "Interworking: No full credential matches - consider options without BW(etc.) limits");
+		cred_rc = interworking_credentials_available_roaming_consortium(
+			wpa_s, bss, 1, excl);
+		if (cred_rc) {
+			wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
+				   "consortium matching credential priority %d "
+				   "sp_priority %d (ignore BW)",
+				   cred_rc->priority, cred_rc->sp_priority);
+			if (allow_excluded && excl && !(*excl))
+				excl = NULL;
+		}
+
+		cred = interworking_credentials_available_realm(wpa_s, bss, 1,
+								excl);
+		if (cred) {
+			wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm "
+				   "list matching credential priority %d "
+				   "sp_priority %d (ignore BW)",
+				   cred->priority, cred->sp_priority);
+			if (allow_excluded && excl && !(*excl))
+				excl = NULL;
+		}
+
+		cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
+								    1, excl);
+		if (cred_3gpp) {
+			wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP "
+				   "matching credential priority %d "
+				   "sp_priority %d (ignore BW)",
+				   cred_3gpp->priority, cred_3gpp->sp_priority);
+			if (allow_excluded && excl && !(*excl))
+				excl = NULL;
+		}
 	}
 
 	if (cred_rc &&
-	    (cred == NULL || cred_rc->priority >= cred->priority) &&
-	    (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
+	    (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
+	    (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
 		return interworking_connect_roaming_consortium(wpa_s, cred_rc,
-							       bss, ie);
+							       bss);
 
 	if (cred_3gpp &&
-	    (cred == NULL || cred_3gpp->priority >= cred->priority)) {
+	    (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
 		return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
 	}
 
@@ -1248,6 +1650,15 @@
 	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
 		   MAC2STR(bss->bssid));
 
+	if (already_connected(wpa_s, cred, bss)) {
+		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+			MAC2STR(bss->bssid));
+		nai_realm_free(realm, count);
+		return 0;
+	}
+
+	remove_duplicate_network(wpa_s, cred, bss);
+
 	ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL) {
 		nai_realm_free(realm, count);
@@ -1258,11 +1669,11 @@
 	wpa_config_set_network_defaults(ssid);
 	ssid->priority = cred->priority;
 	ssid->temporary = 1;
-	ssid->ssid = os_zalloc(ie[1] + 1);
+	ssid->ssid = os_zalloc(bss->ssid_len + 1);
 	if (ssid->ssid == NULL)
 		goto fail;
-	os_memcpy(ssid->ssid, ie + 2, ie[1]);
-	ssid->ssid_len = ie[1];
+	os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+	ssid->ssid_len = bss->ssid_len;
 
 	if (interworking_set_hs20_params(wpa_s, ssid) < 0)
 		goto fail;
@@ -1311,11 +1722,19 @@
 		}
 		break;
 	case EAP_TYPE_PEAP:
-		os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
-			    eap_get_name(EAP_VENDOR_IETF,
-					 eap->inner_method ?
-					 eap->inner_method :
-					 EAP_TYPE_MSCHAPV2));
+	case EAP_TYPE_FAST:
+		if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"",
+				   0) < 0)
+			goto fail;
+		if (wpa_config_set(ssid, "pac_file",
+				   "\"blob://pac_interworking\"", 0) < 0)
+			goto fail;
+		name = eap_get_name(EAP_VENDOR_IETF,
+				    eap->inner_method ? eap->inner_method :
+				    EAP_TYPE_MSCHAPV2);
+		if (name == NULL)
+			goto fail;
+		os_snprintf(buf, sizeof(buf), "\"auth=%s\"", name);
 		if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
 			goto fail;
 		break;
@@ -1329,6 +1748,7 @@
 
 	nai_realm_free(realm, count);
 
+	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
 	interworking_reconnect(wpa_s);
 
@@ -1342,17 +1762,67 @@
 }
 
 
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	return interworking_connect_helper(wpa_s, bss, 1);
+}
+
+
+#ifdef PCSC_FUNCS
+static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s)
+{
+	size_t len;
+
+	if (wpa_s->imsi[0] && wpa_s->mnc_len)
+		return 0;
+
+	len = sizeof(wpa_s->imsi) - 1;
+	if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
+		scard_deinit(wpa_s->scard);
+		wpa_s->scard = NULL;
+		wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
+		return -1;
+	}
+	wpa_s->imsi[len] = '\0';
+	wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
+	wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
+		   wpa_s->imsi, wpa_s->mnc_len);
+
+	return 0;
+}
+#endif /* PCSC_FUNCS */
+
+
 static struct wpa_cred * interworking_credentials_available_3gpp(
-	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+	int *excluded)
 {
 	struct wpa_cred *selected = NULL;
 #ifdef INTERWORKING_3GPP
 	struct wpa_cred *cred;
 	int ret;
+	int is_excluded = 0;
 
 	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
 		return NULL;
 
+#ifdef CONFIG_EAP_PROXY
+	if (!wpa_s->imsi[0]) {
+		size_t len;
+		wpa_printf(MSG_DEBUG, "Interworking: IMSI not available - try to read again through eap_proxy");
+		wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+							     wpa_s->imsi,
+							     &len);
+		if (wpa_s->mnc_len > 0) {
+			wpa_s->imsi[len] = '\0';
+			wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
+				   wpa_s->imsi, wpa_s->mnc_len);
+		} else {
+			wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+		}
+	}
+#endif /* CONFIG_EAP_PROXY */
+
 	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
 		char *sep;
 		const char *imsi;
@@ -1361,8 +1831,9 @@
 		size_t msin_len;
 
 #ifdef PCSC_FUNCS
-		if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
-		    wpa_s->imsi[0]) {
+		if (cred->pcsc && wpa_s->scard) {
+			if (interworking_pcsc_read_imsi(wpa_s) < 0)
+				continue;
 			imsi = wpa_s->imsi;
 			mnc_len = wpa_s->mnc_len;
 			goto compare;
@@ -1377,7 +1848,8 @@
 #endif /* CONFIG_EAP_PROXY */
 
 		if (cred->imsi == NULL || !cred->imsi[0] ||
-		    cred->milenage == NULL || !cred->milenage[0])
+		    (!wpa_s->conf->external_sim &&
+		     (cred->milenage == NULL || !cred->milenage[0])))
 			continue;
 
 		sep = os_strchr(cred->imsi, '-');
@@ -1402,24 +1874,49 @@
 		ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
 		wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
 		if (ret) {
-			if (cred_excluded_ssid(cred, bss))
+			if (cred_no_required_oi_match(cred, bss))
 				continue;
-			if (selected == NULL ||
-			    selected->priority < cred->priority)
-				selected = cred;
+			if (!ignore_bw &&
+			    cred_below_min_backhaul(wpa_s, cred, bss))
+				continue;
+			if (!ignore_bw &&
+			    cred_over_max_bss_load(wpa_s, cred, bss))
+				continue;
+			if (!ignore_bw &&
+			    cred_conn_capab_missing(wpa_s, cred, bss))
+				continue;
+			if (cred_excluded_ssid(cred, bss)) {
+				if (excluded == NULL)
+					continue;
+				if (selected == NULL) {
+					selected = cred;
+					is_excluded = 1;
+				}
+			} else {
+				if (selected == NULL || is_excluded ||
+				    cred_prio_cmp(selected, cred) < 0) {
+					selected = cred;
+					is_excluded = 0;
+				}
+			}
 		}
 	}
+
+	if (excluded)
+		*excluded = is_excluded;
 #endif /* INTERWORKING_3GPP */
 	return selected;
 }
 
 
 static struct wpa_cred * interworking_credentials_available_realm(
-	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+	int *excluded)
 {
 	struct wpa_cred *cred, *selected = NULL;
 	struct nai_realm *realm;
 	u16 count, i;
+	int is_excluded = 0;
 
 	if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
 		return NULL;
@@ -1444,11 +1941,32 @@
 			if (!nai_realm_match(&realm[i], cred->realm))
 				continue;
 			if (nai_realm_find_eap(cred, &realm[i])) {
-				if (cred_excluded_ssid(cred, bss))
+				if (cred_no_required_oi_match(cred, bss))
 					continue;
-				if (selected == NULL ||
-				    selected->priority < cred->priority)
-					selected = cred;
+				if (!ignore_bw &&
+				    cred_below_min_backhaul(wpa_s, cred, bss))
+					continue;
+				if (!ignore_bw &&
+				    cred_over_max_bss_load(wpa_s, cred, bss))
+					continue;
+				if (!ignore_bw &&
+				    cred_conn_capab_missing(wpa_s, cred, bss))
+					continue;
+				if (cred_excluded_ssid(cred, bss)) {
+					if (excluded == NULL)
+						continue;
+					if (selected == NULL) {
+						selected = cred;
+						is_excluded = 1;
+					}
+				} else {
+					if (selected == NULL || is_excluded ||
+					    cred_prio_cmp(selected, cred) < 0)
+					{
+						selected = cred;
+						is_excluded = 0;
+					}
+				}
 				break;
 			}
 		}
@@ -1456,35 +1974,77 @@
 
 	nai_realm_free(realm, count);
 
+	if (excluded)
+		*excluded = is_excluded;
+
 	return selected;
 }
 
 
-static struct wpa_cred * interworking_credentials_available(
-	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static struct wpa_cred * interworking_credentials_available_helper(
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+	int *excluded)
 {
 	struct wpa_cred *cred, *cred2;
+	int excluded1, excluded2;
 
-	cred = interworking_credentials_available_realm(wpa_s, bss);
-	cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
-	if (cred && cred2 && cred2->priority >= cred->priority)
-		cred = cred2;
-	if (!cred)
-		cred = cred2;
+	if (disallowed_bssid(wpa_s, bss->bssid) ||
+	    disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+		wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS "
+			   MACSTR, MAC2STR(bss->bssid));
+		return NULL;
+	}
 
-	cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
-								      bss);
-	if (cred && cred2 && cred2->priority >= cred->priority)
+	cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
+							&excluded1);
+	cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
+							&excluded2);
+	if (cred && cred2 &&
+	    (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
 		cred = cred2;
-	if (!cred)
+		excluded1 = excluded2;
+	}
+	if (!cred) {
 		cred = cred2;
+		excluded1 = excluded2;
+	}
 
+	cred2 = interworking_credentials_available_roaming_consortium(
+		wpa_s, bss, ignore_bw, &excluded2);
+	if (cred && cred2 &&
+	    (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
+		cred = cred2;
+		excluded1 = excluded2;
+	}
+	if (!cred) {
+		cred = cred2;
+		excluded1 = excluded2;
+	}
+
+	if (excluded)
+		*excluded = excluded1;
 	return cred;
 }
 
 
-static int domain_name_list_contains(struct wpabuf *domain_names,
-				     const char *domain)
+static struct wpa_cred * interworking_credentials_available(
+	struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
+{
+	struct wpa_cred *cred;
+
+	if (excluded)
+		*excluded = 0;
+	cred = interworking_credentials_available_helper(wpa_s, bss, 0,
+							 excluded);
+	if (cred)
+		return cred;
+	return interworking_credentials_available_helper(wpa_s, bss, 1,
+							 excluded);
+}
+
+
+int domain_name_list_contains(struct wpabuf *domain_names,
+			      const char *domain, int exact_match)
 {
 	const u8 *pos, *end;
 	size_t len;
@@ -1502,6 +2062,12 @@
 		if (pos[0] == len &&
 		    os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
 			return 1;
+		if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
+			const char *ap = (const char *) (pos + 1);
+			int offset = pos[0] - len;
+			if (os_strncasecmp(domain, ap + offset, len) == 0)
+				return 1;
+		}
 
 		pos += 1 + pos[0];
 	}
@@ -1514,6 +2080,8 @@
 			      struct wpa_cred *cred,
 			      struct wpabuf *domain_names)
 {
+	size_t i;
+	int ret = -1;
 #ifdef INTERWORKING_3GPP
 	char nai[100], *realm;
 
@@ -1521,13 +2089,20 @@
 	int mnc_len = 0;
 	if (cred->imsi)
 		imsi = cred->imsi;
-#ifdef CONFIG_PCSC
-	else if (cred->pcsc && wpa_s->conf->pcsc_reader &&
-		 wpa_s->scard && wpa_s->imsi[0]) {
+#ifdef PCSC_FUNCS
+	else if (cred->pcsc && wpa_s->scard) {
+		if (interworking_pcsc_read_imsi(wpa_s) < 0)
+			return -1;
 		imsi = wpa_s->imsi;
 		mnc_len = wpa_s->mnc_len;
 	}
-#endif /* CONFIG_PCSC */
+#endif /* PCSC_FUNCS */
+#ifdef CONFIG_EAP_PROXY
+	else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
+		imsi = wpa_s->imsi;
+		mnc_len = wpa_s->mnc_len;
+	}
+#endif /* CONFIG_EAP_PROXY */
 	if (domain_names &&
 	    imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
 		realm = os_strchr(nai, '@');
@@ -1536,18 +2111,22 @@
 		wpa_printf(MSG_DEBUG, "Interworking: Search for match "
 			   "with SIM/USIM domain %s", realm);
 		if (realm &&
-		    domain_name_list_contains(domain_names, realm))
+		    domain_name_list_contains(domain_names, realm, 1))
 			return 1;
+		if (realm)
+			ret = 0;
 	}
 #endif /* INTERWORKING_3GPP */
 
 	if (domain_names == NULL || cred->domain == NULL)
-		return 0;
+		return ret;
 
-	wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
-		   "home SP FQDN %s", cred->domain);
-	if (domain_name_list_contains(domain_names, cred->domain))
-		return 1;
+	for (i = 0; i < cred->num_domain; i++) {
+		wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
+			   "home SP FQDN %s", cred->domain[i]);
+		if (domain_name_list_contains(domain_names, cred->domain[i], 1))
+			return 1;
+	}
 
 	return 0;
 }
@@ -1597,19 +2176,127 @@
 }
 
 
+static int roaming_partner_match(struct wpa_supplicant *wpa_s,
+				 struct roaming_partner *partner,
+				 struct wpabuf *domain_names)
+{
+	wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'",
+		   partner->fqdn, partner->exact_match, partner->priority,
+		   partner->country);
+	wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names",
+			  wpabuf_head(domain_names),
+			  wpabuf_len(domain_names));
+	if (!domain_name_list_contains(domain_names, partner->fqdn,
+				       partner->exact_match))
+		return 0;
+	/* TODO: match Country */
+	return 1;
+}
+
+
+static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+		       struct wpa_bss *bss)
+{
+	size_t i;
+
+	if (bss->anqp == NULL || bss->anqp->domain_name == NULL) {
+		wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128");
+		return 128; /* cannot check preference with domain name */
+	}
+
+	if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
+	{
+		wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
+		return 0; /* max preference for home SP network */
+	}
+
+	for (i = 0; i < cred->num_roaming_partner; i++) {
+		if (roaming_partner_match(wpa_s, &cred->roaming_partner[i],
+					  bss->anqp->domain_name)) {
+			wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u",
+				   cred->roaming_partner[i].priority);
+			return cred->roaming_partner[i].priority;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128");
+	return 128;
+}
+
+
+static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
+						  struct wpa_bss *selected,
+						  struct wpa_cred *cred)
+{
+	struct wpa_bss *bss;
+	u8 best_prio, prio;
+	struct wpa_cred *cred2;
+
+	/*
+	 * Check if any other BSS is operated by a more preferred roaming
+	 * partner.
+	 */
+
+	best_prio = roaming_prio(wpa_s, cred, selected);
+	wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
+		   MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
+		   cred->id);
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (bss == selected)
+			continue;
+		cred2 = interworking_credentials_available(wpa_s, bss, NULL);
+		if (!cred2)
+			continue;
+		if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
+			continue;
+		prio = roaming_prio(wpa_s, cred2, bss);
+		wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS "
+			   MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid),
+			   cred2->id);
+		if (prio < best_prio) {
+			int bh1, bh2, load1, load2, conn1, conn2;
+			bh1 = cred_below_min_backhaul(wpa_s, cred, selected);
+			load1 = cred_over_max_bss_load(wpa_s, cred, selected);
+			conn1 = cred_conn_capab_missing(wpa_s, cred, selected);
+			bh2 = cred_below_min_backhaul(wpa_s, cred2, bss);
+			load2 = cred_over_max_bss_load(wpa_s, cred2, bss);
+			conn2 = cred_conn_capab_missing(wpa_s, cred2, bss);
+			wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d  new: %d %d %d",
+				   bh1, load1, conn1, bh2, load2, conn2);
+			if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) {
+				wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid));
+				best_prio = prio;
+				selected = bss;
+			}
+		}
+	}
+
+	return selected;
+}
+
+
 static void interworking_select_network(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
-	int selected_prio = -999999, selected_home_prio = -999999;
+	struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
 	unsigned int count = 0;
 	const char *type;
 	int res;
-	struct wpa_cred *cred;
+	struct wpa_cred *cred, *selected_cred = NULL;
+	struct wpa_cred *selected_home_cred = NULL;
+	struct wpa_cred *selected2_cred = NULL;
+	struct wpa_cred *selected2_home_cred = NULL;
 
 	wpa_s->network_select = 0;
 
+	wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
+		   wpa_s->auto_select);
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		cred = interworking_credentials_available(wpa_s, bss);
+		int excluded = 0;
+		int bh, bss_load, conn_capab;
+		cred = interworking_credentials_available(wpa_s, bss,
+							  &excluded);
 		if (!cred)
 			continue;
 		if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
@@ -1622,7 +2309,8 @@
 				   "RSN", MAC2STR(bss->bssid));
 			continue;
 		}
-		count++;
+		if (!excluded)
+			count++;
 		res = interworking_home_sp(wpa_s, bss->anqp ?
 					   bss->anqp->domain_name : NULL);
 		if (res > 0)
@@ -1631,29 +2319,75 @@
 			type = "roaming";
 		else
 			type = "unknown";
-		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
-			MAC2STR(bss->bssid), type);
+		bh = cred_below_min_backhaul(wpa_s, cred, bss);
+		bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
+		conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
+		wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
+			excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
+			MAC2STR(bss->bssid), type,
+			bh ? " below_min_backhaul=1" : "",
+			bss_load ? " over_max_bss_load=1" : "",
+			conn_capab ? " conn_capab_missing=1" : "",
+			cred->id, cred->priority, cred->sp_priority);
+		if (excluded)
+			continue;
 		if (wpa_s->auto_select ||
 		    (wpa_s->conf->auto_interworking &&
 		     wpa_s->auto_network_select)) {
-			if (selected == NULL ||
-			    cred->priority > selected_prio) {
-				selected = bss;
-				selected_prio = cred->priority;
-			}
-			if (res > 0 &&
-			    (selected_home == NULL ||
-			     cred->priority > selected_home_prio)) {
-				selected_home = bss;
-				selected_home_prio = cred->priority;
+			if (bh || bss_load || conn_capab) {
+				if (selected2_cred == NULL ||
+				    cred_prio_cmp(cred, selected2_cred) > 0) {
+					wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2");
+					selected2 = bss;
+					selected2_cred = cred;
+				}
+				if (res > 0 &&
+				    (selected2_home_cred == NULL ||
+				     cred_prio_cmp(cred, selected2_home_cred) >
+				     0)) {
+					wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home");
+					selected2_home = bss;
+					selected2_home_cred = cred;
+				}
+			} else {
+				if (selected_cred == NULL ||
+				    cred_prio_cmp(cred, selected_cred) > 0) {
+					wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
+					selected = bss;
+					selected_cred = cred;
+				}
+				if (res > 0 &&
+				    (selected_home_cred == NULL ||
+				     cred_prio_cmp(cred, selected_home_cred) >
+				     0)) {
+					wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
+					selected_home = bss;
+					selected_home_cred = cred;
+				}
 			}
 		}
 	}
 
 	if (selected_home && selected_home != selected &&
-	    selected_home_prio >= selected_prio) {
+	    selected_home_cred &&
+	    (selected_cred == NULL ||
+	     cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) {
 		/* Prefer network operated by the Home SP */
+		wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home");
 		selected = selected_home;
+		selected_cred = selected_home_cred;
+	}
+
+	if (!selected) {
+		if (selected2_home) {
+			wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected");
+			selected = selected2_home;
+			selected_cred = selected2_home_cred;
+		} else if (selected2) {
+			wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected");
+			selected = selected2;
+			selected_cred = selected2_cred;
+		}
 	}
 
 	if (count == 0) {
@@ -1665,9 +2399,10 @@
 		if (interworking_find_network_match(wpa_s)) {
 			wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
 				   "match for enabled network configurations");
-			if (wpa_s->auto_select)
+			if (wpa_s->auto_select) {
 				interworking_reconnect(wpa_s);
-			return;
+				return;
+			}
 		}
 
 		if (wpa_s->auto_network_select) {
@@ -1682,8 +2417,18 @@
 			"with matching credentials found");
 	}
 
-	if (selected)
+	if (selected) {
+		wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
+			   MAC2STR(selected->bssid));
+		selected = pick_best_roaming_partner(wpa_s, selected,
+						     selected_cred);
+		wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
+			   " (after best roaming partner selection)",
+			   MAC2STR(selected->bssid));
+		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
+			MAC2STR(selected->bssid));
 		interworking_connect(wpa_s, selected);
+	}
 }
 
 
@@ -1730,8 +2475,21 @@
 	int found = 0;
 	const u8 *ie;
 
-	if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
+	wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
+		   "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
+		   wpa_s->fetch_anqp_in_progress,
+		   wpa_s->fetch_osu_icon_in_progress);
+
+	if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
+		wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
 		return;
+	}
+
+	if (wpa_s->fetch_osu_icon_in_progress) {
+		wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
+		hs20_next_osu_icon(wpa_s);
+		return;
+	}
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -1739,6 +2497,9 @@
 		ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
 		if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
 			continue; /* AP does not support Interworking */
+		if (disallowed_bssid(wpa_s, bss->bssid) ||
+		    disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len))
+			continue; /* Disallowed BSS */
 
 		if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
 			if (bss->anqp == NULL) {
@@ -1762,6 +2523,17 @@
 	}
 
 	if (found == 0) {
+		if (wpa_s->fetch_osu_info) {
+			if (wpa_s->num_prov_found == 0 &&
+			    wpa_s->num_osu_scans < 3) {
+				wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
+				hs20_start_osu_scan(wpa_s);
+				return;
+			}
+			wpa_printf(MSG_DEBUG, "Interworking: Next icon");
+			hs20_osu_icon_fetch(wpa_s);
+			return;
+		}
 		wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
 		wpa_s->fetch_anqp_in_progress = 0;
 		if (wpa_s->network_select)
@@ -1789,6 +2561,7 @@
 
 	wpa_s->network_select = 0;
 	wpa_s->fetch_all_anqp = 1;
+	wpa_s->fetch_osu_info = 0;
 
 	interworking_start_fetch_anqp(wpa_s);
 
@@ -1806,9 +2579,10 @@
 
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-		  u16 info_ids[], size_t num_ids)
+		  u16 info_ids[], size_t num_ids, u32 subtypes)
 {
 	struct wpabuf *buf;
+	struct wpabuf *hs20_buf = NULL;
 	int ret = 0;
 	int freq;
 	struct wpa_bss *bss;
@@ -1826,19 +2600,29 @@
 	wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
 		   MAC2STR(dst), (unsigned int) num_ids);
 
-	buf = anqp_build_req(info_ids, num_ids, NULL);
+#ifdef CONFIG_HS20
+	if (subtypes != 0) {
+		hs20_buf = wpabuf_alloc(100);
+		if (hs20_buf == NULL)
+			return -1;
+		hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
+	}
+#endif /* CONFIG_HS20 */
+
+	buf = anqp_build_req(info_ids, num_ids, hs20_buf);
+	wpabuf_free(hs20_buf);
 	if (buf == NULL)
 		return -1;
 
 	res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+		wpabuf_free(buf);
 		ret = -1;
 	} else
 		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
 			   "%u", res);
 
-	wpabuf_free(buf);
 	return ret;
 }
 
@@ -1986,14 +2770,22 @@
 	u16 slen;
 	struct wpa_bss *bss = NULL, *tmp;
 
-	if (result != GAS_QUERY_SUCCESS)
+	wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
+		   " dialog_token=%u result=%d status_code=%u",
+		   MAC2STR(dst), dialog_token, result, status_code);
+	if (result != GAS_QUERY_SUCCESS) {
+		if (wpa_s->fetch_osu_icon_in_progress)
+			hs20_icon_fetch_failed(wpa_s);
 		return;
+	}
 
 	pos = wpabuf_head(adv_proto);
 	if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
 	    pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
 		wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
 			   "Protocol in response");
+		if (wpa_s->fetch_osu_icon_in_progress)
+			hs20_icon_fetch_failed(wpa_s);
 		return;
 	}
 
@@ -2033,6 +2825,8 @@
 						slen);
 		pos += slen;
 	}
+
+	hs20_notify_parse_done(wpa_s);
 }
 
 
@@ -2045,17 +2839,24 @@
 }
 
 
-int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+			int *freqs)
 {
 	interworking_stop_fetch_anqp(wpa_s);
 	wpa_s->network_select = 1;
 	wpa_s->auto_network_select = 0;
 	wpa_s->auto_select = !!auto_select;
 	wpa_s->fetch_all_anqp = 0;
+	wpa_s->fetch_osu_info = 0;
 	wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
 		   "selection");
 	wpa_s->scan_res_handler = interworking_scan_res_handler;
+	wpa_s->normal_scans = 0;
 	wpa_s->scan_req = MANUAL_SCAN_REQ;
+	os_free(wpa_s->manual_scan_freqs);
+	wpa_s->manual_scan_freqs = freqs;
+	wpa_s->after_wps = 0;
+	wpa_s->known_wps_freq = 0;
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 
 	return 0;
@@ -2068,6 +2869,7 @@
 			const struct wpabuf *resp, u16 status_code)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	struct wpabuf *n;
 
 	wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
 		" dialog_token=%d status_code=%d resp_len=%d",
@@ -2076,10 +2878,14 @@
 	if (!resp)
 		return;
 
-	wpabuf_free(wpa_s->last_gas_resp);
-	wpa_s->last_gas_resp = wpabuf_dup(resp);
-	if (wpa_s->last_gas_resp == NULL)
+	n = wpabuf_dup(resp);
+	if (n == NULL)
 		return;
+	wpabuf_free(wpa_s->prev_gas_resp);
+	wpa_s->prev_gas_resp = wpa_s->last_gas_resp;
+	os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN);
+	wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token;
+	wpa_s->last_gas_resp = n;
 	os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
 	wpa_s->last_gas_dialog_token = dialog_token;
 }
@@ -2095,7 +2901,7 @@
 	struct wpa_bss *bss;
 	int res;
 	size_t len;
-	u8 query_resp_len_limit = 0, pame_bi = 0;
+	u8 query_resp_len_limit = 0;
 
 	freq = wpa_s->assoc_freq;
 	bss = wpa_bss_get_bssid(wpa_s, dst);
@@ -2119,8 +2925,7 @@
 	/* Advertisement Protocol IE */
 	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
 	wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
-	wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
-		      (pame_bi ? 0x80 : 0));
+	wpabuf_put_u8(buf, query_resp_len_limit & 0x7f);
 	wpabuf_put_buf(buf, adv_proto);
 
 	/* GAS Query */
@@ -2133,11 +2938,11 @@
 	res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
+		wpabuf_free(buf);
 		ret = -1;
 	} else
 		wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
 			   "%u", res);
 
-	wpabuf_free(buf);
 	return ret;
 }
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 4a4af82..38ef745 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -12,7 +12,7 @@
 enum gas_query_result;
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-		  u16 info_ids[], size_t num_ids);
+		  u16 info_ids[], size_t num_ids, u32 subtypes);
 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
 		  enum gas_query_result result,
 		  const struct wpabuf *adv_proto,
@@ -22,11 +22,14 @@
 		     const struct wpabuf *query);
 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
-int interworking_select(struct wpa_supplicant *wpa_s, int auto_select);
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+			int *freqs);
 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
 void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s);
 int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
 			      struct wpa_cred *cred,
 			      struct wpabuf *domain_names);
+int domain_name_list_contains(struct wpabuf *domain_names,
+			      const char *domain, int exact_match);
 
 #endif /* INTERWORKING_H */
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index 39b837e..d2e839d 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -16,24 +16,36 @@
 #include "driver_i.h"
 #include "p2p_supplicant.h"
 
-extern struct wpa_driver_ops *wpa_drivers[];
-
 
 static void usage(void)
 {
 	int i;
 	printf("%s\n\n%s\n"
 	       "usage:\n"
-	       "  wpa_supplicant [-BddhKLqqstuvW] [-P<pid file>] "
+	       "  wpa_supplicant [-BddhKLqq"
+#ifdef CONFIG_DEBUG_SYSLOG
+	       "s"
+#endif /* CONFIG_DEBUG_SYSLOG */
+	       "t"
+#ifdef CONFIG_DBUS
+	       "u"
+#endif /* CONFIG_DBUS */
+	       "vW] [-P<pid file>] "
 	       "[-g<global ctrl>] \\\n"
 	       "        [-G<group>] \\\n"
 	       "        -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
 	       "[-p<driver_param>] \\\n"
-	       "        [-b<br_ifname>] [-f<debug file>] [-e<entropy file>] "
-	       "\\\n"
+	       "        [-b<br_ifname>] [-e<entropy file>]"
+#ifdef CONFIG_DEBUG_FILE
+	       " [-f<debug file>]"
+#endif /* CONFIG_DEBUG_FILE */
+	       " \\\n"
 	       "        [-o<override driver>] [-O<override ctrl>] \\\n"
 	       "        [-N -i<ifname> -c<conf> [-C<ctrl>] "
 	       "[-D<driver>] \\\n"
+#ifdef CONFIG_P2P
+	       "        [-m<P2P Device config file>] \\\n"
+#endif /* CONFIG_P2P */
 	       "        [-p<driver_param>] [-b<br_ifname>] [-I<config file>] "
 	       "...]\n"
 	       "\n"
@@ -83,6 +95,9 @@
 #endif /* CONFIG_DBUS */
 	printf("  -v = show version\n"
 	       "  -W = wait for a control interface monitor before starting\n"
+#ifdef CONFIG_P2P
+	       "  -m = Configuration file for the P2P Device interface\n"
+#endif /* CONFIG_P2P */
 	       "  -N = start describing new interface\n");
 
 	printf("example:\n"
@@ -160,7 +175,7 @@
 
 	for (;;) {
 		c = getopt(argc, argv,
-			   "b:Bc:C:D:de:f:g:G:hi:I:KLNo:O:p:P:qsTtuvW");
+			   "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -220,6 +235,11 @@
 			license();
 			exitcode = 0;
 			goto out;
+#ifdef CONFIG_P2P
+		case 'm':
+			iface->conf_p2p_dev = optarg;
+			break;
+#endif /* CONFIG_P2P */
 		case 'o':
 			params.override_driver = optarg;
 			break;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index ab62bea..3b730cf 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -79,12 +79,10 @@
 	/* notify the new DBus API */
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
 
-#ifdef CONFIG_P2P
 	if (new_state == WPA_COMPLETED)
 		wpas_p2p_notif_connected(wpa_s);
 	else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
 		wpas_p2p_notif_disconnected(wpa_s);
-#endif /* CONFIG_P2P */
 
 	sme_state_changed(wpa_s);
 
@@ -96,7 +94,7 @@
 		     MAC2STR(wpa_s->bssid),
 		     wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
 		     wpa_ssid_txt(wpa_s->current_ssid->ssid,
-		     wpa_s->current_ssid->ssid_len): "");
+				  wpa_s->current_ssid->ssid_len) : "");
 #endif /* ANDROID */
 }
 
@@ -252,13 +250,13 @@
 void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
 				 struct wpa_ssid *ssid)
 {
+	if (wpa_s->next_ssid == ssid)
+		wpa_s->next_ssid = NULL;
 	if (wpa_s->wpa)
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
 		wpas_dbus_unregister_network(wpa_s, ssid->id);
-#ifdef CONFIG_P2P
 	wpas_p2p_network_removed(wpa_s, ssid);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -440,9 +438,9 @@
 				   const struct wpa_ssid *ssid,
 				   const char *role)
 {
-	wpas_dbus_unregister_p2p_group(wpa_s, ssid);
-
 	wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+
+	wpas_dbus_unregister_p2p_group(wpa_s, ssid);
 }
 
 
@@ -539,17 +537,10 @@
 	wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
 
 	/*
-	 * Register a group member object corresponding to this peer and
-	 * emit a PeerJoined signal. This will check if it really is a
-	 * P2P group.
-	 */
-	wpas_dbus_register_p2p_groupmember(wpa_s, sta);
-
-	/*
 	 * Create 'peer-joined' signal on group object -- will also
 	 * check P2P itself.
 	 */
-	wpas_dbus_signal_p2p_peer_joined(wpa_s, sta);
+	wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
 #endif /* CONFIG_P2P */
 
 	/* Notify listeners a new station has been authorized */
@@ -558,20 +549,15 @@
 
 
 static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
-					    const u8 *sta)
+					    const u8 *sta,
+					    const u8 *p2p_dev_addr)
 {
 #ifdef CONFIG_P2P
 	/*
-	 * Unregister a group member object corresponding to this peer
-	 * if this is a P2P group.
-	 */
-	wpas_dbus_unregister_p2p_groupmember(wpa_s, sta);
-
-	/*
 	 * Create 'peer-disconnected' signal on group object if this
 	 * is a P2P group.
 	 */
-	wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta);
+	wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
 #endif /* CONFIG_P2P */
 
 	/* Notify listeners a station has been deauthorized */
@@ -586,7 +572,7 @@
 	if (authorized)
 		wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
 	else
-		wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr);
+		wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
 }
 
 
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index d94407c..17689c5 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -12,6 +12,7 @@
 #include "common.h"
 #include "utils/eloop.h"
 #include "wpa_supplicant_i.h"
+#include "p2p_supplicant.h"
 #include "driver_i.h"
 #include "offchannel.h"
 
@@ -55,11 +56,12 @@
 
 	without_roc = wpa_s->pending_action_without_roc;
 	wpa_s->pending_action_without_roc = 0;
-	wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
-		   "(without_roc=%d pending_action_tx=%p)",
-		   without_roc, wpa_s->pending_action_tx);
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)",
+		   without_roc, wpa_s->pending_action_tx,
+		   !!wpa_s->pending_action_tx_done);
 
-	if (wpa_s->pending_action_tx == NULL)
+	if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done)
 		return;
 
 	/*
@@ -159,6 +161,21 @@
 		return;
 	}
 
+	/* Accept report only if the contents of the frame matches */
+	if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 ||
+	    os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx),
+		      wpabuf_len(wpa_s->pending_action_tx)) != 0) {
+		wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+				   "mismatching contents with pending frame");
+		wpa_hexdump(MSG_MSGDUMP, "TX status frame data",
+			    data, data_len);
+		wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+				wpa_s->pending_action_tx);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
+
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 
@@ -172,6 +189,12 @@
 			wpa_s->pending_action_bssid,
 			data, data_len, result);
 	}
+
+	if (wpa_s->p2p_long_listen > 0) {
+		/* Continue the listen */
+		wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+		wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+	}
 }
 
 
@@ -220,6 +243,7 @@
 			   MAC2STR(wpa_s->pending_action_dst));
 		wpabuf_free(wpa_s->pending_action_tx);
 	}
+	wpa_s->pending_action_tx_done = 0;
 	wpa_s->pending_action_tx = wpabuf_alloc(len);
 	if (wpa_s->pending_action_tx == NULL) {
 		wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
@@ -236,18 +260,22 @@
 
 	if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
 		struct wpa_supplicant *iface;
+		int ret;
 
 		iface = wpas_get_tx_interface(wpa_s,
 					      wpa_s->pending_action_src);
 		wpa_s->action_tx_wait_time = wait_time;
 
-		return wpa_drv_send_action(
+		ret = wpa_drv_send_action(
 			iface, wpa_s->pending_action_freq,
 			wait_time, wpa_s->pending_action_dst,
 			wpa_s->pending_action_src, wpa_s->pending_action_bssid,
 			wpabuf_head(wpa_s->pending_action_tx),
 			wpabuf_len(wpa_s->pending_action_tx),
 			wpa_s->pending_action_no_cck);
+		if (ret == 0)
+			wpa_s->pending_action_tx_done = 1;
+		return ret;
 	}
 
 	if (freq) {
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index d7b3189..3eea9e8 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1,6 +1,7 @@
 /*
  * wpa_supplicant - P2P
  * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,6 +20,7 @@
 #include "ap/ap_config.h"
 #include "ap/sta_info.h"
 #include "ap/ap_drv_ops.h"
+#include "ap/wps_hostapd.h"
 #include "ap/p2p_hostapd.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
@@ -33,6 +35,7 @@
 #include "offchannel.h"
 #include "wps_supplicant.h"
 #include "p2p_supplicant.h"
+#include "wifi_display.h"
 
 
 /*
@@ -48,17 +51,14 @@
  * How many seconds to try to reconnect to the GO when connection in P2P client
  * role has been lost.
  */
-#ifdef ANDROID_P2P
-#define P2P_MAX_CLIENT_IDLE 20
-#else
 #define P2P_MAX_CLIENT_IDLE 10
-#endif /* ANDROID_P2P */
 #endif /* P2P_MAX_CLIENT_IDLE */
 
 #ifndef P2P_MAX_INITIAL_CONN_WAIT
 /*
  * How many seconds to wait for initial 4-way handshake to get completed after
- * WPS provisioning step.
+ * WPS provisioning step or after the re-invocation of a persistent group on a
+ * P2P Client.
  */
 #define P2P_MAX_INITIAL_CONN_WAIT 10
 #endif /* P2P_MAX_INITIAL_CONN_WAIT */
@@ -73,9 +73,14 @@
 #define P2P_MAX_INITIAL_CONN_WAIT_GO 10
 #endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */
 
-#ifndef P2P_CONCURRENT_SEARCH_DELAY
-#define P2P_CONCURRENT_SEARCH_DELAY 500
-#endif /* P2P_CONCURRENT_SEARCH_DELAY */
+#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * re-invocation of a persistent group on the GO when the client is expected
+ * to connect automatically (no user interaction).
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15
+#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */
 
 #define P2P_MGMT_DEVICE_PREFIX		"p2p-dev-"
 
@@ -88,9 +93,7 @@
 	P2P_GROUP_REMOVAL_UNAVAILABLE,
 	P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
 	P2P_GROUP_REMOVAL_PSK_FAILURE,
-#ifdef ANDROID_P2P
 	P2P_GROUP_REMOVAL_FREQ_CONFLICT
-#endif
 };
 
 
@@ -98,24 +101,28 @@
 static struct wpa_supplicant *
 wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
 			 int go);
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
-static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+			       const u8 *ssid, size_t ssid_len);
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
+				   const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
 			 const u8 *dev_addr, enum p2p_wps_method wps_method,
-			 int auto_join);
+			 int auto_join, int freq,
+			 const u8 *ssid, size_t ssid_len);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
 					     void *timeout_ctx);
-#ifdef ANDROID_P2P
 static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
-#endif
 static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
 					int group_added);
 static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+static void wpas_stop_listen(void *ctx);
+static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
 
 
 /*
@@ -125,7 +132,7 @@
 static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s)
 {
 	int *freqs;
-	int num;
+	int num, unused;
 
 	freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
 	if (!freqs)
@@ -135,7 +142,9 @@
 				     wpa_s->num_multichan_concurrent);
 	os_free(freqs);
 
-	return wpa_s->num_multichan_concurrent - num;
+	unused = wpa_s->num_multichan_concurrent - num;
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused);
+	return unused;
 }
 
 
@@ -143,28 +152,33 @@
  * Get the frequencies that are currently in use by one or more of the virtual
  * interfaces, and that are also valid for P2P operation.
  */
-static int wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
-				     int *p2p_freqs, unsigned int len)
+static unsigned int
+wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
+			  struct wpa_used_freq_data *p2p_freqs,
+			  unsigned int len)
 {
-	int *freqs;
+	struct wpa_used_freq_data *freqs;
 	unsigned int num, i, j;
 
-	freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+	freqs = os_calloc(wpa_s->num_multichan_concurrent,
+			  sizeof(struct wpa_used_freq_data));
 	if (!freqs)
-		return -1;
+		return 0;
 
-	num = get_shared_radio_freqs(wpa_s, freqs,
-				     wpa_s->num_multichan_concurrent);
+	num = get_shared_radio_freqs_data(wpa_s, freqs,
+					  wpa_s->num_multichan_concurrent);
 
-	os_memset(p2p_freqs, 0, sizeof(int) * len);
+	os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
 
 	for (i = 0, j = 0; i < num && j < len; i++) {
-		if (p2p_supported_freq(wpa_s->global->p2p, freqs[i]))
+		if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
 			p2p_freqs[j++] = freqs[i];
 	}
 
 	os_free(freqs);
 
+	dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j);
+
 	return j;
 }
 
@@ -174,10 +188,13 @@
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
-	if (freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
-	    wpas_p2p_num_unused_channels(wpa_s) > 0 &&
-	    wpa_s->parent->conf->p2p_ignore_shared_freq)
+	if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
+	    freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
+	    wpas_p2p_num_unused_channels(wpa_s) > 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration",
+			   freq);
 		freq = 0;
+	}
 	p2p_set_own_freq_preference(wpa_s->global->p2p, freq);
 }
 
@@ -187,6 +204,12 @@
 {
 	size_t i;
 
+	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 (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
 
@@ -195,14 +218,29 @@
 
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *bss = scan_res->res[i];
-		struct os_time time_tmp_age, entry_ts;
+		struct os_reltime time_tmp_age, entry_ts;
+		const u8 *ies;
+		size_t ies_len;
+
 		time_tmp_age.sec = bss->age / 1000;
 		time_tmp_age.usec = (bss->age % 1000) * 1000;
-		os_time_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
+		os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
+
+		ies = (const u8 *) (bss + 1);
+		ies_len = bss->ie_len;
+		if (bss->beacon_ie_len > 0 &&
+		    !wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+		    wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+			wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for "
+				   MACSTR, MAC2STR(bss->bssid));
+			ies = ies + ies_len;
+			ies_len = bss->beacon_ie_len;
+		}
+
+
 		if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
 					 bss->freq, &entry_ts, bss->level,
-					 (const u8 *) (bss + 1),
-					 bss->ie_len) > 0)
+					 ies, ies_len) > 0)
 			break;
 	}
 
@@ -210,94 +248,130 @@
 }
 
 
+static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpa_driver_scan_params *params = work->ctx;
+	int ret;
+
+	if (deinit) {
+		if (!work->started) {
+			wpa_scan_free_params(params);
+			return;
+		}
+
+		wpa_s->p2p_scan_work = NULL;
+		return;
+	}
+
+	ret = wpa_drv_scan(wpa_s, params);
+	wpa_scan_free_params(params);
+	work->ctx = NULL;
+	if (ret) {
+		radio_work_done(work);
+		return;
+	}
+
+	os_get_reltime(&wpa_s->scan_trigger_time);
+	wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+	wpa_s->own_scan_requested = 1;
+	wpa_s->p2p_scan_work = work;
+}
+
+
 static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
 			 unsigned int num_req_dev_types,
 			 const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	struct wpa_supplicant *ifs;
-	struct wpa_driver_scan_params params;
-	int ret;
+	struct wpa_driver_scan_params *params = NULL;
 	struct wpabuf *wps_ie, *ies;
-	int social_channels[] = { 2412, 2437, 2462, 0, 0 };
 	size_t ielen;
+	u8 *n;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		if (ifs->sta_scan_pending &&
-		    (wpas_scan_scheduled(ifs) || ifs->scanning) &&
-		    wpas_p2p_in_progress(wpa_s) == 2) {
-			wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow "
-				   "pending station mode scan to be "
-				   "completed on interface %s", ifs->ifname);
-			wpa_s->global->p2p_cb_on_scan_complete = 1;
-			wpa_supplicant_req_scan(ifs, 0, 0);
-			return 1;
-		}
+	if (wpa_s->p2p_scan_work) {
+		wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending");
+		return -1;
 	}
 
-	os_memset(&params, 0, sizeof(params));
+	params = os_zalloc(sizeof(*params));
+	if (params == NULL)
+		return -1;
 
 	/* P2P Wildcard SSID */
-	params.num_ssids = 1;
-	params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
-	params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+	params->num_ssids = 1;
+	n = os_malloc(P2P_WILDCARD_SSID_LEN);
+	if (n == NULL)
+		goto fail;
+	os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+	params->ssids[0].ssid = n;
+	params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
 
 	wpa_s->wps->dev.p2p = 1;
 	wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
 					wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
 					num_req_dev_types, req_dev_types);
 	if (wps_ie == NULL)
-		return -1;
+		goto fail;
 
 	ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
 	ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
 	if (ies == NULL) {
 		wpabuf_free(wps_ie);
-		return -1;
+		goto fail;
 	}
 	wpabuf_put_buf(ies, wps_ie);
 	wpabuf_free(wps_ie);
 
 	p2p_scan_ie(wpa_s->global->p2p, ies, dev_id);
 
-	params.p2p_probe = 1;
-	params.extra_ies = wpabuf_head(ies);
-	params.extra_ies_len = wpabuf_len(ies);
+	params->p2p_probe = 1;
+	n = os_malloc(wpabuf_len(ies));
+	if (n == NULL) {
+		wpabuf_free(ies);
+		goto fail;
+	}
+	os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies));
+	params->extra_ies = n;
+	params->extra_ies_len = wpabuf_len(ies);
+	wpabuf_free(ies);
 
 	switch (type) {
 	case P2P_SCAN_SOCIAL:
-		params.freqs = social_channels;
+		params->freqs = os_malloc(4 * sizeof(int));
+		if (params->freqs == NULL)
+			goto fail;
+		params->freqs[0] = 2412;
+		params->freqs[1] = 2437;
+		params->freqs[2] = 2462;
+		params->freqs[3] = 0;
 		break;
 	case P2P_SCAN_FULL:
 		break;
 	case P2P_SCAN_SOCIAL_PLUS_ONE:
-		social_channels[3] = freq;
-		params.freqs = social_channels;
+		params->freqs = os_malloc(5 * sizeof(int));
+		if (params->freqs == NULL)
+			goto fail;
+		params->freqs[0] = 2412;
+		params->freqs[1] = 2437;
+		params->freqs[2] = 2462;
+		params->freqs[3] = freq;
+		params->freqs[4] = 0;
 		break;
 	}
 
-	ret = wpa_drv_scan(wpa_s, &params);
+	radio_remove_works(wpa_s, "p2p-scan", 0);
+	if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb,
+			   params) < 0)
+		goto fail;
+	return 0;
 
-	wpabuf_free(ies);
-
-	if (ret) {
-		for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-			if (ifs->scanning ||
-			    ifs->scan_res_handler == wpas_p2p_scan_res_handler) {
-				wpa_s->global->p2p_cb_on_scan_complete = 1;
-				ret = 1;
-				break;
-			}
-		}
-	} else {
-		os_get_time(&wpa_s->scan_trigger_time);
-		wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
-	}
-
-	return ret;
+fail:
+	wpa_scan_free_params(params);
+	return -1;
 }
 
 
@@ -405,11 +479,9 @@
 	case P2P_GROUP_REMOVAL_PSK_FAILURE:
 		reason = " reason=PSK_FAILURE";
 		break;
-#ifdef ANDROID_P2P
 	case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
 		reason = " reason=FREQ_CONFLICT";
 		break;
-#endif
 	default:
 		reason = "";
 		break;
@@ -420,9 +492,8 @@
 			       wpa_s->ifname, gtype, reason);
 	}
 
-#ifdef ANDROID_P2P
-	eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
-#endif
+	if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0)
+		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout");
 	if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
 		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
 	if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
@@ -432,6 +503,14 @@
 		wpa_s->p2p_in_provisioning = 0;
 	}
 
+	wpa_s->p2p_in_invitation = 0;
+
+	/*
+	 * Make sure wait for the first client does not remain active after the
+	 * group has been removed.
+	 */
+	wpa_s->global->p2p_go_wait_client.sec = 0;
+
 	if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
 		wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
 
@@ -452,6 +531,17 @@
 		return 1;
 	}
 
+	if (!wpa_s->p2p_go_group_formation_completed) {
+		wpa_s->global->p2p_group_formation = NULL;
+		wpa_s->p2p_in_provisioning = 0;
+	}
+
+	wpa_s->show_group_started = 0;
+	os_free(wpa_s->go_params);
+	wpa_s->go_params = NULL;
+
+	wpa_s->waiting_presence_resp = 0;
+
 	wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
 	if (ssid && (ssid->p2p_group ||
 		     ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
@@ -474,7 +564,6 @@
 		wpa_config_remove_network(wpa_s->conf, id);
 		wpa_supplicant_clear_status(wpa_s);
 		wpa_supplicant_cancel_sched_scan(wpa_s);
-		wpa_s->sta_scan_pending = 0;
 	} else {
 		wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
 			   "found");
@@ -504,6 +593,10 @@
 		bssid = wpa_s->bssid;
 
 	bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+	if (bss == NULL && wpa_s->go_params &&
+	    !is_zero_ether_addr(wpa_s->go_params->peer_device_addr))
+		bss = wpa_bss_get_p2p_dev_addr(
+			wpa_s, wpa_s->go_params->peer_device_addr);
 	if (bss == NULL) {
 		u8 iface_addr[ETH_ALEN];
 		if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
@@ -518,6 +611,9 @@
 	}
 
 	p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+	if (p2p == NULL)
+		p2p = wpa_bss_get_vendor_ie_multi_beacon(bss,
+							 P2P_IE_VENDOR_TYPE);
 	if (p2p == NULL) {
 		wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
 			   "group is persistent - BSS " MACSTR
@@ -630,12 +726,10 @@
 		changed = 1;
 	}
 
-#ifndef CONFIG_NO_CONFIG_WRITE
 	if (changed && wpa_s->conf->update_config &&
 	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
 	}
-#endif /* CONFIG_NO_CONFIG_WRITE */
 
 	return s->id;
 }
@@ -692,7 +786,7 @@
 		os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
 		s->p2p_client_list = n;
 		s->num_p2p_clients++;
-	} else if (!found) {
+	} else if (!found && s->p2p_client_list) {
 		/* Not enough room for an additional entry - drop the oldest
 		 * entry */
 		os_memmove(s->p2p_client_list,
@@ -703,11 +797,54 @@
 			  addr, ETH_ALEN);
 	}
 
-#ifndef CONFIG_NO_CONFIG_WRITE
 	if (wpa_s->parent->conf->update_config &&
 	    wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s,
+				   int go, struct wpa_ssid *ssid, int freq,
+				   const u8 *psk, const char *passphrase,
+				   const u8 *go_dev_addr, int persistent,
+				   const char *extra)
+{
+	const char *ssid_txt;
+	char psk_txt[65];
+
+	if (psk)
+		wpa_snprintf_hex(psk_txt, sizeof(psk_txt), psk, 32);
+	else
+		psk_txt[0] = '\0';
+
+	if (ssid)
+		ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+	else
+		ssid_txt = "";
+
+	if (passphrase && passphrase[0] == '\0')
+		passphrase = NULL;
+
+	/*
+	 * Include PSK/passphrase only in the control interface message and
+	 * leave it out from the debug log entry.
+	 */
+	wpa_msg_global_ctrl(wpa_s->parent, MSG_INFO,
+			    P2P_EVENT_GROUP_STARTED
+			    "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr="
+			    MACSTR "%s%s",
+			    wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
+			    psk ? " psk=" : "", psk_txt,
+			    passphrase ? " passphrase=\"" : "",
+			    passphrase ? passphrase : "",
+			    passphrase ? "\"" : "",
+			    MAC2STR(go_dev_addr),
+			    persistent ? " [PERSISTENT]" : "", extra);
+	wpa_printf(MSG_INFO, P2P_EVENT_GROUP_STARTED
+		   "%s %s ssid=\"%s\" freq=%d go_dev_addr=" MACSTR "%s%s",
+		   wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
+		   MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "",
+		   extra);
 }
 
 
@@ -715,7 +852,6 @@
 					   int success)
 {
 	struct wpa_ssid *ssid;
-	const char *ssid_txt;
 	int client;
 	int persistent;
 	u8 go_dev_addr[ETH_ALEN];
@@ -728,8 +864,11 @@
 	 */
 	if (wpa_s->global->p2p_group_formation)
 		wpa_s = wpa_s->global->p2p_group_formation;
-	wpa_s->global->p2p_group_formation = NULL;
-	wpa_s->p2p_in_provisioning = 0;
+	if (wpa_s->p2p_go_group_formation_completed) {
+		wpa_s->global->p2p_group_formation = NULL;
+		wpa_s->p2p_in_provisioning = 0;
+	}
+	wpa_s->p2p_in_invitation = 0;
 
 	if (!success) {
 		wpa_msg_global(wpa_s->parent, MSG_INFO,
@@ -751,7 +890,6 @@
 
 	persistent = 0;
 	if (ssid) {
-		ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
 		client = ssid->mode == WPAS_MODE_INFRA;
 		if (ssid->mode == WPAS_MODE_P2P_GO) {
 			persistent = ssid->p2p_persistent_group;
@@ -763,7 +901,6 @@
 							       ssid->ssid,
 							       ssid->ssid_len);
 	} else {
-		ssid_txt = "";
 		client = wpa_s->p2p_group_interface ==
 			P2P_GROUP_INTERFACE_CLIENT;
 		os_memset(go_dev_addr, 0, ETH_ALEN);
@@ -777,33 +914,13 @@
 		 * packets.
 		 */
 		wpa_s->show_group_started = 1;
-#ifdef ANDROID_P2P
-		/* For client Second phase of Group formation (4-way handshake) can be still pending
-		 * So we need to restore wpa_s->global->p2p_group_formation */
-		wpa_printf(MSG_INFO, "Restoring back wpa_s->global->p2p_group_formation to wpa_s %p\n", wpa_s);
-		wpa_s->global->p2p_group_formation = wpa_s;
-#endif
-
-	} else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
-		char psk[65];
-		wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
-		wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-			       "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr="
-			       MACSTR "%s",
-			       wpa_s->ifname, ssid_txt, ssid->frequency, psk,
-			       MAC2STR(go_dev_addr),
-			       persistent ? " [PERSISTENT]" : "");
-		wpas_p2p_cross_connect_setup(wpa_s);
-		wpas_p2p_set_group_idle_timeout(wpa_s);
 	} else {
-		wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-			       "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
-			       "go_dev_addr=" MACSTR "%s",
-			       wpa_s->ifname, ssid_txt,
-			       ssid ? ssid->frequency : 0,
-			       ssid && ssid->passphrase ? ssid->passphrase : "",
-			       MAC2STR(go_dev_addr),
-			       persistent ? " [PERSISTENT]" : "");
+		wpas_p2p_group_started(wpa_s, 1, ssid,
+				       ssid ? ssid->frequency : 0,
+				       ssid && ssid->passphrase == NULL &&
+				       ssid->psk_set ? ssid->psk : NULL,
+				       ssid ? ssid->passphrase : NULL,
+				       go_dev_addr, persistent, "");
 		wpas_p2p_cross_connect_setup(wpa_s);
 		wpas_p2p_set_group_idle_timeout(wpa_s);
 	}
@@ -819,7 +936,60 @@
 		network_id = ssid->id;
 	if (!client) {
 		wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
-		os_get_time(&wpa_s->global->p2p_go_wait_client);
+		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+	}
+}
+
+
+struct send_action_work {
+	unsigned int freq;
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	size_t len;
+	unsigned int wait_time;
+	u8 buf[0];
+};
+
+
+static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
+					      void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->p2p_send_action_work)
+		return;
+
+	wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
+	os_free(wpa_s->p2p_send_action_work->ctx);
+	radio_work_done(wpa_s->p2p_send_action_work);
+	wpa_s->p2p_send_action_work = NULL;
+}
+
+
+static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_send_action_work) {
+		struct send_action_work *awork;
+		awork = wpa_s->p2p_send_action_work->ctx;
+		if (awork->wait_time == 0) {
+			os_free(awork);
+			radio_work_done(wpa_s->p2p_send_action_work);
+			wpa_s->p2p_send_action_work = NULL;
+		} else {
+			/*
+			 * In theory, this should not be needed, but number of
+			 * places in the P2P code is still using non-zero wait
+			 * time for the last Action frame in the sequence and
+			 * some of these do not call send_action_done().
+			 */
+			eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+					     wpa_s, NULL);
+			eloop_register_timeout(
+				0, awork->wait_time * 1000,
+				wpas_p2p_send_action_work_timeout,
+				wpa_s, NULL);
+		}
 	}
 }
 
@@ -834,10 +1004,10 @@
 {
 	enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
 
+	wpas_p2p_action_tx_clear(wpa_s);
+
 	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
 		return;
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return;
 
 	switch (result) {
 	case OFFCHANNEL_SEND_ACTION_SUCCESS:
@@ -867,11 +1037,87 @@
 }
 
 
+static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct send_action_work *awork = work->ctx;
+
+	if (deinit) {
+		if (work->started) {
+			eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+					     wpa_s, NULL);
+			wpa_s->p2p_send_action_work = NULL;
+			offchannel_send_action_done(wpa_s);
+		}
+		os_free(awork);
+		return;
+	}
+
+	if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src,
+				   awork->bssid, awork->buf, awork->len,
+				   awork->wait_time,
+				   wpas_p2p_send_action_tx_status, 1) < 0) {
+		os_free(awork);
+		radio_work_done(work);
+		return;
+	}
+	wpa_s->p2p_send_action_work = work;
+}
+
+
+static int wpas_send_action_work(struct wpa_supplicant *wpa_s,
+				 unsigned int freq, const u8 *dst,
+				 const u8 *src, const u8 *bssid, const u8 *buf,
+				 size_t len, unsigned int wait_time)
+{
+	struct send_action_work *awork;
+
+	if (wpa_s->p2p_send_action_work) {
+		wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending");
+		return -1;
+	}
+
+	awork = os_zalloc(sizeof(*awork) + len);
+	if (awork == NULL)
+		return -1;
+
+	awork->freq = freq;
+	os_memcpy(awork->dst, dst, ETH_ALEN);
+	os_memcpy(awork->src, src, ETH_ALEN);
+	os_memcpy(awork->bssid, bssid, ETH_ALEN);
+	awork->len = len;
+	awork->wait_time = wait_time;
+	os_memcpy(awork->buf, buf, len);
+
+	if (radio_add_work(wpa_s, freq, "p2p-send-action", 0,
+			   wpas_send_action_cb, awork) < 0) {
+		os_free(awork);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
 			    const u8 *src, const u8 *bssid, const u8 *buf,
 			    size_t len, unsigned int wait_time)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	int listen_freq = -1, send_freq = -1;
+
+	if (wpa_s->p2p_listen_work)
+		listen_freq = wpa_s->p2p_listen_work->freq;
+	if (wpa_s->p2p_send_action_work)
+		send_freq = wpa_s->p2p_send_action_work->freq;
+	if (listen_freq != (int) freq && send_freq != (int) freq) {
+		wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)",
+			   listen_freq, send_freq);
+		return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
+					     len, wait_time);
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX");
 	return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
 				      wait_time,
 				      wpas_p2p_send_action_tx_status, 1);
@@ -881,6 +1127,15 @@
 static void wpas_send_action_done(void *ctx)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->p2p_send_action_work) {
+		eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+				     wpa_s, NULL);
+		os_free(wpa_s->p2p_send_action_work->ctx);
+		radio_work_done(wpa_s->p2p_send_action_work);
+		wpa_s->p2p_send_action_work = NULL;
+	}
+
 	offchannel_send_action_done(wpa_s);
 }
 
@@ -901,15 +1156,29 @@
 static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *res)
 {
-	wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
-		   MAC2STR(res->peer_interface_addr));
+	wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
+		   " dev_addr " MACSTR " wps_method %d",
+		   MAC2STR(res->peer_interface_addr),
+		   MAC2STR(res->peer_device_addr), res->wps_method);
 	wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
 			  res->ssid, res->ssid_len);
 	wpa_supplicant_ap_deinit(wpa_s);
 	wpas_copy_go_neg_results(wpa_s, res);
-	if (res->wps_method == WPS_PBC)
+	if (res->wps_method == WPS_PBC) {
 		wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
-	else {
+#ifdef CONFIG_WPS_NFC
+	} else if (res->wps_method == WPS_NFC) {
+		wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
+				   res->peer_interface_addr,
+				   wpa_s->parent->p2p_oob_dev_pw,
+				   wpa_s->parent->p2p_oob_dev_pw_id, 1,
+				   wpa_s->parent->p2p_oob_dev_pw_id ==
+				   DEV_PW_NFC_CONNECTION_HANDOVER ?
+				   wpa_s->parent->p2p_peer_oob_pubkey_hash :
+				   NULL,
+				   NULL, 0, 0);
+#endif /* CONFIG_WPS_NFC */
+	} else {
 		u16 dev_pw_id = DEV_PW_DEFAULT;
 		if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
 			dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
@@ -969,33 +1238,14 @@
 		wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
 		if (wpa_s->global->p2p_group_formation == wpa_s)
 			wpa_s->global->p2p_group_formation = NULL;
-		if (os_strlen(params->passphrase) > 0) {
-			wpa_msg_global(wpa_s->parent, MSG_INFO,
-				       P2P_EVENT_GROUP_STARTED
-				       "%s GO ssid=\"%s\" freq=%d "
-				       "passphrase=\"%s\" go_dev_addr=" MACSTR
-				       "%s", wpa_s->ifname,
-				       wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-				       ssid->frequency, params->passphrase,
-				       MAC2STR(wpa_s->global->p2p_dev_addr),
-				       params->persistent_group ?
-				       " [PERSISTENT]" : "");
-		} else {
-			char psk[65];
-			wpa_snprintf_hex(psk, sizeof(psk), params->psk,
-					 sizeof(params->psk));
-			wpa_msg_global(wpa_s->parent, MSG_INFO,
-				       P2P_EVENT_GROUP_STARTED
-				       "%s GO ssid=\"%s\" freq=%d psk=%s "
-				       "go_dev_addr=" MACSTR "%s",
-				       wpa_s->ifname,
-				       wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-				       ssid->frequency, psk,
-				       MAC2STR(wpa_s->global->p2p_dev_addr),
-				       params->persistent_group ?
-				       " [PERSISTENT]" : "");
-		}
+		wpas_p2p_group_started(wpa_s, 1, ssid, ssid->frequency,
+				       params->passphrase[0] == '\0' ?
+				       params->psk : NULL,
+				       params->passphrase,
+				       wpa_s->global->p2p_dev_addr,
+				       params->persistent_group, "");
 
+		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
 		if (params->persistent_group) {
 			network_id = wpas_p2p_store_persistent_group(
 				wpa_s->parent, ssid,
@@ -1007,6 +1257,21 @@
 		wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
 		wpas_p2p_cross_connect_setup(wpa_s);
 		wpas_p2p_set_group_idle_timeout(wpa_s);
+
+		if (wpa_s->p2p_first_connection_timeout) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Start group formation timeout of %d seconds until first data connection on GO",
+				wpa_s->p2p_first_connection_timeout);
+			wpa_s->p2p_go_group_formation_completed = 0;
+			wpa_s->global->p2p_group_formation = wpa_s;
+			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+					     wpa_s->parent, NULL);
+			eloop_register_timeout(
+				wpa_s->p2p_first_connection_timeout, 0,
+				wpas_p2p_group_formation_timeout,
+				wpa_s->parent, NULL);
+		}
+
 		return;
 	}
 
@@ -1017,10 +1282,24 @@
 			   "filtering");
 		return;
 	}
-	if (params->wps_method == WPS_PBC)
+	if (params->wps_method == WPS_PBC) {
 		wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
 					  params->peer_device_addr);
-	else if (wpa_s->p2p_pin[0])
+#ifdef CONFIG_WPS_NFC
+	} else if (params->wps_method == WPS_NFC) {
+		if (wpa_s->parent->p2p_oob_dev_pw_id !=
+		    DEV_PW_NFC_CONNECTION_HANDOVER &&
+		    !wpa_s->parent->p2p_oob_dev_pw) {
+			wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+			return;
+		}
+		wpas_ap_wps_add_nfc_pw(
+			wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+			wpa_s->parent->p2p_oob_dev_pw,
+			wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+			wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+#endif /* CONFIG_WPS_NFC */
+	} else if (wpa_s->p2p_pin[0])
 		wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
 					  wpa_s->p2p_pin, NULL, 0, 0);
 	os_free(wpa_s->go_params);
@@ -1057,6 +1336,7 @@
 		WPAS_MODE_P2P_GO;
 	ssid->frequency = params->freq;
 	ssid->ht40 = params->ht40;
+	ssid->vht = params->vht;
 	ssid->ssid = os_zalloc(params->ssid_len + 1);
 	if (ssid->ssid) {
 		os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -1086,6 +1366,7 @@
 	wpa_s->ap_configured_cb = p2p_go_configured;
 	wpa_s->ap_configured_cb_ctx = wpa_s;
 	wpa_s->ap_configured_cb_data = wpa_s->go_params;
+	wpa_s->scan_req = NORMAL_SCAN_REQ;
 	wpa_s->connect_without_scan = ssid;
 	wpa_s->reassociate = 1;
 	wpa_s->disconnected = 0;
@@ -1125,7 +1406,14 @@
 	d->pbc_in_m1 = s->pbc_in_m1;
 	d->ignore_old_scan_res = s->ignore_old_scan_res;
 	d->beacon_int = s->beacon_int;
+	d->dtim_period = s->dtim_period;
 	d->disassoc_low_ack = s->disassoc_low_ack;
+	d->disable_scan_offload = s->disable_scan_offload;
+
+	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) {
+		d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
+		d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
+	}
 }
 
 
@@ -1143,8 +1431,7 @@
 	if (os_strlen(ifname) >= IFNAMSIZ &&
 	    os_strlen(wpa_s->ifname) < IFNAMSIZ) {
 		/* Try to avoid going over the IFNAMSIZ length limit */
-		os_snprintf(ifname, sizeof(ifname), "p2p-%d",
-			    wpa_s->p2p_group_idx);
+		os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
 	}
 }
 
@@ -1277,12 +1564,21 @@
 			     wpa_s->parent, NULL);
 	if (wpa_s->global->p2p)
 		p2p_group_formation_failed(wpa_s->global->p2p);
-	else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		wpa_drv_p2p_group_formation_failed(wpa_s);
 	wpas_group_formation_completed(wpa_s, 0);
 }
 
 
+static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure");
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+			     wpa_s->parent, NULL);
+	eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+			       wpa_s->parent, NULL);
+	wpa_s->global->p2p_fail_on_wps_complete = 0;
+}
+
+
 void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->global->p2p_group_formation != wpa_s)
@@ -1295,7 +1591,7 @@
 }
 
 
-void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
@@ -1316,6 +1612,8 @@
 
 	if (wpa_s->p2p_go_ht40)
 		res->ht40 = 1;
+	if (wpa_s->p2p_go_vht)
+		res->vht = 1;
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
 		       "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
@@ -1345,6 +1643,9 @@
 			wpas_p2p_init_group_interface(wpa_s, res->role_go);
 		if (group_wpa_s == NULL) {
 			wpas_p2p_remove_pending_group_interface(wpa_s);
+			eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
+					     wpa_s, NULL);
+			wpas_p2p_group_formation_failed(wpa_s);
 			return;
 		}
 		if (group_wpa_s != wpa_s) {
@@ -1380,7 +1681,7 @@
 }
 
 
-void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
@@ -1390,33 +1691,34 @@
 }
 
 
-void wpas_dev_found(void *ctx, const u8 *addr,
-		    const struct p2p_peer_info *info,
-		    int new_device)
+static void wpas_dev_found(void *ctx, const u8 *addr,
+			   const struct p2p_peer_info *info,
+			   int new_device)
 {
 #ifndef CONFIG_NO_STDOUT_DEBUG
 	struct wpa_supplicant *wpa_s = ctx;
 	char devtype[WPS_DEV_TYPE_BUFSIZE];
-#define WFD_DEV_INFO_SIZE 9
-	char wfd_dev_info_hex[2 * WFD_DEV_INFO_SIZE + 1];
-	os_memset(wfd_dev_info_hex, 0, sizeof(wfd_dev_info_hex));
+	char *wfd_dev_info_hex = NULL;
+
 #ifdef CONFIG_WIFI_DISPLAY
-	if (info->wfd_subelems) {
-		wpa_snprintf_hex(wfd_dev_info_hex, sizeof(wfd_dev_info_hex),
-					wpabuf_head(info->wfd_subelems),
-					WFD_DEV_INFO_SIZE);
-	}
+	wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems,
+						    WFD_SUBELEM_DEVICE_INFO);
 #endif /* CONFIG_WIFI_DISPLAY */
+
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
 		       " p2p_dev_addr=" MACSTR
 		       " pri_dev_type=%s name='%s' config_methods=0x%x "
-		       "dev_capab=0x%x group_capab=0x%x%s%s",
+		       "dev_capab=0x%x group_capab=0x%x%s%s%s",
 		       MAC2STR(addr), MAC2STR(info->p2p_device_addr),
 		       wps_dev_type_bin2str(info->pri_dev_type, devtype,
 					    sizeof(devtype)),
 		       info->device_name, info->config_methods,
 		       info->dev_capab, info->group_capab,
-		       wfd_dev_info_hex[0] ? " wfd_dev_info=0x" : "", wfd_dev_info_hex);
+		       wfd_dev_info_hex ? " wfd_dev_info=0x" : "",
+		       wfd_dev_info_hex ? wfd_dev_info_hex : "",
+		       info->vendor_elems ? " vendor_elems=1" : "");
+
+	os_free(wfd_dev_info_hex);
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
 	wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
@@ -1441,32 +1743,108 @@
 }
 
 
+struct wpas_p2p_listen_work {
+	unsigned int freq;
+	unsigned int duration;
+	struct wpabuf *probe_resp_ie;
+};
+
+
+static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork)
+{
+	if (lwork == NULL)
+		return;
+	wpabuf_free(lwork->probe_resp_ie);
+	os_free(lwork);
+}
+
+
+static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_p2p_listen_work *lwork;
+
+	if (!wpa_s->p2p_listen_work)
+		return;
+
+	lwork = wpa_s->p2p_listen_work->ctx;
+	wpas_p2p_listen_work_free(lwork);
+	radio_work_done(wpa_s->p2p_listen_work);
+	wpa_s->p2p_listen_work = NULL;
+}
+
+
+static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpas_p2p_listen_work *lwork = work->ctx;
+
+	if (deinit) {
+		if (work->started) {
+			wpa_s->p2p_listen_work = NULL;
+			wpas_stop_listen(wpa_s);
+		}
+		wpas_p2p_listen_work_free(lwork);
+		return;
+	}
+
+	wpa_s->p2p_listen_work = work;
+
+	wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL);
+
+	if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
+			   "report received Probe Request frames");
+		wpas_p2p_listen_work_done(wpa_s);
+		return;
+	}
+
+	wpa_s->pending_listen_freq = lwork->freq;
+	wpa_s->pending_listen_duration = lwork->duration;
+
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, lwork->duration) < 0)
+	{
+		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
+			   "to remain on channel (%u MHz) for Listen "
+			   "state", lwork->freq);
+		wpas_p2p_listen_work_done(wpa_s);
+		wpa_s->pending_listen_freq = 0;
+		return;
+	}
+	wpa_s->off_channel_freq = 0;
+	wpa_s->roc_waiting_drv_freq = lwork->freq;
+}
+
+
 static int wpas_start_listen(void *ctx, unsigned int freq,
 			     unsigned int duration,
 			     const struct wpabuf *probe_resp_ie)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	struct wpas_p2p_listen_work *lwork;
 
-	wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL);
-
-	if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
-			   "report received Probe Request frames");
+	if (wpa_s->p2p_listen_work) {
+		wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists");
 		return -1;
 	}
 
-	wpa_s->pending_listen_freq = freq;
-	wpa_s->pending_listen_duration = duration;
+	lwork = os_zalloc(sizeof(*lwork));
+	if (lwork == NULL)
+		return -1;
+	lwork->freq = freq;
+	lwork->duration = duration;
+	if (probe_resp_ie) {
+		lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie);
+		if (lwork->probe_resp_ie == NULL) {
+			wpas_p2p_listen_work_free(lwork);
+			return -1;
+		}
+	}
 
-	if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
-			   "to remain on channel (%u MHz) for Listen "
-			   "state", freq);
-		wpa_s->pending_listen_freq = 0;
+	if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb,
+			   lwork) < 0) {
+		wpas_p2p_listen_work_free(lwork);
 		return -1;
 	}
-	wpa_s->off_channel_freq = 0;
-	wpa_s->roc_waiting_drv_freq = freq;
 
 	return 0;
 }
@@ -1482,6 +1860,7 @@
 	}
 	wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
 	wpa_drv_probe_req_report(wpa_s, 0);
+	wpas_p2p_listen_work_done(wpa_s);
 }
 
 
@@ -1982,8 +2361,8 @@
 #endif /* CONFIG_WIFI_DISPLAY */
 
 
-void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-		     u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+			    u16 update_indic, const u8 *tlvs, size_t tlvs_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	const u8 *pos = tlvs;
@@ -2100,8 +2479,8 @@
 }
 
 
-void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-		      const u8 *tlvs, size_t tlvs_len)
+static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+			     const u8 *tlvs, size_t tlvs_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	const u8 *pos = tlvs;
@@ -2168,8 +2547,6 @@
 u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
 			const struct wpabuf *tlvs)
 {
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return wpa_drv_p2p_sd_request(wpa_s, dst, tlvs);
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return 0;
 	return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
@@ -2201,8 +2578,6 @@
 static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
 				   const struct wpabuf *tlvs)
 {
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return 0;
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return 0;
 	return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
@@ -2280,8 +2655,6 @@
 
 int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
 {
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return wpa_drv_p2p_sd_cancel_request(wpa_s, req);
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 	return p2p_sd_cancel_request(wpa_s->global->p2p,
@@ -2293,11 +2666,6 @@
 			  const u8 *dst, u8 dialog_token,
 			  const struct wpabuf *resp_tlvs)
 {
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-		wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token,
-					resp_tlvs);
-		return;
-	}
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
 	p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
@@ -2307,10 +2675,6 @@
 
 void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-		wpa_drv_p2p_service_update(wpa_s);
-		return;
-	}
 	if (wpa_s->global->p2p)
 		p2p_sd_service_update(wpa_s->global->p2p);
 }
@@ -2435,11 +2799,11 @@
 }
 
 
-void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
-			const u8 *dev_addr, const u8 *pri_dev_type,
-			const char *dev_name, u16 supp_config_methods,
-			u8 dev_capab, u8 group_capab, const u8 *group_id,
-			size_t group_id_len)
+static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+			       const u8 *dev_addr, const u8 *pri_dev_type,
+			       const char *dev_name, u16 supp_config_methods,
+			       u8 dev_capab, u8 group_capab, const u8 *group_id,
+			       size_t group_id_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -2492,7 +2856,7 @@
 }
 
 
-void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	unsigned int generated_pin = 0;
@@ -2504,7 +2868,7 @@
 		wpa_s->pending_pd_before_join = 0;
 		wpa_printf(MSG_DEBUG, "P2P: Starting pending "
 			   "join-existing-group operation");
-		wpas_p2p_join_start(wpa_s);
+		wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 		return;
 	}
 
@@ -2548,7 +2912,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Starting pending "
 			   "join-existing-group operation (no ACK for PD "
 			   "Req attempts)");
-		wpas_p2p_join_start(wpa_s);
+		wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 		return;
 	}
 
@@ -2570,20 +2934,62 @@
 }
 
 
+/**
+ * Pick the best frequency to use from all the currently used frequencies.
+ */
+static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s,
+					struct wpa_used_freq_data *freqs,
+					unsigned int num)
+{
+	unsigned int i, c;
+
+	/* find a candidate freq that is supported by P2P */
+	for (c = 0; c < num; c++)
+		if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq))
+			break;
+
+	if (c == num)
+		return 0;
+
+	/* once we have a candidate, try to find a 'better' one */
+	for (i = c + 1; i < num; i++) {
+		if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
+			continue;
+
+		/*
+		 * 1. Infrastructure station interfaces have higher preference.
+		 * 2. P2P Clients have higher preference.
+		 * 3. All others.
+		 */
+		if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) {
+			c = i;
+			break;
+		}
+
+		if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT))
+			c = i;
+	}
+	return freqs[c].freq;
+}
+
+
 static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
 				  const u8 *go_dev_addr, const u8 *ssid,
 				  size_t ssid_len, int *go, u8 *group_bssid,
 				  int *force_freq, int persistent_group,
-				  const struct p2p_channels *channels)
+				  const struct p2p_channels *channels,
+				  int dev_pw_id)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
-	int res;
+	struct wpa_used_freq_data *freqs;
 	struct wpa_supplicant *grp;
+	int best_freq;
 
 	if (!persistent_group) {
 		wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
-			   " to join an active group", MAC2STR(sa));
+			   " to join an active group (SSID: %s)",
+			   MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len));
 		if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
 		    (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
 		     == 0 ||
@@ -2592,6 +2998,21 @@
 				   "authorized invitation");
 			goto accept_inv;
 		}
+
+#ifdef CONFIG_WPS_NFC
+		if (dev_pw_id >= 0 && wpa_s->parent->p2p_nfc_tag_enabled &&
+		    dev_pw_id == wpa_s->parent->p2p_oob_dev_pw_id) {
+			wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
+			wpa_s->parent->p2p_wps_method = WPS_NFC;
+			wpa_s->parent->pending_join_wps_method = WPS_NFC;
+			os_memcpy(wpa_s->parent->pending_join_dev_addr,
+				  go_dev_addr, ETH_ALEN);
+			os_memcpy(wpa_s->parent->pending_join_iface_addr,
+				  bssid, ETH_ALEN);
+			goto accept_inv;
+		}
+#endif /* CONFIG_WPS_NFC */
+
 		/*
 		 * Do not accept the invitation automatically; notify user and
 		 * request approval.
@@ -2612,6 +3033,7 @@
 	    os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) {
 		wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated "
 			   "invitation to re-invoke a persistent group");
+		os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 	} else if (!wpa_s->conf->persistent_reconnect)
 		return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
 
@@ -2654,11 +3076,26 @@
 accept_inv:
 	wpas_p2p_set_own_freq_preference(wpa_s, 0);
 
+	best_freq = 0;
+	freqs = os_calloc(wpa_s->num_multichan_concurrent,
+			  sizeof(struct wpa_used_freq_data));
+	if (freqs) {
+		int num_channels = wpa_s->num_multichan_concurrent;
+		int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels);
+		best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+		os_free(freqs);
+	}
+
 	/* Get one of the frequencies currently in use */
-	if (wpas_p2p_valid_oper_freqs(wpa_s, &res, 1) > 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match a channel already used by one of the interfaces");
-		*force_freq = res;
-		wpas_p2p_set_own_freq_preference(wpa_s, res);
+	if (best_freq > 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces");
+		wpas_p2p_set_own_freq_preference(wpa_s, best_freq);
+
+		if (wpa_s->num_multichan_concurrent < 2 ||
+		    wpas_p2p_num_unused_channels(wpa_s) < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces");
+			*force_freq = best_freq;
+		}
 	}
 
 	if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
@@ -2701,16 +3138,18 @@
 
 	if (status == P2P_SC_SUCCESS) {
 		wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
-			   " was accepted; op_freq=%d MHz",
-			   MAC2STR(sa), op_freq);
+			   " was accepted; op_freq=%d MHz, SSID=%s",
+			   MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len));
 		if (s) {
 			int go = s->mode == WPAS_MODE_P2P_GO;
 			wpas_p2p_group_add_persistent(
-				wpa_s, s, go, go ? op_freq : 0, 0, NULL);
+				wpa_s, s, go, 0, op_freq, 0, 0, NULL,
+				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
 			wpas_p2p_join(wpa_s, bssid, go_dev_addr,
-				      wpa_s->p2p_wps_method, 0);
+				      wpa_s->p2p_wps_method, 0, op_freq,
+				      ssid, ssid_len);
 		}
 		return;
 	}
@@ -2765,7 +3204,7 @@
 			      ETH_ALEN) == 0)
 			break;
 	}
-	if (i >= ssid->num_p2p_clients) {
+	if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) {
 		if (ssid->mode != WPAS_MODE_P2P_GO &&
 		    os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) {
 			wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d "
@@ -2785,11 +3224,9 @@
 		   ssid->p2p_client_list + (i + 1) * ETH_ALEN,
 		   (ssid->num_p2p_clients - i - 1) * ETH_ALEN);
 	ssid->num_p2p_clients--;
-#ifndef CONFIG_NO_CONFIG_WRITE
 	if (wpa_s->parent->conf->update_config &&
 	    wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
 }
 
 
@@ -2813,10 +3250,12 @@
 
 static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
 				   const struct p2p_channels *channels,
-				   const u8 *peer)
+				   const u8 *peer, int neg_freq,
+				   int peer_oper_freq)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *ssid;
+	int freq;
 
 	if (bssid) {
 		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
@@ -2868,38 +3307,39 @@
 	 * the persistent group so that we will remain on the current channel to
 	 * acknowledge any possible retransmission from the peer.
 	 */
-#ifndef ANDROID_P2P
 	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
 		"starting persistent group");
 	os_sleep(0, 50000);
-#else
-	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 100 ms wait on current channel before "
-		"starting persistent group");
-	os_sleep(0, 100000);
-#endif
 
+	if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
+	    freq_included(channels, neg_freq))
+		freq = neg_freq;
+	else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
+		 freq_included(channels, peer_oper_freq))
+		freq = peer_oper_freq;
+	else
+		freq = 0;
+
+	wpa_printf(MSG_DEBUG, "P2P: Persistent group invitation success - op_freq=%d MHz SSID=%s",
+		   freq, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 	wpas_p2p_group_add_persistent(wpa_s, ssid,
 				      ssid->mode == WPAS_MODE_P2P_GO,
 				      wpa_s->p2p_persistent_go_freq,
-				      wpa_s->p2p_go_ht40, channels);
+				      freq,
+				      wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+				      channels,
+				      ssid->mode == WPAS_MODE_P2P_GO ?
+				      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
+				      0);
 }
 
 
 static int wpas_p2p_disallowed_freq(struct wpa_global *global,
 				    unsigned int freq)
 {
-	unsigned int i;
-
-	if (global->p2p_disallow_freq == NULL)
-		return 0;
-
-	for (i = 0; i < global->num_p2p_disallow_freq; i++) {
-		if (freq >= global->p2p_disallow_freq[i].min &&
-		    freq <= global->p2p_disallow_freq[i].max)
-			return 1;
-	}
-
-	return 0;
+	if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
+		return 1;
+	return freq_range_list_includes(&global->p2p_disallow_freq, freq);
 }
 
 
@@ -2911,10 +3351,13 @@
 
 
 static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
-				     struct p2p_channels *chan)
+				     struct p2p_channels *chan,
+				     struct p2p_channels *cli_chan)
 {
 	int i, cla = 0;
 
+	os_memset(cli_chan, 0, sizeof(*cli_chan));
+
 	wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
 		   "band");
 
@@ -2982,6 +3425,10 @@
 }
 
 
+enum chan_allowed {
+	NOT_ALLOWED, PASSIVE_ONLY, ALLOWED
+};
+
 static int has_channel(struct wpa_global *global,
 		       struct hostapd_hw_modes *mode, u8 chan, int *flags)
 {
@@ -2991,21 +3438,25 @@
 	freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
 		chan * 5;
 	if (wpas_p2p_disallowed_freq(global, freq))
-		return 0;
+		return NOT_ALLOWED;
 
 	for (i = 0; i < mode->num_channels; i++) {
 		if (mode->channels[i].chan == chan) {
 			if (flags)
 				*flags = mode->channels[i].flag;
-			return !(mode->channels[i].flag &
-				 (HOSTAPD_CHAN_DISABLED |
-				  HOSTAPD_CHAN_PASSIVE_SCAN |
-				  HOSTAPD_CHAN_NO_IBSS |
-				  HOSTAPD_CHAN_RADAR));
+			if (mode->channels[i].flag &
+			    (HOSTAPD_CHAN_DISABLED |
+			     HOSTAPD_CHAN_RADAR))
+				return NOT_ALLOWED;
+			if (mode->channels[i].flag &
+			    (HOSTAPD_CHAN_PASSIVE_SCAN |
+			     HOSTAPD_CHAN_NO_IBSS))
+				return PASSIVE_ONLY;
+			return ALLOWED;
 		}
 	}
 
-	return 0;
+	return NOT_ALLOWED;
 }
 
 
@@ -3015,7 +3466,7 @@
 	u8 min_chan;
 	u8 max_chan;
 	u8 inc;
-	enum { BW20, BW40PLUS, BW40MINUS } bw;
+	enum { BW20, BW40PLUS, BW40MINUS, BW80 } bw;
 };
 
 static struct p2p_oper_class_map op_class[] = {
@@ -3030,73 +3481,171 @@
 	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
 	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
 	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
+
+	/*
+	 * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
+	 * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
+	 * 80 MHz, but currently use the following definition for simplicity
+	 * (these center frequencies are not actual channels, which makes
+	 * has_channel() fail). wpas_p2p_verify_80mhz() should take care of
+	 * removing invalid channels.
+	 */
+	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
 	{ -1, 0, 0, 0, 0, BW20 }
 };
 
 
-static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
-				   struct hostapd_hw_modes *mode,
-				   u8 channel, u8 bw)
+static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s,
+				     struct hostapd_hw_modes *mode,
+				     u8 channel)
 {
-	int flag;
+	u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
+	unsigned int i;
 
-	if (!has_channel(wpa_s->global, mode, channel, &flag))
-		return -1;
-	if (bw == BW40MINUS &&
-	    (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
-	     !has_channel(wpa_s->global, mode, channel - 4, NULL)))
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
 		return 0;
-	if (bw == BW40PLUS &&
-	    (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
-	     !has_channel(wpa_s->global, mode, channel + 4, NULL)))
-		return 0;
-	return 1;
+
+	for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+		/*
+		 * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
+		 * so the center channel is 6 channels away from the start/end.
+		 */
+		if (channel >= center_channels[i] - 6 &&
+		    channel <= center_channels[i] + 6)
+			return center_channels[i];
+
+	return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s,
+					       struct hostapd_hw_modes *mode,
+					       u8 channel, u8 bw)
+{
+	u8 center_chan;
+	int i, flags;
+	enum chan_allowed res, ret = ALLOWED;
+
+	center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel);
+	if (!center_chan)
+		return NOT_ALLOWED;
+	if (center_chan >= 58 && center_chan <= 138)
+		return NOT_ALLOWED; /* Do not allow DFS channels for P2P */
+
+	/* check all the channels are available */
+	for (i = 0; i < 4; i++) {
+		int adj_chan = center_chan - 6 + i * 4;
+
+		res = has_channel(wpa_s->global, mode, adj_chan, &flags);
+		if (res == NOT_ALLOWED)
+			return NOT_ALLOWED;
+		if (res == PASSIVE_ONLY)
+			ret = PASSIVE_ONLY;
+
+		if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
+			return NOT_ALLOWED;
+		if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50))
+			return NOT_ALLOWED;
+		if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30))
+			return NOT_ALLOWED;
+		if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))
+			return NOT_ALLOWED;
+	}
+
+	return ret;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
+						 struct hostapd_hw_modes *mode,
+						 u8 channel, u8 bw)
+{
+	int flag = 0;
+	enum chan_allowed res, res2;
+
+	res2 = res = has_channel(wpa_s->global, mode, channel, &flag);
+	if (bw == BW40MINUS) {
+		if (!(flag & HOSTAPD_CHAN_HT40MINUS))
+			return NOT_ALLOWED;
+		res2 = has_channel(wpa_s->global, mode, channel - 4, NULL);
+	} else if (bw == BW40PLUS) {
+		if (!(flag & HOSTAPD_CHAN_HT40PLUS))
+			return NOT_ALLOWED;
+		res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
+	} else if (bw == BW80) {
+		res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+	}
+
+	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
+		return NOT_ALLOWED;
+	if (res == PASSIVE_ONLY || res2 == PASSIVE_ONLY)
+		return PASSIVE_ONLY;
+	return res;
 }
 
 
 static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
-				   struct p2p_channels *chan)
+				   struct p2p_channels *chan,
+				   struct p2p_channels *cli_chan)
 {
 	struct hostapd_hw_modes *mode;
-	int cla, op;
+	int cla, op, cli_cla;
 
 	if (wpa_s->hw.modes == NULL) {
 		wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
 			   "of all supported channels; assume dualband "
 			   "support");
-		return wpas_p2p_default_channels(wpa_s, chan);
+		return wpas_p2p_default_channels(wpa_s, chan, cli_chan);
 	}
 
-	cla = 0;
+	cla = cli_cla = 0;
 
 	for (op = 0; op_class[op].op_class; op++) {
 		struct p2p_oper_class_map *o = &op_class[op];
 		u8 ch;
-		struct p2p_reg_class *reg = NULL;
+		struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
 
 		mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
 		if (mode == NULL)
 			continue;
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
-			if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1)
-				continue;
-			if (reg == NULL) {
-				wpa_printf(MSG_DEBUG, "P2P: Add operating "
-					   "class %u", o->op_class);
-				reg = &chan->reg_class[cla];
-				cla++;
-				reg->reg_class = o->op_class;
+			enum chan_allowed res;
+			res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
+			if (res == ALLOWED) {
+				if (reg == NULL) {
+					wpa_printf(MSG_DEBUG, "P2P: Add operating class %u",
+						   o->op_class);
+					reg = &chan->reg_class[cla];
+					cla++;
+					reg->reg_class = o->op_class;
+				}
+				reg->channel[reg->channels] = ch;
+				reg->channels++;
+			} else if (res == PASSIVE_ONLY &&
+				   wpa_s->conf->p2p_add_cli_chan) {
+				if (cli_reg == NULL) {
+					wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)",
+						   o->op_class);
+					cli_reg = &cli_chan->reg_class[cli_cla];
+					cli_cla++;
+					cli_reg->reg_class = o->op_class;
+				}
+				cli_reg->channel[cli_reg->channels] = ch;
+				cli_reg->channels++;
 			}
-			reg->channel[reg->channels] = ch;
-			reg->channels++;
 		}
 		if (reg) {
 			wpa_hexdump(MSG_DEBUG, "P2P: Channels",
 				    reg->channel, reg->channels);
 		}
+		if (cli_reg) {
+			wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)",
+				    cli_reg->channel, cli_reg->channels);
+		}
 	}
 
 	chan->reg_classes = cla;
+	cli_chan->reg_classes = cli_cla;
 
 	return 0;
 }
@@ -3105,7 +3654,8 @@
 int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
 			   struct hostapd_hw_modes *mode, u8 channel)
 {
-	int op, ret;
+	int op;
+	enum chan_allowed ret;
 
 	for (op = 0; op_class[op].op_class; op++) {
 		struct p2p_oper_class_map *o = &op_class[op];
@@ -3116,18 +3666,24 @@
 			    o->bw == BW20 || ch != channel)
 				continue;
 			ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
-			if (ret < 0)
-				continue;
-			else if (ret > 0)
+			if (ret == ALLOWED)
 				return (o->bw == BW40MINUS) ? -1 : 1;
-			else
-				return 0;
 		}
 	}
 	return 0;
 }
 
 
+int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
+			      struct hostapd_hw_modes *mode, u8 channel)
+{
+	if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW80))
+		return 0;
+
+	return wpas_p2p_get_center_80mhz(wpa_s, mode, channel);
+}
+
+
 static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
 			size_t buf_len)
 {
@@ -3144,10 +3700,31 @@
 }
 
 
-static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+					      const u8 *ssid, size_t ssid_len)
 {
-	struct wpa_supplicant *wpa_s = ctx;
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		struct wpa_ssid *s = wpa_s->current_ssid;
+		if (s == NULL)
+			continue;
+		if (s->mode != WPAS_MODE_P2P_GO &&
+		    s->mode != WPAS_MODE_AP &&
+		    s->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+			continue;
+		if (s->ssid_len != ssid_len ||
+		    os_memcmp(ssid, s->ssid, ssid_len) != 0)
+			continue;
+		return wpa_s;
+	}
 
+	return NULL;
+
+}
+
+
+struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
+						  const u8 *peer_dev_addr)
+{
 	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
 		if (ssid == NULL)
@@ -3157,10 +3734,33 @@
 		if (wpa_s->wpa_state != WPA_COMPLETED &&
 		    wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
 			continue;
-		if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0)
-			return 1;
+		if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
+			return wpa_s;
 	}
 
+	return NULL;
+}
+
+
+static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL;
+}
+
+
+static int wpas_is_concurrent_session_active(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_supplicant *ifs;
+
+	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+		if (ifs == wpa_s)
+			continue;
+		if (ifs->wpa_state > WPA_ASSOCIATED)
+			return 1;
+	}
 	return 0;
 }
 
@@ -3198,7 +3798,20 @@
 	iface.ifname = wpa_s->pending_interface_name;
 	iface.driver = wpa_s->driver->name;
 	iface.driver_param = wpa_s->conf->driver_param;
-	iface.confname = wpa_s->confname;
+
+	/*
+	 * If a P2P Device configuration file was given, use it as the interface
+	 * configuration file (instead of using parent's configuration file.
+	 */
+	if (wpa_s->conf_p2p_dev) {
+		iface.confname = wpa_s->conf_p2p_dev;
+		iface.ctrl_interface = NULL;
+	} else {
+		iface.confname = wpa_s->confname;
+		iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+	}
+	iface.conf_p2p_dev = NULL;
+
 	p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
 	if (!p2pdev_wpa_s) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
@@ -3211,6 +3824,35 @@
 }
 
 
+static void wpas_presence_resp(void *ctx, const u8 *src, u8 status,
+			       const u8 *noa, size_t noa_len)
+{
+	struct wpa_supplicant *wpa_s, *intf = ctx;
+	char hex[100];
+
+	for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->waiting_presence_resp)
+			break;
+	}
+	if (!wpa_s) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response");
+		return;
+	}
+	wpa_s->waiting_presence_resp = 0;
+
+	wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len);
+	wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR
+		" status=%u noa=%s", MAC2STR(src), status, hex);
+}
+
+
+static int _wpas_p2p_in_progress(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	return wpas_p2p_in_progress(wpa_s);
+}
+
+
 /**
  * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
  * @global: Pointer to global data from wpa_supplicant_init()
@@ -3232,25 +3874,6 @@
 	if (global->p2p)
 		return 0;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-		struct p2p_params params;
-
-		wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management");
-		os_memset(&params, 0, sizeof(params));
-		params.dev_name = wpa_s->conf->device_name;
-		os_memcpy(params.pri_dev_type, wpa_s->conf->device_type,
-			  WPS_DEV_TYPE_LEN);
-		params.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
-		os_memcpy(params.sec_dev_type,
-			  wpa_s->conf->sec_device_type,
-			  params.num_sec_dev_types * WPS_DEV_TYPE_LEN);
-
-		if (wpa_drv_p2p_set_params(wpa_s, &params) < 0)
-			return -1;
-
-		return 0;
-	}
-
 	os_memset(&p2p, 0, sizeof(p2p));
 	p2p.cb_ctx = wpa_s;
 	p2p.debug_print = wpas_p2p_debug_print;
@@ -3275,6 +3898,9 @@
 	p2p.invitation_result = wpas_invitation_result;
 	p2p.get_noa = wpas_get_noa;
 	p2p.go_connected = wpas_go_connected;
+	p2p.presence_resp = wpas_presence_resp;
+	p2p.is_concurrent_session_active = wpas_is_concurrent_session_active;
+	p2p.is_p2p_in_progress = _wpas_p2p_in_progress;
 
 	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -3292,6 +3918,7 @@
 	    wpa_s->conf->p2p_listen_channel) {
 		p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
 		p2p.channel = wpa_s->conf->p2p_listen_channel;
+		p2p.channel_forced = 1;
 	} else {
 		p2p.reg_class = 81;
 		/*
@@ -3300,6 +3927,7 @@
 		 */
 		os_get_random((u8 *) &r, sizeof(r));
 		p2p.channel = 1 + (r % 3) * 5;
+		p2p.channel_forced = 0;
 	}
 	wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel);
 
@@ -3335,7 +3963,7 @@
 	} else
 		os_memcpy(p2p.country, "XX\x04", 3);
 
-	if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) {
+	if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels)) {
 		wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
 			   "channel list");
 		return -1;
@@ -3366,6 +3994,12 @@
 
 	p2p.max_listen = wpa_s->max_remain_on_chan;
 
+	if (wpa_s->conf->p2p_passphrase_len >= 8 &&
+	    wpa_s->conf->p2p_passphrase_len <= 63)
+		p2p.passphrase_len = wpa_s->conf->p2p_passphrase_len;
+	else
+		p2p.passphrase_len = 8;
+
 	global->p2p = p2p_init(&p2p);
 	if (global->p2p == NULL)
 		return -1;
@@ -3378,6 +4012,8 @@
 			global->p2p, wpa_s->conf->wps_vendor_ext[i]);
 	}
 
+	p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq);
+
 	return 0;
 }
 
@@ -3402,15 +4038,24 @@
 
 	os_free(wpa_s->go_params);
 	wpa_s->go_params = NULL;
-#ifdef ANDROID_P2P
-	eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
-#endif
+	eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 	wpa_s->p2p_long_listen = 0;
 	eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 	wpas_p2p_remove_pending_group_interface(wpa_s);
+	eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
+	wpas_p2p_listen_work_done(wpa_s);
+	if (wpa_s->p2p_send_action_work) {
+		os_free(wpa_s->p2p_send_action_work->ctx);
+		radio_work_done(wpa_s->p2p_send_action_work);
+		wpa_s->p2p_send_action_work = NULL;
+	}
+	eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
+
+	wpabuf_free(wpa_s->p2p_oob_dev_pw);
+	wpa_s->p2p_oob_dev_pw = NULL;
 
 	/* TODO: remove group interface from the driver if this wpa_s instance
 	 * is on top of a P2P group interface */
@@ -3423,7 +4068,7 @@
  *
  * This function deinitializes the global (per device) P2P module.
  */
-void wpas_p2p_deinit_global(struct wpa_global *global)
+static void wpas_p2p_deinit_global(struct wpa_global *global)
 {
 	struct wpa_supplicant *wpa_s, *tmp;
 
@@ -3495,12 +4140,6 @@
 	if (persistent_group && wpa_s->conf->persistent_reconnect)
 		persistent_group = 2;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-		return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method,
-					   go_intent, own_interface_addr,
-					   force_freq, persistent_group);
-	}
-
 	/*
 	 * Increase GO config timeout if HT40 is used since it takes some time
 	 * to scan channels for coex purposes before the BSS can be started.
@@ -3512,7 +4151,9 @@
 			   go_intent, own_interface_addr, force_freq,
 			   persistent_group, ssid ? ssid->ssid : NULL,
 			   ssid ? ssid->ssid_len : 0,
-			   wpa_s->p2p_pd_before_go_neg, pref_freq);
+			   wpa_s->p2p_pd_before_go_neg, pref_freq,
+			   wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+			   0);
 }
 
 
@@ -3526,13 +4167,12 @@
 	if (persistent_group && wpa_s->conf->persistent_reconnect)
 		persistent_group = 2;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return -1;
-
 	return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
 			     go_intent, own_interface_addr, force_freq,
 			     persistent_group, ssid ? ssid->ssid : NULL,
-			     ssid ? ssid->ssid_len : 0, pref_freq);
+			     ssid ? ssid->ssid_len : 0, pref_freq,
+			     wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+			     0);
 }
 
 
@@ -3562,26 +4202,25 @@
 
 static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
 {
-	int *freqs, res, num, i;
+	int res;
+	unsigned int num, i;
+	struct wpa_used_freq_data *freqs;
 
 	if (wpas_p2p_num_unused_channels(wpa_s) > 0) {
 		/* Multiple channels are supported and not all are in use */
 		return 0;
 	}
 
-	freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+	freqs = os_calloc(wpa_s->num_multichan_concurrent,
+			  sizeof(struct wpa_used_freq_data));
 	if (!freqs)
 		return 1;
 
 	num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
 					wpa_s->num_multichan_concurrent);
-	if (num < 0) {
-		res = 1;
-		goto exit_free;
-	}
 
 	for (i = 0; i < num; i++) {
-		if (freqs[i] == freq) {
+		if (freqs[i].freq == freq) {
 			wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used",
 				   freq);
 			res = 0;
@@ -3589,6 +4228,7 @@
 		}
 	}
 
+	wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies");
 	res = 1;
 
 exit_free:
@@ -3612,7 +4252,8 @@
 		return 0;
 	}
 
-	updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update);
+	updated = os_reltime_before(&wpa_s->p2p_auto_started,
+				    &bss->last_update);
 	wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
 		   "%ld.%06ld (%supdated in last scan)",
 		   bss->last_update.sec, bss->last_update.usec,
@@ -3625,7 +4266,7 @@
 static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
 				   struct wpa_scan_results *scan_res)
 {
-	struct wpa_bss *bss;
+	struct wpa_bss *bss = NULL;
 	int freq;
 	u8 iface_addr[ETH_ALEN];
 
@@ -3657,7 +4298,7 @@
 					   MAC2STR(wpa_s->
 						   pending_join_dev_addr),
 					   freq);
-				wpas_p2p_join_scan_req(wpa_s, freq);
+				wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0);
 				return;
 			}
 		}
@@ -3695,7 +4336,8 @@
 					 wpa_s->p2p_connect_freq,
 					 wpa_s->p2p_persistent_id,
 					 wpa_s->p2p_pd_before_go_neg,
-					 wpa_s->p2p_go_ht40);
+					 wpa_s->p2p_go_ht40,
+					 wpa_s->p2p_go_vht);
 			return;
 		}
 
@@ -3729,11 +4371,27 @@
 		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
 			   "from P2P peer table: %d MHz", freq);
 	}
-	bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
+	if (wpa_s->p2p_join_ssid_len) {
+		wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
+			   MACSTR " and SSID %s",
+			   MAC2STR(wpa_s->pending_join_iface_addr),
+			   wpa_ssid_txt(wpa_s->p2p_join_ssid,
+					wpa_s->p2p_join_ssid_len));
+		bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
+				  wpa_s->p2p_join_ssid,
+				  wpa_s->p2p_join_ssid_len);
+	}
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
+			   MACSTR, MAC2STR(wpa_s->pending_join_iface_addr));
+		bss = wpa_bss_get_bssid_latest(wpa_s,
+					       wpa_s->pending_join_iface_addr);
+	}
 	if (bss) {
 		freq = bss->freq;
 		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
-			   "from BSS table: %d MHz", freq);
+			   "from BSS table: %d MHz (SSID %s)", freq,
+			   wpa_ssid_txt(bss->ssid, bss->ssid_len));
 	}
 	if (freq > 0) {
 		u16 method;
@@ -3801,34 +4459,33 @@
 
 start:
 	/* Start join operation immediately */
-	wpas_p2p_join_start(wpa_s);
+	wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 }
 
 
-static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
+				   const u8 *ssid, size_t ssid_len)
 {
 	int ret;
 	struct wpa_driver_scan_params params;
 	struct wpabuf *wps_ie, *ies;
 	size_t ielen;
 	int freqs[2] = { 0, 0 };
-#ifdef ANDROID_P2P
-	int oper_freq;
 
-	/* If freq is not provided, check the operating freq of the GO and do a
-	 * a directed scan to save time
-	 */
-	if(!freq) {
-		freq = (oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
-			 wpa_s->pending_join_iface_addr) == -1) ? 0 : oper_freq; 
-	}
-#endif
 	os_memset(&params, 0, sizeof(params));
 
 	/* P2P Wildcard SSID */
 	params.num_ssids = 1;
-	params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
-	params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+	if (ssid && ssid_len) {
+		params.ssids[0].ssid = ssid;
+		params.ssids[0].ssid_len = ssid_len;
+		os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len);
+		wpa_s->p2p_join_ssid_len = ssid_len;
+	} else {
+		params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+		params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+		wpa_s->p2p_join_ssid_len = 0;
+	}
 
 	wpa_s->wps->dev.p2p = 1;
 	wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
@@ -3854,6 +4511,18 @@
 	params.p2p_probe = 1;
 	params.extra_ies = wpabuf_head(ies);
 	params.extra_ies_len = wpabuf_len(ies);
+
+	if (!freq) {
+		int oper_freq;
+		/*
+		 * If freq is not provided, check the operating freq of the GO
+		 * and use a single channel scan on if possible.
+		 */
+		oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
+					      wpa_s->pending_join_iface_addr);
+		if (oper_freq > 0)
+			freq = oper_freq;
+	}
 	if (freq > 0) {
 		freqs[0] = freq;
 		params.freqs = freqs;
@@ -3865,8 +4534,9 @@
 	 */
 	ret = wpa_drv_scan(wpa_s, &params);
 	if (!ret) {
-		os_get_time(&wpa_s->scan_trigger_time);
+		os_get_reltime(&wpa_s->scan_trigger_time);
 		wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+		wpa_s->own_scan_requested = 1;
 	}
 
 	wpabuf_free(ies);
@@ -3884,18 +4554,23 @@
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
-	wpas_p2p_join_scan_req(wpa_s, 0);
+	wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0);
 }
 
 
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
 			 const u8 *dev_addr, enum p2p_wps_method wps_method,
-			 int auto_join)
+			 int auto_join, int op_freq,
+			 const u8 *ssid, size_t ssid_len)
 {
 	wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
-		   MACSTR " dev " MACSTR ")%s",
-		   MAC2STR(iface_addr), MAC2STR(dev_addr),
+		   MACSTR " dev " MACSTR " op_freq=%d)%s",
+		   MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq,
 		   auto_join ? " (auto_join)" : "");
+	if (ssid && ssid_len) {
+		wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s",
+			   wpa_ssid_txt(ssid, ssid_len));
+	}
 
 	wpa_s->p2p_auto_pd = 0;
 	wpa_s->p2p_auto_join = !!auto_join;
@@ -3907,12 +4582,13 @@
 	wpas_p2p_stop_find(wpa_s);
 
 	wpa_s->p2p_join_scan_count = 0;
-	wpas_p2p_join_scan(wpa_s, NULL);
+	wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len);
 	return 0;
 }
 
 
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+			       const u8 *ssid, size_t ssid_len)
 {
 	struct wpa_supplicant *group;
 	struct p2p_go_neg_results res;
@@ -3940,14 +4616,25 @@
 	group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
 	os_memset(&res, 0, sizeof(res));
+	os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
 	os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
 		  ETH_ALEN);
 	res.wps_method = wpa_s->pending_join_wps_method;
-	bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
-	if (bss) {
-		res.freq = bss->freq;
-		res.ssid_len = bss->ssid_len;
-		os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+	if (freq && ssid && ssid_len) {
+		res.freq = freq;
+		res.ssid_len = ssid_len;
+		os_memcpy(res.ssid, ssid, ssid_len);
+	} else {
+		bss = wpa_bss_get_bssid_latest(wpa_s,
+					       wpa_s->pending_join_iface_addr);
+		if (bss) {
+			res.freq = bss->freq;
+			res.ssid_len = bss->ssid_len;
+			os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+			wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
+				   bss->freq,
+				   wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		}
 	}
 
 	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
@@ -3973,23 +4660,38 @@
 
 
 static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
-				int *force_freq, int *pref_freq)
+				int *force_freq, int *pref_freq, int go)
 {
-	int *freqs, res;
+	struct wpa_used_freq_data *freqs;
+	int res, best_freq, num_unused;
 	unsigned int freq_in_use = 0, num, i;
 
-	freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+	freqs = os_calloc(wpa_s->num_multichan_concurrent,
+			  sizeof(struct wpa_used_freq_data));
 	if (!freqs)
 		return -1;
 
-	num = get_shared_radio_freqs(wpa_s, freqs,
-				     wpa_s->num_multichan_concurrent);
+	num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+					wpa_s->num_multichan_concurrent);
+
+	/*
+	 * It is possible that the total number of used frequencies is bigger
+	 * than the number of frequencies used for P2P, so get the system wide
+	 * number of unused frequencies.
+	 */
+	num_unused = wpas_p2p_num_unused_channels(wpa_s);
+
 	wpa_printf(MSG_DEBUG,
-		   "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u",
-		   freq, wpa_s->num_multichan_concurrent, num);
+		   "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d",
+		   freq, wpa_s->num_multichan_concurrent, num, num_unused);
 
 	if (freq > 0) {
-		if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+		int ret;
+		if (go)
+			ret = p2p_supported_freq(wpa_s->global->p2p, freq);
+		else
+			ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
+		if (!ret) {
 			wpa_printf(MSG_DEBUG, "P2P: The forced channel "
 				   "(%u MHz) is not supported for P2P uses",
 				   freq);
@@ -3998,11 +4700,11 @@
 		}
 
 		for (i = 0; i < num; i++) {
-			if (freqs[i] == freq)
+			if (freqs[i].freq == freq)
 				freq_in_use = 1;
 		}
 
-		if (num == wpa_s->num_multichan_concurrent && !freq_in_use) {
+		if (num_unused <= 0 && !freq_in_use) {
 			wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels",
 				   freq);
 			res = -2;
@@ -4014,38 +4716,28 @@
 		goto exit_ok;
 	}
 
-	for (i = 0; i < num; i++) {
-		if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i]))
-			continue;
+	best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
 
-#ifndef ANDROID_P2P
-		wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use",
-			   *force_freq);
-		*force_freq = freqs[i];
-#endif
-
-		if (*pref_freq == 0 && num < wpa_s->num_multichan_concurrent) {
-			wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency we are already using");
-			*pref_freq = freqs[i];
-#ifdef ANDROID_P2P
+	/* We have a candidate frequency to use */
+	if (best_freq > 0) {
+		if (*pref_freq == 0 && num_unused > 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using",
+				   best_freq);
+			*pref_freq = best_freq;
 		} else {
 			wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use",
-				   *force_freq);
-			*force_freq = freqs[i];
-#endif
+				   best_freq);
+			*force_freq = best_freq;
 		}
-		break;
-	}
-
-	if (i == num) {
-		if (num < wpa_s->num_multichan_concurrent) {
-			wpa_printf(MSG_DEBUG, "P2P: Current operating channels are not available for P2P. Try to use another channel");
-			*force_freq = 0;
-		} else {
-			wpa_printf(MSG_DEBUG, "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group");
-			res = -2;
-			goto exit_free;
-		}
+	} else if (num_unused > 0) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Current operating channels are not available for P2P. Try to use another channel");
+		*force_freq = 0;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group");
+		res = -2;
+		goto exit_free;
 	}
 
 exit_ok:
@@ -4074,6 +4766,7 @@
  * @pd: Whether to send Provision Discovery prior to GO Negotiation as an
  *	interoperability workaround when initiating group formation
  * @ht40: Start GO with 40 MHz channel width
+ * @vht:  Start GO with VHT support
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *	failure, -2 on failure due to channel not currently available,
  *	-3 if forced channel is not supported
@@ -4082,7 +4775,7 @@
 		     const char *pin, enum p2p_wps_method wps_method,
 		     int persistent_group, int auto_join, int join, int auth,
 		     int go_intent, int freq, int persistent_id, int pd,
-		     int ht40)
+		     int ht40, int vht)
 {
 	int force_freq = 0, pref_freq = 0;
 	int ret = 0, res;
@@ -4103,6 +4796,8 @@
 	os_free(wpa_s->global->add_psk);
 	wpa_s->global->add_psk = NULL;
 
+	wpa_s->global->p2p_fail_on_wps_complete = 0;
+
 	if (go_intent < 0)
 		go_intent = wpa_s->conf->p2p_go_intent;
 
@@ -4117,6 +4812,7 @@
 	wpa_s->p2p_fallback_to_go_neg = 0;
 	wpa_s->p2p_pd_before_go_neg = !!pd;
 	wpa_s->p2p_go_ht40 = !!ht40;
+	wpa_s->p2p_go_vht = !!vht;
 
 	if (pin)
 		os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -4146,7 +4842,7 @@
 					 dev_addr);
 		}
 		if (auto_join) {
-			os_get_time(&wpa_s->p2p_auto_started);
+			os_get_reltime(&wpa_s->p2p_auto_started);
 			wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
 				   "%ld.%06ld",
 				   wpa_s->p2p_auto_started.sec,
@@ -4154,15 +4850,17 @@
 		}
 		wpa_s->user_initiated_pd = 1;
 		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
-				  auto_join) < 0)
+				  auto_join, freq, NULL, 0) < 0)
 			return -1;
 		return ret;
 	}
 
-	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq);
+	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+				   go_intent == 15);
 	if (res)
 		return res;
-	wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
+	wpas_p2p_set_own_freq_preference(wpa_s,
+					 force_freq ? force_freq : pref_freq);
 
 	wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
 
@@ -4219,20 +4917,20 @@
 		p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
 			      wpa_s->pending_listen_duration);
 		wpa_s->pending_listen_freq = 0;
+	} else {
+		wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)",
+			   wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+			   freq, duration);
 	}
 }
 
 
-static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
-				 unsigned int timeout)
+int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout)
 {
 	/* Limit maximum Listen state time based on driver limitation. */
 	if (timeout > wpa_s->max_remain_on_chan)
 		timeout = wpa_s->max_remain_on_chan;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return wpa_drv_p2p_listen(wpa_s, timeout);
-
 	return p2p_listen(wpa_s->global->p2p, timeout);
 }
 
@@ -4252,14 +4950,15 @@
 	wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
 		   "(p2p_long_listen=%d ms pending_action_tx=%p)",
 		   wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s));
+	wpas_p2p_listen_work_done(wpa_s);
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
+	if (wpa_s->p2p_long_listen > 0)
+		wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
 	if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
 		return; /* P2P module started a new operation */
 	if (offchannel_pending_action_tx(wpa_s))
 		return;
-	if (wpa_s->p2p_long_listen > 0)
-		wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
 	if (wpa_s->p2p_long_listen > 0) {
 		wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
 		wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
@@ -4322,8 +5021,8 @@
 		wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
 			   "band");
 		if (wpa_s->best_24_freq > 0 &&
-		    p2p_supported_freq(wpa_s->global->p2p,
-				       wpa_s->best_24_freq)) {
+		    p2p_supported_freq_go(wpa_s->global->p2p,
+					  wpa_s->best_24_freq)) {
 			freq = wpa_s->best_24_freq;
 			wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
 				   "channel: %d MHz", freq);
@@ -4339,7 +5038,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
 			   "band");
 		if (wpa_s->best_5_freq > 0 &&
-		    p2p_supported_freq(wpa_s->global->p2p,
+		    p2p_supported_freq_go(wpa_s->global->p2p,
 				       wpa_s->best_5_freq)) {
 			freq = wpa_s->best_5_freq;
 			wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
@@ -4347,7 +5046,7 @@
 		} else {
 			os_get_random((u8 *) &r, sizeof(r));
 			freq = 5180 + (r % 4) * 20;
-			if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
 				wpa_printf(MSG_DEBUG, "P2P: Could not select "
 					   "5 GHz channel for P2P group");
 				return -1;
@@ -4357,7 +5056,7 @@
 		}
 	}
 
-	if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
+	if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
 		wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
 			   "(%u MHz) is not supported for P2P uses",
 			   freq);
@@ -4368,18 +5067,52 @@
 }
 
 
+static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
+					struct p2p_go_neg_results *params,
+					const struct p2p_channels *channels)
+{
+	unsigned int i, r;
+
+	/* first try some random selection of the social channels */
+	os_get_random((u8 *) &r, sizeof(r));
+
+	for (i = 0; i < 3; i++) {
+		params->freq = 2412 + ((r + i) % 3) * 25;
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+		    freq_included(channels, params->freq))
+			goto out;
+	}
+
+	/* try all channels in reg. class 81 */
+	for (i = 0; i < 11; i++) {
+		params->freq = 2412 + i * 5;
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+		    freq_included(channels, params->freq))
+			goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel allowed");
+	return -1;
+out:
+	wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
+		   params->freq);
+	return 0;
+}
+
+
 static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
 				   struct p2p_go_neg_results *params,
-				   int freq, int ht40,
+				   int freq, int ht40, int vht,
 				   const struct p2p_channels *channels)
 {
-	int res, *freqs;
-	unsigned int pref_freq;
+	struct wpa_used_freq_data *freqs;
+	unsigned int pref_freq, cand_freq;
 	unsigned int num, i;
 
 	os_memset(params, 0, sizeof(*params));
 	params->role_go = 1;
 	params->ht40 = ht40;
+	params->vht = vht;
 	if (freq) {
 		if (!freq_included(channels, freq)) {
 			wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
@@ -4410,24 +5143,24 @@
 			   "frequency %d MHz", params->freq);
 	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
 		   wpa_s->best_overall_freq > 0 &&
-		   p2p_supported_freq(wpa_s->global->p2p,
-				      wpa_s->best_overall_freq) &&
+		   p2p_supported_freq_go(wpa_s->global->p2p,
+					 wpa_s->best_overall_freq) &&
 		   freq_included(channels, wpa_s->best_overall_freq)) {
 		params->freq = wpa_s->best_overall_freq;
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
 			   "channel %d MHz", params->freq);
 	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
 		   wpa_s->best_24_freq > 0 &&
-		   p2p_supported_freq(wpa_s->global->p2p,
-				      wpa_s->best_24_freq) &&
+		   p2p_supported_freq_go(wpa_s->global->p2p,
+					 wpa_s->best_24_freq) &&
 		   freq_included(channels, wpa_s->best_24_freq)) {
 		params->freq = wpa_s->best_24_freq;
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
 			   "channel %d MHz", params->freq);
 	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
 		   wpa_s->best_5_freq > 0 &&
-		   p2p_supported_freq(wpa_s->global->p2p,
-				      wpa_s->best_5_freq) &&
+		   p2p_supported_freq_go(wpa_s->global->p2p,
+					 wpa_s->best_5_freq) &&
 		   freq_included(channels, wpa_s->best_5_freq)) {
 		params->freq = wpa_s->best_5_freq;
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
@@ -4438,47 +5171,37 @@
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
 			   "channels", params->freq);
 	} else {
-		int chan;
-		for (chan = 0; chan < 11; chan++) {
-			params->freq = 2412 + chan * 5;
-			if (!wpas_p2p_disallowed_freq(wpa_s->global,
-						      params->freq) &&
-			    freq_included(channels, params->freq))
-				break;
-		}
-		if (chan == 11) {
-			wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel "
-				   "allowed");
+		/* no preference, select some channel */
+		if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
 			return -1;
-		}
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
-			   "known)", params->freq);
 	}
 
-	freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+	freqs = os_calloc(wpa_s->num_multichan_concurrent,
+			  sizeof(struct wpa_used_freq_data));
 	if (!freqs)
 		return -1;
 
-	res = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+	num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
 					wpa_s->num_multichan_concurrent);
-	if (res < 0) {
-		os_free(freqs);
-		return -1;
-	}
-	num = res;
 
-	if (!freq) {
+	cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+
+	/* First try the best used frequency if possible */
+	if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
+		params->freq = cand_freq;
+	} else if (!freq) {
+		/* Try any of the used frequencies */
 		for (i = 0; i < num; i++) {
-			if (freq_included(channels, freqs[i])) {
+			if (freq_included(channels, freqs[i].freq)) {
 				wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
-					   freqs[i]);
-				params->freq = freqs[i];
+					   freqs[i].freq);
+				params->freq = freqs[i].freq;
 				break;
 			}
 		}
 
 		if (i == num) {
-			if (num == wpa_s->num_multichan_concurrent) {
+			if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
 				wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
 				os_free(freqs);
 				return -1;
@@ -4488,20 +5211,22 @@
 		}
 	} else {
 		for (i = 0; i < num; i++) {
-			if (freqs[i] == freq)
+			if (freqs[i].freq == freq)
 				break;
 		}
 
 		if (i == num) {
-			if (num == wpa_s->num_multichan_concurrent) {
-				wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
+			if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+				if (freq)
+					wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
 				os_free(freqs);
 				return -1;
 			} else {
-				wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
+				wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
 			}
 		}
 	}
+
 	os_free(freqs);
 	return 0;
 }
@@ -4516,6 +5241,7 @@
 	if (!wpas_p2p_create_iface(wpa_s)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group "
 			"operations");
+		wpa_s->p2p_first_connection_timeout = 0;
 		return wpa_s;
 	}
 
@@ -4535,6 +5261,7 @@
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
 		group_wpa_s->ifname);
+	group_wpa_s->p2p_first_connection_timeout = 0;
 	return group_wpa_s;
 }
 
@@ -4544,13 +5271,15 @@
  * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
  * @persistent_group: Whether to create a persistent group
  * @freq: Frequency for the group or 0 to indicate no hardcoding
+ * @ht40: Start GO with 40 MHz channel width
+ * @vht:  Start GO with VHT support
  * Returns: 0 on success, -1 on failure
  *
  * This function creates a new P2P group with the local end as the Group Owner,
  * i.e., without using Group Owner Negotiation.
  */
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-		       int freq, int ht40)
+		       int freq, int ht40, int vht)
 {
 	struct p2p_go_neg_results params;
 
@@ -4568,10 +5297,10 @@
 	if (freq < 0)
 		return -1;
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, NULL))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, NULL))
 		return -1;
 	if (params.freq &&
-	    !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
 		wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
 			   "(%u MHz) is not supported for P2P uses",
 			   params.freq);
@@ -4590,7 +5319,8 @@
 
 
 static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
-				 struct wpa_ssid *params, int addr_allocated)
+				 struct wpa_ssid *params, int addr_allocated,
+				 int freq)
 {
 	struct wpa_ssid *ssid;
 
@@ -4626,9 +5356,16 @@
 	if (params->passphrase)
 		ssid->passphrase = os_strdup(params->passphrase);
 
-	wpa_supplicant_select_network(wpa_s, ssid);
-
 	wpa_s->show_group_started = 1;
+	wpa_s->p2p_in_invitation = 1;
+	wpa_s->p2p_invite_go_freq = freq;
+
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+			     NULL);
+	eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+			       wpas_p2p_group_formation_timeout,
+			       wpa_s->parent, NULL);
+	wpa_supplicant_select_network(wpa_s, ssid);
 
 	return 0;
 }
@@ -4636,11 +5373,12 @@
 
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
-				  int freq, int ht40,
-				  const struct p2p_channels *channels)
+				  int force_freq, int neg_freq, int ht40,
+				  int vht, const struct p2p_channels *channels,
+				  int connection_timeout)
 {
 	struct p2p_go_neg_results params;
-	int go = 0;
+	int go = 0, freq;
 
 	if (ssid->disabled != 2 || ssid->ssid == NULL)
 		return -1;
@@ -4660,17 +5398,23 @@
 
 	wpa_s->p2p_fallback_to_go_neg = 0;
 
+	if (force_freq > 0) {
+		freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
+		if (freq < 0)
+			return -1;
+	} else {
+		freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
+		if (freq < 0 || (freq > 0 && !freq_included(channels, freq)))
+			freq = 0;
+	}
+
 	if (ssid->mode == WPAS_MODE_INFRA)
-		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq);
 
 	if (ssid->mode != WPAS_MODE_P2P_GO)
 		return -1;
 
-	freq = wpas_p2p_select_go_freq(wpa_s, freq);
-	if (freq < 0)
-		return -1;
-
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, channels))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
 		return -1;
 
 	params.role_go = 1;
@@ -4694,6 +5438,7 @@
 	if (wpa_s == NULL)
 		return -1;
 
+	wpa_s->p2p_first_connection_timeout = connection_timeout;
 	wpas_start_wps_go(wpa_s, &params, 0);
 
 	return 0;
@@ -4731,9 +5476,14 @@
 	if (!wpa_s->ap_iface)
 		return;
 	wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
-	if (idle)
+	if (idle) {
+		if (wpa_s->global->p2p_fail_on_wps_complete &&
+		    wpa_s->p2p_in_provisioning) {
+			wpas_p2p_grpform_fail_after_wps(wpa_s);
+			return;
+		}
 		wpas_p2p_set_group_idle_timeout(wpa_s);
-	else
+	} else
 		eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 }
 
@@ -4744,8 +5494,6 @@
 	struct p2p_group *group;
 	struct p2p_group_config *cfg;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return NULL;
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return NULL;
 
@@ -4765,6 +5513,7 @@
 		cfg->max_clients = wpa_s->conf->max_num_sta;
 	os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
 	cfg->ssid_len = ssid->ssid_len;
+	cfg->freq = ssid->frequency;
 	cfg->cb_ctx = wpa_s;
 	cfg->ie_update = wpas_p2p_ie_update;
 	cfg->idle_update = wpas_p2p_idle_update;
@@ -4801,6 +5550,7 @@
 
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
 			     NULL);
+	wpa_s->p2p_go_group_formation_completed = 1;
 	if (ssid && ssid->mode == WPAS_MODE_INFRA) {
 		/*
 		 * Use a separate timeout for initial data connection to
@@ -4808,14 +5558,31 @@
 		 * something goes wrong in this step before the P2P group idle
 		 * timeout mechanism is taken into use.
 		 */
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Re-start group formation timeout (%d seconds) as client for initial connection",
+			P2P_MAX_INITIAL_CONN_WAIT);
 		eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
 				       wpas_p2p_group_formation_timeout,
 				       wpa_s->parent, NULL);
+	} else if (ssid) {
+		/*
+		 * Use a separate timeout for initial data connection to
+		 * complete to allow the group to be removed automatically if
+		 * the client does not complete data connection successfully.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Re-start group formation timeout (%d seconds) as GO for initial connection",
+			P2P_MAX_INITIAL_CONN_WAIT_GO);
+		eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0,
+				       wpas_p2p_group_formation_timeout,
+				       wpa_s->parent, NULL);
+		/*
+		 * Complete group formation on first successful data connection
+		 */
+		wpa_s->p2p_go_group_formation_completed = 0;
 	}
 	if (wpa_s->global->p2p)
 		p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
-	else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		wpa_drv_wps_success_cb(wpa_s, peer_addr);
 	wpas_group_formation_completed(wpa_s, 1);
 }
 
@@ -4836,6 +5603,31 @@
 	}
 
 	wpas_notify_p2p_wps_failed(wpa_s, fail);
+
+	if (wpa_s == wpa_s->global->p2p_group_formation) {
+		/*
+		 * Allow some time for the failed WPS negotiation exchange to
+		 * complete, but remove the group since group formation cannot
+		 * succeed after provisioning failure.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout");
+		wpa_s->global->p2p_fail_on_wps_complete = 1;
+		eloop_deplete_timeout(0, 50000,
+				      wpas_p2p_group_formation_timeout,
+				      wpa_s->parent, NULL);
+	}
+}
+
+
+int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->global->p2p_fail_on_wps_complete ||
+	    !wpa_s->p2p_in_provisioning)
+		return 0;
+
+	wpas_p2p_grpform_fail_after_wps(wpa_s);
+
+	return 1;
 }
 
 
@@ -4868,7 +5660,7 @@
 		wpa_s->auto_pd_scan_retry = 0;
 		wpas_p2p_stop_find(wpa_s);
 		wpa_s->p2p_join_scan_count = 0;
-		os_get_time(&wpa_s->p2p_auto_started);
+		os_get_reltime(&wpa_s->p2p_auto_started);
 		wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld",
 			   wpa_s->p2p_auto_started.sec,
 			   wpa_s->p2p_auto_started.usec);
@@ -4876,12 +5668,6 @@
 		return 0;
 	}
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-		return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
-						 config_methods,
-						 use == WPAS_P2P_PD_FOR_JOIN);
-	}
-
 	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
 		return -1;
 
@@ -4903,6 +5689,8 @@
 	if (!offchannel_pending_action_tx(wpa_s))
 		return;
 
+	wpas_p2p_action_tx_clear(wpa_s);
+
 	wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
 		   "operation request");
 	offchannel_clear_pending_action_tx(wpa_s);
@@ -4917,9 +5705,6 @@
 	wpas_p2p_clear_pending_action_tx(wpa_s);
 	wpa_s->p2p_long_listen = 0;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return wpa_drv_p2p_find(wpa_s, timeout, type);
-
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
 	    wpa_s->p2p_in_provisioning)
 		return -1;
@@ -4938,12 +5723,6 @@
 	wpa_s->p2p_long_listen = 0;
 	eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
-	wpa_s->global->p2p_cb_on_scan_complete = 0;
-
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-		wpa_drv_p2p_stop_find(wpa_s);
-		return 1;
-	}
 
 	if (wpa_s->global->p2p)
 		p2p_stop_find(wpa_s->global->p2p);
@@ -5081,7 +5860,7 @@
 }
 
 
-void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
 {
 	p2p_group_deinit(wpa_s->p2p_group);
 	wpa_s->p2p_group = NULL;
@@ -5097,9 +5876,6 @@
 {
 	wpa_s->p2p_long_listen = 0;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return wpa_drv_p2p_reject(wpa_s, addr);
-
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
@@ -5110,12 +5886,13 @@
 /* Invite to reinvoke a persistent group */
 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 ht40, int pref_freq)
+		    int ht40, int vht, int pref_freq)
 {
 	enum p2p_invite_role role;
 	u8 *bssid = NULL;
 	int force_freq = 0;
 	int res;
+	int no_pref_freq_given = pref_freq == 0;
 
 	wpa_s->global->p2p_invite_group = NULL;
 	if (peer_addr)
@@ -5149,21 +5926,26 @@
 	}
 	wpa_s->pending_invite_ssid_id = ssid->id;
 
-	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq);
+	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+				   role == P2P_INVITE_ROLE_GO);
 	if (res)
 		return res;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
-					  ssid->ssid, ssid->ssid_len,
-					  go_dev_addr, 1);
-
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
+	if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
+	    no_pref_freq_given && pref_freq > 0 &&
+	    wpa_s->num_multichan_concurrent > 1 &&
+	    wpas_p2p_num_unused_channels(wpa_s) > 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration",
+			   pref_freq);
+		pref_freq = 0;
+	}
+
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
-			  1, pref_freq);
+			  1, pref_freq, -1);
 }
 
 
@@ -5176,11 +5958,12 @@
 	u8 *bssid = NULL;
 	struct wpa_ssid *ssid;
 	int persistent;
-	int force_freq = 0, pref_freq = 0;
+	int freq = 0, force_freq = 0, pref_freq = 0;
 	int res;
 
 	wpa_s->p2p_persistent_go_freq = 0;
 	wpa_s->p2p_go_ht40 = 0;
+	wpa_s->p2p_go_vht = 0;
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -5208,6 +5991,7 @@
 		bssid = wpa_s->own_addr;
 		if (go_dev_addr == NULL)
 			go_dev_addr = wpa_s->global->p2p_dev_addr;
+		freq = ssid->frequency;
 	} else {
 		role = P2P_INVITE_ROLE_CLIENT;
 		if (wpa_s->wpa_state < WPA_ASSOCIATED) {
@@ -5219,36 +6003,35 @@
 		if (go_dev_addr == NULL &&
 		    !is_zero_ether_addr(wpa_s->go_dev_addr))
 			go_dev_addr = wpa_s->go_dev_addr;
+		freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
+			(int) wpa_s->assoc_freq;
 	}
 	wpa_s->parent->pending_invite_ssid_id = -1;
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
-					  ssid->ssid, ssid->ssid_len,
-					  go_dev_addr, persistent);
-
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
-	res = wpas_p2p_setup_freqs(wpa_s, 0, &force_freq, &pref_freq);
+	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+				   role == P2P_INVITE_ROLE_ACTIVE_GO);
 	if (res)
 		return res;
 	wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
 
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq,
-			  go_dev_addr, persistent, pref_freq);
+			  go_dev_addr, persistent, pref_freq, -1);
 }
 
 
 void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
-	const char *ssid_txt;
 	u8 go_dev_addr[ETH_ALEN];
 	int network_id = -1;
 	int persistent;
 	int freq;
+	u8 ip[3 * 4];
+	char ip_addr[100];
 
 	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
 		eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
@@ -5256,11 +6039,10 @@
 	}
 
 	if (!wpa_s->show_group_started || !ssid)
-		goto done;
+		return;
 
 	wpa_s->show_group_started = 0;
 
-	ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
 	os_memset(go_dev_addr, 0, ETH_ALEN);
 	if (ssid->bssid_set)
 		os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
@@ -5273,42 +6055,36 @@
 
 	freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
 		(int) wpa_s->assoc_freq;
-	if (ssid->passphrase == NULL && ssid->psk_set) {
-		char psk[65];
-		wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
-		wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-			       "%s client ssid=\"%s\" freq=%d psk=%s "
-			       "go_dev_addr=" MACSTR "%s",
-			       wpa_s->ifname, ssid_txt, freq, psk,
-			       MAC2STR(go_dev_addr),
-			       persistent ? " [PERSISTENT]" : "");
-	} else {
-		wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-			       "%s client ssid=\"%s\" freq=%d "
-			       "passphrase=\"%s\" go_dev_addr=" MACSTR "%s",
-			       wpa_s->ifname, ssid_txt, freq,
-			       ssid->passphrase ? ssid->passphrase : "",
-			       MAC2STR(go_dev_addr),
-			       persistent ? " [PERSISTENT]" : "");
+
+	ip_addr[0] = '\0';
+	if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
+		os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u "
+			    "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
+			    ip[0], ip[1], ip[2], ip[3],
+			    ip[4], ip[5], ip[6], ip[7],
+			    ip[8], ip[9], ip[10], ip[11]);
 	}
 
+	wpas_p2p_group_started(wpa_s, 0, ssid, freq,
+			       ssid->passphrase == NULL && ssid->psk_set ?
+			       ssid->psk : NULL,
+			       ssid->passphrase, go_dev_addr, persistent,
+			       ip_addr);
+
 	if (persistent)
 		network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
 							     ssid, go_dev_addr);
 	if (network_id < 0)
 		network_id = ssid->id;
 	wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
-
-done:
-	wpas_p2p_continue_after_scan(wpa_s);
 }
 
 
 int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
 			  u32 interval1, u32 duration2, u32 interval2)
 {
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return -1;
+	int ret;
+
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
@@ -5317,18 +6093,19 @@
 	    wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
 		return -1;
 
-	return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
-				wpa_s->own_addr, wpa_s->assoc_freq,
-				duration1, interval1, duration2, interval2);
+	ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+			       wpa_s->own_addr, wpa_s->assoc_freq,
+			       duration1, interval1, duration2, interval2);
+	if (ret == 0)
+		wpa_s->waiting_presence_resp = 1;
+
+	return ret;
 }
 
 
 int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
 			unsigned int interval)
 {
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return -1;
-
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
@@ -5430,8 +6207,6 @@
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return 0;
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return 0;
 
 	if (!locally_generated)
 		p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
@@ -5459,8 +6234,6 @@
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return;
 
 	if (!locally_generated)
 		p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
@@ -5537,10 +6310,13 @@
 		u8 reg_class, channel;
 		int ret;
 		unsigned int r;
+		u8 channel_forced;
+
 		if (wpa_s->conf->p2p_listen_reg_class &&
 		    wpa_s->conf->p2p_listen_channel) {
 			reg_class = wpa_s->conf->p2p_listen_reg_class;
 			channel = wpa_s->conf->p2p_listen_channel;
+			channel_forced = 1;
 		} else {
 			reg_class = 81;
 			/*
@@ -5549,8 +6325,10 @@
 			 */
 			os_get_random((u8 *) &r, sizeof(r));
 			channel = 1 + (r % 3) * 5;
+			channel_forced = 0;
 		}
-		ret = p2p_set_listen_channel(p2p, reg_class, channel);
+		ret = p2p_set_listen_channel(p2p, reg_class, channel,
+					     channel_forced);
 		if (ret)
 			wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
 				   "failed: %d", ret);
@@ -5587,7 +6365,15 @@
 			wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
 				   "update failed");
 		}
+
+		if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) {
+			wpa_printf(MSG_ERROR, "P2P: No GO channel list "
+				   "update failed");
+		}
 	}
+
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PASSPHRASE_LEN)
+		p2p_set_passphrase_len(p2p, wpa_s->conf->p2p_passphrase_len);
 }
 
 
@@ -5605,8 +6391,6 @@
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-		return -1;
 
 	wpa_s->global->cross_connection = enabled;
 	p2p_set_cross_connect(wpa_s->global->p2p, enabled);
@@ -5756,21 +6540,52 @@
 }
 
 
+void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpas_p2p_notif_pbc_overlap(wpa_s);
+}
+
+
 void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
 {
-	struct p2p_channels chan;
+	struct p2p_channels chan, cli_chan;
+	struct wpa_supplicant *ifs;
 
 	if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
 		return;
 
 	os_memset(&chan, 0, sizeof(chan));
-	if (wpas_p2p_setup_channels(wpa_s, &chan)) {
+	os_memset(&cli_chan, 0, sizeof(cli_chan));
+	if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
 		wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
 			   "channel list");
 		return;
 	}
 
-	p2p_update_channel_list(wpa_s->global->p2p, &chan);
+	p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
+
+	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+		int freq;
+		if (!ifs->current_ssid ||
+		    !ifs->current_ssid->p2p_group ||
+		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+				continue;
+		freq = ifs->current_ssid->frequency;
+		if (freq_included(&chan, freq)) {
+			wpa_dbg(ifs, MSG_DEBUG,
+				"P2P GO operating frequency %d MHz in valid range",
+				freq);
+			continue;
+		}
+
+		wpa_dbg(ifs, MSG_DEBUG,
+			"P2P GO operating in invalid frequency %d MHz",	freq);
+		/* TODO: Consider using CSA or removing the group within
+		 * wpa_supplicant */
+		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+	}
 }
 
 
@@ -5836,6 +6651,11 @@
 			wpas_p2p_group_delete(wpa_s,
 					      P2P_GROUP_REMOVAL_REQUESTED);
 			break;
+		} else if (wpa_s->p2p_in_invitation) {
+			wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
+				   wpa_s->ifname);
+			found = 1;
+			wpas_p2p_group_formation_failed(wpa_s);
 		}
 	}
 
@@ -5863,7 +6683,7 @@
 				   int freq_24, int freq_5, int freq_overall)
 {
 	struct p2p_data *p2p = wpa_s->global->p2p;
-	if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+	if (p2p == NULL)
 		return;
 	p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
 }
@@ -5874,7 +6694,7 @@
 	u8 peer[ETH_ALEN];
 	struct p2p_data *p2p = wpa_s->global->p2p;
 
-	if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+	if (p2p == NULL)
 		return -1;
 
 	if (hwaddr_aton(addr, peer))
@@ -5931,10 +6751,10 @@
 	}
 
 	if (!ret && wpa_s->global->p2p_go_wait_client.sec) {
-		struct os_time now;
-		os_get_time(&now);
-		if (now.sec > wpa_s->global->p2p_go_wait_client.sec +
-		    P2P_MAX_INITIAL_CONN_WAIT_GO) {
+		struct os_reltime now;
+		os_get_reltime(&now);
+		if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client,
+				       P2P_MAX_INITIAL_CONN_WAIT_GO)) {
 			/* Wait for the first client has expired */
 			wpa_s->global->p2p_go_wait_client.sec = 0;
 		} else {
@@ -6007,6 +6827,26 @@
 void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 				       const u8 *addr)
 {
+	if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+				 wpa_s->parent, NULL) > 0) {
+		/*
+		 * This can happen if WPS provisioning step is not terminated
+		 * cleanly (e.g., P2P Client does not send WSC_Done). Since the
+		 * peer was able to connect, there is no need to time out group
+		 * formation after this, though. In addition, this is used with
+		 * the initial connection wait on the GO as a separate formation
+		 * timeout and as such, expected to be hit after the initial WPS
+		 * provisioning step.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection");
+	}
+	if (!wpa_s->p2p_go_group_formation_completed) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection");
+		wpa_s->p2p_go_group_formation_completed = 1;
+		wpa_s->global->p2p_group_formation = NULL;
+		wpa_s->p2p_in_provisioning = 0;
+		wpa_s->p2p_in_invitation = 0;
+	}
 	wpa_s->global->p2p_go_wait_client.sec = 0;
 	if (addr == NULL)
 		return;
@@ -6030,7 +6870,8 @@
 			 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
 			 wpa_s->p2p_persistent_id,
 			 wpa_s->p2p_pd_before_go_neg,
-			 wpa_s->p2p_go_ht40);
+			 wpa_s->p2p_go_ht40,
+			 wpa_s->p2p_go_vht);
 }
 
 
@@ -6053,35 +6894,24 @@
 
 unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
 {
-	const char *rn, *rn2;
 	struct wpa_supplicant *ifs;
 
 	if (wpa_s->wpa_state > WPA_SCANNING) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to "
 			"concurrent operation",
-			P2P_CONCURRENT_SEARCH_DELAY);
-		return P2P_CONCURRENT_SEARCH_DELAY;
+			wpa_s->conf->p2p_search_delay);
+		return wpa_s->conf->p2p_search_delay;
 	}
 
-	if (!wpa_s->driver->get_radio_name)
-		return 0;
-	rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-	if (rn == NULL || rn[0] == '\0')
-		return 0;
-
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		if (ifs == wpa_s || !ifs->driver->get_radio_name)
-			continue;
-
-		rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-		if (!rn2 || os_strcmp(rn, rn2) != 0)
-			continue;
-		if (ifs->wpa_state > WPA_SCANNING) {
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
 				"delay due to concurrent operation on "
 				"interface %s",
-				P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname);
-			return P2P_CONCURRENT_SEARCH_DELAY;
+				wpa_s->conf->p2p_search_delay,
+				ifs->ifname);
+			return wpa_s->conf->p2p_search_delay;
 		}
 	}
 
@@ -6089,30 +6919,6 @@
 }
 
 
-void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s)
-{
-	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Station mode scan operation not "
-		"pending anymore (sta_scan_pending=%d "
-		"p2p_cb_on_scan_complete=%d)", wpa_s->sta_scan_pending,
-		wpa_s->global->p2p_cb_on_scan_complete);
-	wpa_s->sta_scan_pending = 0;
-
-	if (!wpa_s->global->p2p_cb_on_scan_complete)
-		return;
-	wpa_s->global->p2p_cb_on_scan_complete = 0;
-
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return;
-
-	if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
-			"continued after successful connection");
-		p2p_increase_search_delay(wpa_s->global->p2p,
-					  wpas_p2p_search_delay(wpa_s));
-	}
-}
-
-
 static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s,
 				     struct wpa_ssid *s, const u8 *addr,
 				     int iface_addr)
@@ -6146,7 +6952,7 @@
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	struct wpa_ssid *persistent;
-	struct psk_list_entry *p;
+	struct psk_list_entry *p, *last;
 
 	if (psk_len != sizeof(p->psk))
 		return;
@@ -6206,10 +7012,9 @@
 	}
 	os_memcpy(p->psk, psk, psk_len);
 
-	if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS) {
-		struct psk_list_entry *last;
-		last = dl_list_last(&persistent->psk_list,
-				    struct psk_list_entry, list);
+	if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS &&
+	    (last = dl_list_last(&persistent->psk_list,
+				 struct psk_list_entry, list))) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for "
 			MACSTR " (p2p=%u) to make room for a new one",
 			MAC2STR(last->addr), last->p2p);
@@ -6229,11 +7034,9 @@
 	}
 	dl_list_add(&persistent->psk_list, &p->list);
 
-#ifndef CONFIG_NO_CONFIG_WRITE
 	if (wpa_s->parent->conf->update_config &&
 	    wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
 }
 
 
@@ -6244,14 +7047,10 @@
 	int res;
 
 	res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
-	if (res > 0) {
-#ifndef CONFIG_NO_CONFIG_WRITE
-		if (wpa_s->conf->update_config &&
-		    wpa_config_write(wpa_s->confname, wpa_s->conf))
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
-	}
+	if (res > 0 && wpa_s->conf->update_config &&
+	    wpa_config_write(wpa_s->confname, wpa_s->conf))
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Failed to update configuration");
 }
 
 
@@ -6336,6 +7135,63 @@
 }
 
 
+static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group");
+	wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
+}
+
+
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq,
+					struct wpa_ssid *ssid)
+{
+	struct wpa_supplicant *iface;
+
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		if (!iface->current_ssid ||
+		    iface->current_ssid->frequency == freq ||
+		    (iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
+		     !iface->current_ssid->p2p_group))
+			continue;
+
+		/* Remove the connection with least priority */
+		if (!wpas_is_p2p_prioritized(iface)) {
+			/* STA connection has priority over existing
+			 * P2P connection, so remove the interface. */
+			wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict");
+			eloop_register_timeout(0, 0,
+					       wpas_p2p_group_freq_conflict,
+					       iface, NULL);
+			/* If connection in progress is P2P connection, do not
+			 * proceed for the connection. */
+			if (wpa_s == iface)
+				return -1;
+			else
+				return 0;
+		} else {
+			/* P2P connection has priority, disable the STA network
+			 */
+			wpa_supplicant_disable_network(wpa_s->global->ifaces,
+						       ssid);
+			wpa_msg(wpa_s->global->ifaces, MSG_INFO,
+				WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id);
+			os_memset(wpa_s->global->ifaces->pending_bssid, 0,
+				  ETH_ALEN);
+			/* If P2P connection is in progress, continue
+			 * connecting...*/
+			if (wpa_s == iface)
+				return 0;
+			else
+				return -1;
+		}
+	}
+
+	return 0;
+}
+
+
 int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
@@ -6383,72 +7239,655 @@
 	return 0;
 }
 
-#ifdef ANDROID_P2P
-static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_supplicant *wpa_s = eloop_ctx;
 
-	wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group");
-	wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
+					     struct wpabuf *p2p)
+{
+	struct wpabuf *ret;
+	size_t wsc_len;
+
+	if (p2p == NULL) {
+		wpabuf_free(wsc);
+		wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover");
+		return NULL;
+	}
+
+	wsc_len = wsc ? wpabuf_len(wsc) : 0;
+	ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p));
+	if (ret == NULL) {
+		wpabuf_free(wsc);
+		wpabuf_free(p2p);
+		return NULL;
+	}
+
+	wpabuf_put_be16(ret, wsc_len);
+	if (wsc)
+		wpabuf_put_buf(ret, wsc);
+	wpabuf_put_be16(ret, wpabuf_len(p2p));
+	wpabuf_put_buf(ret, p2p);
+
+	wpabuf_free(wsc);
+	wpabuf_free(p2p);
+	wpa_hexdump_buf(MSG_DEBUG,
+			"P2P: Generated NFC connection handover message", ret);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_p2p(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL) {
+			wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request");
+			return NULL;
+		}
+		ret = tmp;
+	}
+
+	return ret;
 }
 
-int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq,
-	struct wpa_ssid *ssid)
+
+static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid **ssid, u8 *go_dev_addr)
 {
-	struct wpa_supplicant *iface = NULL;
-	struct p2p_data *p2p = wpa_s->global->p2p;
+	struct wpa_supplicant *iface;
 
+	if (go_dev_addr)
+		os_memset(go_dev_addr, 0, ETH_ALEN);
+	if (ssid)
+		*ssid = NULL;
 	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
-		if ((iface->current_ssid) &&
-		    (iface->current_ssid->frequency != freq) &&
-		    ((iface->p2p_group_interface) ||
-		     (iface->current_ssid->p2p_group))) {
-
-			if ((iface->p2p_group_interface == P2P_GROUP_INTERFACE_GO)  ||
-			    (iface->current_ssid->mode == WPAS_MODE_P2P_GO)) {
-				/* Try to see whether we can move the GO. If it
-				 * is not possible, remove the GO interface
-				 */
-				if (wpa_drv_switch_channel(iface, freq) == 0) {
-					wpa_printf(MSG_ERROR, "P2P: GO Moved to freq(%d)", freq);
-					iface->current_ssid->frequency = freq;
-					continue;
-				}
-			}
-
-			/* If GO cannot be moved or if the conflicting interface is a
-			 * P2P Client, remove the interface depending up on the connection
-			 * priority */
-			if(!wpas_is_p2p_prioritized(iface)) {
-				/* STA connection has priority over existing
-				 * P2P connection. So remove the interface */
-				wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to Single channel"
-						"concurrent mode frequency conflict");
-				eloop_register_timeout(0, 0, wpas_p2p_group_freq_conflict,
-						       iface, NULL);
-				/* If connection in progress is p2p connection, do not proceed for the connection */
-				if (wpa_s == iface)
-					return -1;
-				else
-					/* If connection in progress is STA connection, proceed for the connection */
-					return 0;
-			} else {
-				/* P2p connection has priority, disable the STA network*/
-				wpa_supplicant_disable_network(wpa_s->global->ifaces, ssid);
-				wpa_msg(wpa_s->global->ifaces, MSG_INFO, WPA_EVENT_FREQ_CONFLICT
-					" id=%d", ssid->id);
-				os_memset(wpa_s->global->ifaces->pending_bssid, 0, ETH_ALEN);
-				if (wpa_s == iface) {
-					/* p2p connection is in progress, continue connecting...*/
-					return 0;
-				}
-				else {
-					/* STA connection is in progress, do not allow to continue */
-					return -1;
-				}
-			}
-		}
+		if (iface->wpa_state < WPA_ASSOCIATING ||
+		    iface->current_ssid == NULL || iface->assoc_freq == 0 ||
+		    !iface->current_ssid->p2p_group ||
+		    iface->current_ssid->mode != WPAS_MODE_INFRA)
+			continue;
+		if (ssid)
+			*ssid = iface->current_ssid;
+		if (go_dev_addr)
+			os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN);
+		return iface->assoc_freq;
 	}
 	return 0;
 }
-#endif
+
+
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef)
+{
+	struct wpabuf *wsc, *p2p;
+	struct wpa_ssid *ssid;
+	u8 go_dev_addr[ETH_ALEN];
+	int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request");
+		return NULL;
+	}
+
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+	    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+			   &wpa_s->conf->wps_nfc_dh_privkey) < 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request");
+		return NULL;
+	}
+
+	if (cli_freq == 0) {
+		wsc = wps_build_nfc_handover_req_p2p(
+			wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
+	} else
+		wsc = NULL;
+	p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq,
+					 go_dev_addr, ssid ? ssid->ssid : NULL,
+					 ssid ? ssid->ssid_len : 0);
+
+	return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+					  int ndef, int tag)
+{
+	struct wpabuf *wsc, *p2p;
+	struct wpa_ssid *ssid;
+	u8 go_dev_addr[ETH_ALEN];
+	int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return NULL;
+
+	if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+	    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+			   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+		return NULL;
+
+	if (cli_freq == 0) {
+		wsc = wps_build_nfc_handover_sel_p2p(
+			wpa_s->parent->wps,
+			tag ? wpa_s->conf->wps_nfc_dev_pw_id :
+			DEV_PW_NFC_CONNECTION_HANDOVER,
+			wpa_s->conf->wps_nfc_dh_pubkey,
+			tag ? wpa_s->conf->wps_nfc_dev_pw : NULL);
+	} else
+		wsc = NULL;
+	p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq,
+					 go_dev_addr, ssid ? ssid->ssid : NULL,
+					 ssid ? ssid->ssid_len : 0);
+
+	return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
+				   struct p2p_nfc_params *params)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
+		   "connection handover (freq=%d)",
+		   params->go_freq);
+
+	if (params->go_freq && params->go_ssid_len) {
+		wpa_s->p2p_wps_method = WPS_NFC;
+		wpa_s->pending_join_wps_method = WPS_NFC;
+		os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN);
+		os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr,
+			  ETH_ALEN);
+		return wpas_p2p_join_start(wpa_s, params->go_freq,
+					   params->go_ssid,
+					   params->go_ssid_len);
+	}
+
+	return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+				WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
+				params->go_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
+				  struct p2p_nfc_params *params, int tag)
+{
+	int res, persistent;
+	struct wpa_ssid *ssid;
+
+	wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
+		   "connection handover");
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		ssid = wpa_s->current_ssid;
+		if (ssid == NULL)
+			continue;
+		if (ssid->mode != WPAS_MODE_P2P_GO)
+			continue;
+		if (wpa_s->ap_iface == NULL)
+			continue;
+		break;
+	}
+	if (wpa_s == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface");
+		return -1;
+	}
+
+	if (wpa_s->parent->p2p_oob_dev_pw_id !=
+	    DEV_PW_NFC_CONNECTION_HANDOVER &&
+	    !wpa_s->parent->p2p_oob_dev_pw) {
+		wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+		return -1;
+	}
+	res = wpas_ap_wps_add_nfc_pw(
+		wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+		wpa_s->parent->p2p_oob_dev_pw,
+		wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+		wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+	if (res)
+		return res;
+
+	if (!tag) {
+		wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation");
+		return 0;
+	}
+
+	if (!params->peer ||
+	    !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE))
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR
+		   " to join", MAC2STR(params->peer->p2p_device_addr));
+
+	wpa_s->global->p2p_invite_group = wpa_s;
+	persistent = ssid->p2p_persistent_group &&
+		wpas_p2p_get_persistent(wpa_s->parent,
+					params->peer->p2p_device_addr,
+					ssid->ssid, ssid->ssid_len);
+	wpa_s->parent->pending_invite_ssid_id = -1;
+
+	return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
+			  P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
+			  ssid->ssid, ssid->ssid_len, ssid->frequency,
+			  wpa_s->global->p2p_dev_addr, persistent, 0,
+			  wpa_s->parent->p2p_oob_dev_pw_id);
+}
+
+
+static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
+				    struct p2p_nfc_params *params,
+				    int forced_freq)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
+		   "connection handover");
+	return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+				WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
+				forced_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
+				    struct p2p_nfc_params *params,
+				    int forced_freq)
+{
+	int res;
+
+	wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC "
+		   "connection handover");
+	res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+			       WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
+			       forced_freq, -1, 0, 1, 1);
+	if (res)
+		return res;
+
+	res = wpas_p2p_listen(wpa_s, 60);
+	if (res) {
+		p2p_unauthorize(wpa_s->global->p2p,
+				params->peer->p2p_device_addr);
+	}
+
+	return res;
+}
+
+
+static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
+					    const struct wpabuf *data,
+					    int sel, int tag, int forced_freq)
+{
+	const u8 *pos, *end;
+	u16 len, id;
+	struct p2p_nfc_params params;
+	int res;
+
+	os_memset(&params, 0, sizeof(params));
+	params.sel = sel;
+
+	wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data);
+
+	pos = wpabuf_head(data);
+	end = pos + wpabuf_len(data);
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC "
+			   "attributes");
+		return -1;
+	}
+	len = WPA_GET_BE16(pos);
+	pos += 2;
+	if (pos + len > end) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
+			   "attributes");
+		return -1;
+	}
+	params.wsc_attr = pos;
+	params.wsc_len = len;
+	pos += len;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P "
+			   "attributes");
+		return -1;
+	}
+	len = WPA_GET_BE16(pos);
+	pos += 2;
+	if (pos + len > end) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
+			   "attributes");
+		return -1;
+	}
+	params.p2p_attr = pos;
+	params.p2p_len = len;
+	pos += len;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes",
+		    params.wsc_attr, params.wsc_len);
+	wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes",
+		    params.p2p_attr, params.p2p_len);
+	if (pos < end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "P2P: Ignored extra data after P2P attributes",
+			    pos, end - pos);
+	}
+
+	res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, &params);
+	if (res)
+		return res;
+
+	if (params.next_step == NO_ACTION)
+		return 0;
+
+	if (params.next_step == BOTH_GO) {
+		wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR,
+			MAC2STR(params.peer->p2p_device_addr));
+		return 0;
+	}
+
+	if (params.next_step == PEER_CLIENT) {
+		if (!is_zero_ether_addr(params.go_dev_addr)) {
+			wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+				"peer=" MACSTR " freq=%d go_dev_addr=" MACSTR
+				" ssid=\"%s\"",
+				MAC2STR(params.peer->p2p_device_addr),
+				params.go_freq,
+				MAC2STR(params.go_dev_addr),
+				wpa_ssid_txt(params.go_ssid,
+					     params.go_ssid_len));
+		} else {
+			wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+				"peer=" MACSTR " freq=%d",
+				MAC2STR(params.peer->p2p_device_addr),
+				params.go_freq);
+		}
+		return 0;
+	}
+
+	if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) {
+		wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer="
+			MACSTR, MAC2STR(params.peer->p2p_device_addr));
+		return 0;
+	}
+
+	wpabuf_free(wpa_s->p2p_oob_dev_pw);
+	wpa_s->p2p_oob_dev_pw = NULL;
+
+	if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw "
+			   "received");
+		return -1;
+	}
+
+	id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN);
+	wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id);
+	wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash",
+		    params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+	os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash,
+		  params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+	wpa_s->p2p_peer_oob_pk_hash_known = 1;
+
+	if (tag) {
+		if (id < 0x10) {
+			wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
+				   "peer OOB Device Password Id %u", id);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB "
+			   "Device Password Id %u", id);
+		wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password",
+				params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+				params.oob_dev_pw_len -
+				WPS_OOB_PUBKEY_HASH_LEN - 2);
+		wpa_s->p2p_oob_dev_pw_id = id;
+		wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy(
+			params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+			params.oob_dev_pw_len -
+			WPS_OOB_PUBKEY_HASH_LEN - 2);
+		if (wpa_s->p2p_oob_dev_pw == NULL)
+			return -1;
+
+		if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+		    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+				   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+			return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake "
+			   "without Device Password");
+		wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+	}
+
+	switch (params.next_step) {
+	case NO_ACTION:
+	case BOTH_GO:
+	case PEER_CLIENT:
+		/* already covered above */
+		return 0;
+	case JOIN_GROUP:
+		return wpas_p2p_nfc_join_group(wpa_s, &params);
+	case AUTH_JOIN:
+		return wpas_p2p_nfc_auth_join(wpa_s, &params, tag);
+	case INIT_GO_NEG:
+		return wpas_p2p_nfc_init_go_neg(wpa_s, &params, forced_freq);
+	case RESP_GO_NEG:
+		/* TODO: use own OOB Dev Pw */
+		return wpas_p2p_nfc_resp_go_neg(wpa_s, &params, forced_freq);
+	}
+
+	return -1;
+}
+
+
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+			     const struct wpabuf *data, int forced_freq)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq);
+}
+
+
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+				 const struct wpabuf *req,
+				 const struct wpabuf *sel, int forced_freq)
+{
+	struct wpabuf *tmp;
+	int ret;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported");
+
+	wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req",
+			  wpabuf_head(req), wpabuf_len(req));
+	wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
+			  wpabuf_head(sel), wpabuf_len(sel));
+	if (forced_freq)
+		wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq);
+	tmp = ndef_parse_p2p(init ? sel : req);
+	if (tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
+		return -1;
+	}
+
+	ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0,
+					       forced_freq);
+	wpabuf_free(tmp);
+
+	return ret;
+}
+
+
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
+{
+	const u8 *if_addr;
+	int go_intent = wpa_s->conf->p2p_go_intent;
+	struct wpa_supplicant *iface;
+
+	if (wpa_s->global->p2p == NULL)
+		return -1;
+
+	if (!enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag");
+		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+		{
+			if (!iface->ap_iface)
+				continue;
+			hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]);
+		}
+		p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0,
+						 0, NULL);
+		if (wpa_s->p2p_nfc_tag_enabled)
+			wpas_p2p_remove_pending_group_interface(wpa_s);
+		wpa_s->p2p_nfc_tag_enabled = 0;
+		return 0;
+	}
+
+	if (wpa_s->global->p2p_disabled)
+		return -1;
+
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+	    wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+	    wpa_s->conf->wps_nfc_dev_pw == NULL ||
+	    wpa_s->conf->wps_nfc_dev_pw_id < 0x10) {
+		wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured "
+			   "to allow static handover cases");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag");
+
+	wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+	wpabuf_free(wpa_s->p2p_oob_dev_pw);
+	wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+	if (wpa_s->p2p_oob_dev_pw == NULL)
+		return -1;
+	wpa_s->p2p_peer_oob_pk_hash_known = 0;
+
+	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
+	    wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) {
+		/*
+		 * P2P Group Interface present and the command came on group
+		 * interface, so enable the token for the current interface.
+		 */
+		wpa_s->create_p2p_iface = 0;
+	} else {
+		wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+	}
+
+	if (wpa_s->create_p2p_iface) {
+		enum wpa_driver_if_type iftype;
+		/* Prepare to add a new interface for the group */
+		iftype = WPA_IF_P2P_GROUP;
+		if (go_intent == 15)
+			iftype = WPA_IF_P2P_GO;
+		if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
+			wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+				   "interface for the group");
+			return -1;
+		}
+
+		if_addr = wpa_s->pending_interface_addr;
+	} else
+		if_addr = wpa_s->own_addr;
+
+	wpa_s->p2p_nfc_tag_enabled = enabled;
+
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		struct hostapd_data *hapd;
+		if (iface->ap_iface == NULL)
+			continue;
+		hapd = iface->ap_iface->bss[0];
+		wpabuf_free(hapd->conf->wps_nfc_dh_pubkey);
+		hapd->conf->wps_nfc_dh_pubkey =
+			wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+		wpabuf_free(hapd->conf->wps_nfc_dh_privkey);
+		hapd->conf->wps_nfc_dh_privkey =
+			wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+		wpabuf_free(hapd->conf->wps_nfc_dev_pw);
+		hapd->conf->wps_nfc_dev_pw =
+			wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+		hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+
+		if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) {
+			wpa_dbg(iface, MSG_DEBUG,
+				"P2P: Failed to enable NFC Tag for GO");
+		}
+	}
+	p2p_set_authorized_oob_dev_pw_id(
+		wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent,
+		if_addr);
+
+	return 0;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+					     struct wpa_used_freq_data *freqs,
+					     unsigned int num)
+{
+	u8 curr_chan, cand, chan;
+	unsigned int i;
+
+	curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
+	for (i = 0, cand = 0; i < num; i++) {
+		ieee80211_freq_to_chan(freqs[i].freq, &chan);
+		if (curr_chan == chan) {
+			cand = 0;
+			break;
+		}
+
+		if (chan == 1 || chan == 6 || chan == 11)
+			cand = chan;
+	}
+
+	if (cand) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Update Listen channel to %u baased on operating channel",
+			cand);
+		p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
+	}
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_used_freq_data *freqs;
+	unsigned int num = wpa_s->num_multichan_concurrent;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+
+	/*
+	 * If possible, optimize the Listen channel to be a channel that is
+	 * already used by one of the other interfaces.
+	 */
+	if (!wpa_s->conf->p2p_optimize_listen_chan)
+		return;
+
+	if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+		return;
+
+	freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+	if (!freqs)
+		return;
+
+	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
+	wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+	os_free(freqs);
+}
+
+
+void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s == wpa_s->parent)
+		wpas_p2p_group_remove(wpa_s, "*");
+	if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
+			"the management interface is being removed");
+		wpas_p2p_deinit_global(wpa_s->global);
+	}
+}
+
+
+void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->ap_iface->bss)
+		wpa_s->ap_iface->bss[0]->p2p_group = NULL;
+	wpas_p2p_group_deinit(wpa_s);
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 65ccbf9..d79ec03 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -16,34 +16,28 @@
 struct p2p_channels;
 struct wps_event_fail;
 
-int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
-void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
-void wpas_p2p_deinit_global(struct wpa_global *global);
 int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s);
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+					      const u8 *ssid, size_t ssid_len);
+struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
+						  const u8 *peer_dev_addr);
 int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		     const char *pin, enum p2p_wps_method wps_method,
 		     int persistent_group, int auto_join, int join,
 		     int auth, int go_intent, int freq, int persistent_id,
-		     int pd, int ht40);
-void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
-				   unsigned int freq, unsigned int duration);
-void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
-					  unsigned int freq);
-#ifdef ANDROID_P2P
+		     int pd, int ht40, int vht);
 int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
                                           int freq, struct wpa_ssid *ssid);
-#endif
 int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-		       int freq, int ht40);
+		       int freq, int ht40, int vht);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
-				  int freq, int ht40,
-				  const struct p2p_channels *channels);
+				  int force_freq, int neg_freq, int ht40,
+				  int vht, const struct p2p_channels *channels,
+				  int connection_timeout);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 				       struct wpa_ssid *ssid);
-void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-			  int registrar);
 enum wpas_p2p_prov_disc_use {
 	WPAS_P2P_PD_FOR_GO_NEG,
 	WPAS_P2P_PD_FOR_JOIN,
@@ -64,33 +58,11 @@
 		  const u8 *dev_id, unsigned int search_delay);
 void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
 int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout);
 int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			  u8 *buf, size_t len, int p2p_group);
-int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
-			  const u8 *dst, const u8 *bssid,
-			  const u8 *ie, size_t ie_len,
-			  int ssi_signal);
-void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
-			const u8 *sa, const u8 *bssid,
-			u8 category, const u8 *data, size_t len, int freq);
 void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
-void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
-void wpas_dev_found(void *ctx, const u8 *addr,
-		    const struct p2p_peer_info *info,
-		    int new_device);
 void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s);
-void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
-void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id);
-void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
-			const u8 *dev_addr, const u8 *pri_dev_type,
-			const char *dev_name, u16 supp_config_methods,
-			u8 dev_capab, u8 group_capab, const u8 *group_id,
-			size_t group_id_len);
-void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
-void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-		     u16 update_indic, const u8 *tlvs, size_t tlvs_len);
-void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-		      const u8 *tlvs, size_t tlvs_len);
 u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
 			const struct wpabuf *tlvs);
 u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
@@ -114,10 +86,9 @@
 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 ht40, int pref_freq);
+		    int ht40, int vht, int pref_freq);
 int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 			  const u8 *peer_addr, const u8 *go_dev_addr);
-void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
 int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
 			  u32 interval1, u32 duration2, u32 interval2);
 int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
@@ -128,25 +99,12 @@
 void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
 			     u16 reason_code, const u8 *ie, size_t ie_len,
 			     int locally_generated);
-void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
 int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
 		     int duration);
 int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
-void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
-void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
-int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
 int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
-void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
-void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
-				   int freq_24, int freq_5, int freq_overall);
 int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
 int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
-void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
-			 struct wps_event_fail *fail);
-int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
-void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
-			      struct wpa_ssid *ssid);
 struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
 					  const u8 *addr, const u8 *ssid,
 					  size_t ssid_len);
@@ -155,20 +113,143 @@
 int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
 int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
 			   struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
+			      struct hostapd_hw_modes *mode, u8 channel);
 unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
 void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
 			 const u8 *p2p_dev_addr,
 			 const u8 *psk, size_t psk_len);
 void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
 			    int iface_addr);
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef);
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+					  int ndef, int tag);
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+			     const struct wpabuf *data, int forced_freq);
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+				 const struct wpabuf *req,
+				 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);
 
 #ifdef CONFIG_P2P
-void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s);
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  const u8 *dst, const u8 *bssid,
+			  const u8 *ie, size_t ie_len,
+			  int ssi_signal);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+			  int registrar);
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+				   int freq_24, int freq_5, int freq_overall);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+			const u8 *sa, const u8 *bssid,
+			u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq, unsigned int duration);
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq);
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
 int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
 void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s);
+void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid);
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
+int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s);
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+			 struct wps_event_fail *fail);
+
 #else /* CONFIG_P2P */
-static inline void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s)
+
+static inline int
+wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
 {
+	return 0;
+}
+
+static inline void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s,
+					const u8 *addr,
+					const u8 *dst, const u8 *bssid,
+					const u8 *ie, size_t ie_len,
+					int ssi_signal)
+{
+	return 0;
+}
+
+static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s,
+					const u8 *peer_addr, int registrar)
+{
+}
+
+static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+						 int freq_24, int freq_5,
+						 int freq_overall)
+{
+}
+
+static inline void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s,
+				      const u8 *da,
+				      const u8 *sa, const u8 *bssid,
+				      u8 category, const u8 *data, size_t len,
+				      int freq)
+{
+}
+
+static inline void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+						 unsigned int freq,
+						 unsigned int duration)
+{
+}
+
+static inline void
+wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				     unsigned int freq)
+{
+}
+
+static inline void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+	return 0;
 }
 
 static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
@@ -179,6 +260,39 @@
 static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
 {
 }
+
+static inline void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+					    struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
+static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+				       struct wps_event_fail *fail)
+{
+}
+
 #endif /* CONFIG_P2P */
 
 #endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index ff2ae74..ed57085 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -27,9 +27,6 @@
 #include "drivers/driver.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
 struct wpa_driver_ops *wpa_drivers[] = { NULL };
 
 
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index a0f51d0..40eb8d8 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Scanning
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -141,71 +141,43 @@
 }
 
 
-static int int_array_len(const int *a)
+static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
 {
-	int i;
-	for (i = 0; a && a[i]; i++)
-		;
-	return i;
-}
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpa_driver_scan_params *params = work->ctx;
+	int ret;
 
-
-static void int_array_concat(int **res, const int *a)
-{
-	int reslen, alen, i;
-	int *n;
-
-	reslen = int_array_len(*res);
-	alen = int_array_len(a);
-
-	n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
-	if (n == NULL) {
-		os_free(*res);
-		*res = NULL;
-		return;
-	}
-	for (i = 0; i <= alen; i++)
-		n[reslen + i] = a[i];
-	*res = n;
-}
-
-
-static int freq_cmp(const void *a, const void *b)
-{
-	int _a = *(int *) a;
-	int _b = *(int *) b;
-
-	if (_a == 0)
-		return 1;
-	if (_b == 0)
-		return -1;
-	return _a - _b;
-}
-
-
-static void int_array_sort_unique(int *a)
-{
-	int alen;
-	int i, j;
-
-	if (a == NULL)
-		return;
-
-	alen = int_array_len(a);
-	qsort(a, alen, sizeof(int), freq_cmp);
-
-	i = 0;
-	j = 1;
-	while (a[i] && a[j]) {
-		if (a[i] == a[j]) {
-			j++;
-			continue;
+	if (deinit) {
+		if (!work->started) {
+			wpa_scan_free_params(params);
+			return;
 		}
-		a[++i] = a[j++];
+		wpa_supplicant_notify_scanning(wpa_s, 0);
+		wpas_notify_scan_done(wpa_s, 0);
+		wpa_s->scan_work = NULL;
+		return;
 	}
-	if (a[i])
-		i++;
-	a[i] = 0;
+
+	wpa_supplicant_notify_scanning(wpa_s, 1);
+
+	if (wpa_s->clear_driver_scan_cache)
+		params->only_new_results = 1;
+	ret = wpa_drv_scan(wpa_s, params);
+	wpa_scan_free_params(params);
+	work->ctx = NULL;
+	if (ret) {
+		wpa_supplicant_notify_scanning(wpa_s, 0);
+		wpas_notify_scan_done(wpa_s, 0);
+		radio_work_done(work);
+		return;
+	}
+
+	os_get_reltime(&wpa_s->scan_trigger_time);
+	wpa_s->scan_runs++;
+	wpa_s->normal_scans++;
+	wpa_s->own_scan_requested = 1;
+	wpa_s->clear_driver_scan_cache = 0;
+	wpa_s->scan_work = work;
 }
 
 
@@ -218,21 +190,24 @@
 int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
 				struct wpa_driver_scan_params *params)
 {
-	int ret;
+	struct wpa_driver_scan_params *ctx;
 
-	wpa_supplicant_notify_scanning(wpa_s, 1);
-
-	ret = wpa_drv_scan(wpa_s, params);
-	if (ret) {
-		wpa_supplicant_notify_scanning(wpa_s, 0);
-		wpas_notify_scan_done(wpa_s, 0);
-	} else {
-		os_get_time(&wpa_s->scan_trigger_time);
-		wpa_s->scan_runs++;
-		wpa_s->normal_scans++;
+	if (wpa_s->scan_work) {
+		wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
+		return -1;
 	}
 
-	return ret;
+	ctx = wpa_scan_clone_params(params);
+	if (ctx == NULL)
+		return -1;
+
+	if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
+	{
+		wpa_scan_free_params(ctx);
+		return -1;
+	}
+
+	return 0;
 }
 
 
@@ -260,10 +235,9 @@
 }
 
 
-static int
-wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				struct wpa_driver_scan_params *params,
-				int interval)
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_scan_params *params,
+				    int interval)
 {
 	int ret;
 
@@ -271,15 +245,14 @@
 	ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
 	if (ret)
 		wpa_supplicant_notify_scanning(wpa_s, 0);
-	else {
+	else
 		wpa_s->sched_scanning = 1;
-	}
 
 	return ret;
 }
 
 
-static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
 {
 	int ret;
 
@@ -353,6 +326,32 @@
 		}
 		wpa_s->p2p_in_provisioning++;
 	}
+
+	if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
+		/*
+		 * Optimize scan based on GO information during persistent
+		 * group reinvocation
+		 */
+		if (wpa_s->p2p_in_invitation < 5 &&
+		    wpa_s->p2p_invite_go_freq > 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
+				wpa_s->p2p_invite_go_freq);
+			params->freqs = os_zalloc(2 * sizeof(int));
+			if (params->freqs)
+				params->freqs[0] = wpa_s->p2p_invite_go_freq;
+		}
+		wpa_s->p2p_in_invitation++;
+		if (wpa_s->p2p_in_invitation > 20) {
+			/*
+			 * This should not really happen since the variable is
+			 * cleared on group removal, but if it does happen, make
+			 * sure we do not get stuck in special invitation scan
+			 * mode.
+			 */
+			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
+			wpa_s->p2p_in_invitation = 0;
+		}
+	}
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS
@@ -367,7 +366,8 @@
 		if (params->freqs)
 			params->freqs[0] = wpa_s->wps_freq;
 		wpa_s->after_wps--;
-	}
+	} else if (wpa_s->after_wps)
+		wpa_s->after_wps--;
 
 	if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
 	{
@@ -391,11 +391,17 @@
 		return;
 
 	wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
-	wpabuf_put_u8(buf, 4);
+	wpabuf_put_u8(buf, 6);
 	wpabuf_put_u8(buf, 0x00);
 	wpabuf_put_u8(buf, 0x00);
 	wpabuf_put_u8(buf, 0x00);
 	wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */
+	wpabuf_put_u8(buf, 0x00);
+#ifdef CONFIG_HS20
+	wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */
+#else /* CONFIG_HS20 */
+	wpabuf_put_u8(buf, 0x00);
+#endif /* CONFIG_HS20 */
 
 	wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
 	wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
@@ -451,7 +457,7 @@
 
 #ifdef CONFIG_HS20
 	if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0)
-		wpas_hs20_add_indication(extra_ie);
+		wpas_hs20_add_indication(extra_ie, -1);
 #endif /* CONFIG_HS20 */
 
 	return extra_ie;
@@ -542,11 +548,51 @@
 }
 
 
+static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
+			       struct wpa_driver_scan_params *params,
+			       size_t max_ssids)
+{
+	unsigned int i;
+	struct wpa_ssid *ssid;
+
+	for (i = 0; i < wpa_s->scan_id_count; i++) {
+		unsigned int j;
+
+		ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
+		if (!ssid || !ssid->scan_ssid)
+			continue;
+
+		for (j = 0; j < params->num_ssids; j++) {
+			if (params->ssids[j].ssid_len == ssid->ssid_len &&
+			    params->ssids[j].ssid &&
+			    os_memcmp(params->ssids[j].ssid, ssid->ssid,
+				      ssid->ssid_len) == 0)
+				break;
+		}
+		if (j < params->num_ssids)
+			continue; /* already in the list */
+
+		if (params->num_ssids + 1 > max_ssids) {
+			wpa_printf(MSG_DEBUG,
+				   "Over max scan SSIDs for manual request");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
+			   wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+		params->ssids[params->num_ssids].ssid = ssid->ssid;
+		params->ssids[params->num_ssids].ssid_len = ssid->ssid_len;
+		params->num_ssids++;
+	}
+
+	wpa_s->scan_id_count = 0;
+}
+
+
 static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	struct wpa_ssid *ssid;
-	enum scan_req_type scan_req = NORMAL_SCAN_REQ;
 	int ret;
 	struct wpabuf *extra_ie = NULL;
 	struct wpa_driver_scan_params params;
@@ -554,30 +600,36 @@
 	size_t max_ssids;
 	enum wpa_states prev_state;
 
+	if (wpa_s->pno || wpa_s->pno_sched_pending) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
+		return;
+	}
+
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
-		wpas_p2p_continue_after_scan(wpa_s);
 		return;
 	}
 
 	if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
 		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
-		wpas_p2p_continue_after_scan(wpa_s);
 		return;
 	}
-#ifdef ANDROID
+
 	if (wpa_s->scanning) {
-		/* If we are already in scanning state, we shall ignore this new scan request*/
-		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - already scanning");
+		/*
+		 * If we are already in scanning state, we shall reschedule the
+		 * the incoming scan request.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
+		wpa_supplicant_req_scan(wpa_s, 1, 0);
 		return;
 	}
-#endif
+
 	if (!wpa_supplicant_enabled_networks(wpa_s) &&
 	    wpa_s->scan_req == NORMAL_SCAN_REQ) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
 		wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
-		wpas_p2p_continue_after_scan(wpa_s);
 		return;
 	}
 
@@ -594,22 +646,11 @@
 		return;
 	}
 
-#ifdef CONFIG_P2P
-	if (wpas_p2p_in_progress(wpa_s) || wpas_wpa_is_in_progress(wpa_s)) {
-		if (wpa_s->sta_scan_pending &&
-		    wpas_p2p_in_progress(wpa_s) == 2 &&
-		    wpa_s->global->p2p_cb_on_scan_complete) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station "
-				"mode scan during P2P search");
-		} else {
-			wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
-				"while P2P operation is in progress");
-			wpa_s->sta_scan_pending = 1;
-			wpa_supplicant_req_scan(wpa_s, 5, 0);
-			return;
-		}
+	if (wpas_p2p_in_progress(wpa_s)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
+		wpa_supplicant_req_scan(wpa_s, 5, 0);
+		return;
 	}
-#endif /* CONFIG_P2P */
 
 	if (wpa_s->conf->ap_scan == 2)
 		max_ssids = 1;
@@ -619,7 +660,7 @@
 			max_ssids = WPAS_MAX_SCAN_SSIDS;
 	}
 
-	scan_req = wpa_s->scan_req;
+	wpa_s->last_scan_req = wpa_s->scan_req;
 	wpa_s->scan_req = NORMAL_SCAN_REQ;
 
 	os_memset(&params, 0, sizeof(params));
@@ -637,7 +678,8 @@
 		goto scan;
 	}
 
-	if (scan_req != MANUAL_SCAN_REQ && wpa_s->connect_without_scan) {
+	if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+	    wpa_s->connect_without_scan) {
 		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 			if (ssid == wpa_s->connect_without_scan)
 				break;
@@ -654,13 +696,27 @@
 #ifdef CONFIG_P2P
 	if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
 	    wpa_s->go_params) {
-		wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during "
-			   "P2P group formation");
+		wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
+			   wpa_s->p2p_in_provisioning,
+			   wpa_s->show_group_started);
 		params.ssids[0].ssid = wpa_s->go_params->ssid;
 		params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
 		params.num_ssids = 1;
 		goto ssid_list_set;
 	}
+
+	if (wpa_s->p2p_in_invitation) {
+		if (wpa_s->current_ssid) {
+			wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation");
+			params.ssids[0].ssid = wpa_s->current_ssid->ssid;
+			params.ssids[0].ssid_len =
+				wpa_s->current_ssid->ssid_len;
+			params.num_ssids = 1;
+		} else {
+			wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
+		}
+		goto ssid_list_set;
+	}
 #endif /* CONFIG_P2P */
 
 	/* Find the starting point from which to continue scanning */
@@ -675,19 +731,48 @@
 		}
 	}
 
-	if (scan_req != MANUAL_SCAN_REQ && wpa_s->conf->ap_scan == 2) {
+	if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+	    wpa_s->conf->ap_scan == 2) {
 		wpa_s->connect_without_scan = NULL;
 		wpa_s->prev_scan_wildcard = 0;
 		wpa_supplicant_assoc_try(wpa_s, ssid);
 		return;
-#ifndef ANDROID
 	} else if (wpa_s->conf->ap_scan == 2) {
 		/*
 		 * User-initiated scan request in ap_scan == 2; scan with
 		 * wildcard SSID.
 		 */
 		ssid = NULL;
-#endif
+	} else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
+		/*
+		 * Perform single-channel single-SSID scan for
+		 * reassociate-to-same-BSS operation.
+		 */
+		/* Setup SSID */
+		ssid = wpa_s->current_ssid;
+		wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+				  ssid->ssid, ssid->ssid_len);
+		params.ssids[0].ssid = ssid->ssid;
+		params.ssids[0].ssid_len = ssid->ssid_len;
+		params.num_ssids = 1;
+
+		/*
+		 * Allocate memory for frequency array, allocate one extra
+		 * slot for the zero-terminator.
+		 */
+		params.freqs = os_malloc(sizeof(int) * 2);
+		if (params.freqs == NULL) {
+			wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed");
+			return;
+		}
+		params.freqs[0] = wpa_s->assoc_freq;
+		params.freqs[1] = 0;
+
+		/*
+		 * Reset the reattach flag so that we fall back to full scan if
+		 * this scan fails.
+		 */
+		wpa_s->reattach = 0;
 	} else {
 		struct wpa_ssid *start = ssid, *tssid;
 		int freqs_set = 0;
@@ -714,6 +799,10 @@
 				ssid = wpa_s->conf->ssid;
 		}
 
+		if (wpa_s->scan_id_count &&
+		    wpa_s->last_scan_req == MANUAL_SCAN_REQ)
+			wpa_set_scan_ssids(wpa_s, &params, max_ssids);
+
 		for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
 			if (wpas_network_disabled(wpa_s, tssid))
 				continue;
@@ -755,6 +844,9 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
 			"the scan request");
 		params.num_ssids++;
+	} else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+		   wpa_s->manual_scan_passive && params.num_ssids == 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
 	} else {
 		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
 		params.num_ssids++;
@@ -768,6 +860,17 @@
 	wpa_supplicant_optimize_freqs(wpa_s, &params);
 	extra_ie = wpa_supplicant_extra_ies(wpa_s);
 
+	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+	    wpa_s->manual_scan_only_new)
+		params.only_new_results = 1;
+
+	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
+	    wpa_s->manual_scan_freqs) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
+		params.freqs = wpa_s->manual_scan_freqs;
+		wpa_s->manual_scan_freqs = NULL;
+	}
+
 	if (params.freqs == NULL && wpa_s->next_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
 			"generated frequency list");
@@ -810,7 +913,7 @@
 	}
 
 #ifdef CONFIG_P2P
-	if (wpa_s->p2p_in_provisioning ||
+	if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
 	    (wpa_s->show_group_started && wpa_s->go_params)) {
 		/*
 		 * The interface may not yet be in P2P mode, so we have to
@@ -833,7 +936,8 @@
 	 * station interface when we are not configured to prefer station
 	 * connection and a concurrent operation is already in process.
 	 */
-	if (wpa_s->scan_for_connection && scan_req == NORMAL_SCAN_REQ &&
+	if (wpa_s->scan_for_connection &&
+	    wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
 	    !scan_params->freqs && !params.freqs &&
 	    wpas_is_p2p_prioritized(wpa_s) &&
 	    wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
@@ -855,6 +959,13 @@
 
 	ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
 
+	if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
+	    !wpa_s->manual_scan_freqs) {
+		/* Restore manual_scan_freqs for the next attempt */
+		wpa_s->manual_scan_freqs = params.freqs;
+		params.freqs = NULL;
+	}
+
 	wpabuf_free(extra_ie);
 	os_free(params.freqs);
 	os_free(params.filter_ssids);
@@ -864,7 +975,7 @@
 		if (prev_state != wpa_s->wpa_state)
 			wpa_supplicant_set_state(wpa_s, prev_state);
 		/* Restore scan_req since we will try to scan again */
-		wpa_s->scan_req = scan_req;
+		wpa_s->scan_req = wpa_s->last_scan_req;
 		wpa_supplicant_req_scan(wpa_s, 1, 0);
 	} else {
 		wpa_s->scan_for_connection = 0;
@@ -874,7 +985,7 @@
 
 void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
 {
-	struct os_time remaining, new_int;
+	struct os_reltime remaining, new_int;
 	int cancelled;
 
 	cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
@@ -882,13 +993,15 @@
 
 	new_int.sec = sec;
 	new_int.usec = 0;
-	if (cancelled && os_time_before(&remaining, &new_int)) {
+	if (cancelled && os_reltime_before(&remaining, &new_int)) {
 		new_int.sec = remaining.sec;
 		new_int.usec = remaining.usec;
 	}
 
-	eloop_register_timeout(new_int.sec, new_int.usec, wpa_supplicant_scan,
-			       wpa_s, NULL);
+	if (cancelled) {
+		eloop_register_timeout(new_int.sec, new_int.usec,
+				       wpa_supplicant_scan, wpa_s, NULL);
+	}
 	wpa_s->scan_interval = sec;
 }
 
@@ -904,34 +1017,19 @@
  */
 void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
 {
-#ifndef ANDROID
-	/* If there's at least one network that should be specifically scanned
-	 * then don't cancel the scan and reschedule.  Some drivers do
-	 * background scanning which generates frequent scan results, and that
-	 * causes the specific SSID scan to get continually pushed back and
-	 * never happen, which causes hidden APs to never get probe-scanned.
-	 */
-	if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) &&
-	    wpa_s->conf->ap_scan == 1) {
-		struct wpa_ssid *ssid = wpa_s->conf->ssid;
-
-		while (ssid) {
-			if (!wpas_network_disabled(wpa_s, ssid) &&
-			    ssid->scan_ssid)
-				break;
-			ssid = ssid->next;
-		}
-		if (ssid) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
-			        "ensure that specific SSID scans occur");
-			return;
-		}
+	int res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
+					NULL);
+	if (res == 1) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
+			sec, usec);
+	} else if (res == 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
+			sec, usec);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
+			sec, usec);
+		eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
 	}
-#endif
-	wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
-		sec, usec);
-	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
-	eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
 }
 
 
@@ -1143,6 +1241,9 @@
 		params.extra_ies_len = wpabuf_len(extra_ie);
 	}
 
+	if (wpa_s->conf->filter_rssi)
+		params.filter_rssi = wpa_s->conf->filter_rssi;
+
 	scan_params = &params;
 
 scan:
@@ -1203,10 +1304,6 @@
 {
 	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
 	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
-	wpas_p2p_continue_after_scan(wpa_s);
-#ifdef ANDROID
-	wpa_supplicant_notify_scanning(wpa_s, 0);
-#endif
 }
 
 
@@ -1345,6 +1442,43 @@
 
 
 /**
+ * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ *
+ * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+					 u32 vendor_type)
+{
+	const u8 *end, *pos;
+
+	if (res->beacon_ie_len == 0)
+		return NULL;
+
+	pos = (const u8 *) (res + 1);
+	pos += res->ie_len;
+	end = pos + res->beacon_ie_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+		    vendor_type == WPA_GET_BE32(&pos[2]))
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+
+/**
  * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
  * @res: Scan result entry
  * @vendor_type: Vendor type (four octets starting the IE payload)
@@ -1632,12 +1766,12 @@
 		 * Make sure we have a valid timestamp if the driver wrapper
 		 * does not set this.
 		 */
-		os_get_time(&scan_res->fetch_time);
+		os_get_reltime(&scan_res->fetch_time);
 	}
 	filter_scan_res(wpa_s, scan_res);
 
 #ifdef CONFIG_WPS
-	if (wpas_wps_in_progress(wpa_s)) {
+	if (wpas_wps_searching(wpa_s)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
 			"provisioning rules");
 		compar = wpa_scan_result_wps_compar;
@@ -1689,9 +1823,21 @@
 		       struct wpa_scan_results *scan_res)
 {
 	wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
-	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+			     wpa_s->manual_scan_id);
+		wpa_s->manual_scan_use_id = 0;
+	} else {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+	}
 	wpas_notify_scan_results(wpa_s);
 	wpas_notify_scan_done(wpa_s, 1);
+	if (wpa_s->scan_work) {
+		struct wpa_radio_work *work = wpa_s->scan_work;
+		wpa_s->scan_work = NULL;
+		radio_work_done(work);
+	}
 }
 
 
@@ -1699,3 +1845,213 @@
 {
 	return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL);
 }
+
+
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
+{
+	struct wpa_driver_scan_params *params;
+	size_t i;
+	u8 *n;
+
+	params = os_zalloc(sizeof(*params));
+	if (params == NULL)
+		return NULL;
+
+	for (i = 0; i < src->num_ssids; i++) {
+		if (src->ssids[i].ssid) {
+			n = os_malloc(src->ssids[i].ssid_len);
+			if (n == NULL)
+				goto failed;
+			os_memcpy(n, src->ssids[i].ssid,
+				  src->ssids[i].ssid_len);
+			params->ssids[i].ssid = n;
+			params->ssids[i].ssid_len = src->ssids[i].ssid_len;
+		}
+	}
+	params->num_ssids = src->num_ssids;
+
+	if (src->extra_ies) {
+		n = os_malloc(src->extra_ies_len);
+		if (n == NULL)
+			goto failed;
+		os_memcpy(n, src->extra_ies, src->extra_ies_len);
+		params->extra_ies = n;
+		params->extra_ies_len = src->extra_ies_len;
+	}
+
+	if (src->freqs) {
+		int len = int_array_len(src->freqs);
+		params->freqs = os_malloc((len + 1) * sizeof(int));
+		if (params->freqs == NULL)
+			goto failed;
+		os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int));
+	}
+
+	if (src->filter_ssids) {
+		params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) *
+						 src->num_filter_ssids);
+		if (params->filter_ssids == NULL)
+			goto failed;
+		os_memcpy(params->filter_ssids, src->filter_ssids,
+			  sizeof(*params->filter_ssids) *
+			  src->num_filter_ssids);
+		params->num_filter_ssids = src->num_filter_ssids;
+	}
+
+	params->filter_rssi = src->filter_rssi;
+	params->p2p_probe = src->p2p_probe;
+	params->only_new_results = src->only_new_results;
+	params->low_priority = src->low_priority;
+
+	return params;
+
+failed:
+	wpa_scan_free_params(params);
+	return NULL;
+}
+
+
+void wpa_scan_free_params(struct wpa_driver_scan_params *params)
+{
+	size_t i;
+
+	if (params == NULL)
+		return;
+
+	for (i = 0; i < params->num_ssids; i++)
+		os_free((u8 *) params->ssids[i].ssid);
+	os_free((u8 *) params->extra_ies);
+	os_free(params->freqs);
+	os_free(params->filter_ssids);
+	os_free(params);
+}
+
+
+int wpas_start_pno(struct wpa_supplicant *wpa_s)
+{
+	int ret, interval;
+	size_t i, num_ssid, num_match_ssid;
+	struct wpa_ssid *ssid;
+	struct wpa_driver_scan_params params;
+
+	if (!wpa_s->sched_scan_supported)
+		return -1;
+
+	if (wpa_s->pno || wpa_s->pno_sched_pending)
+		return 0;
+
+	if ((wpa_s->wpa_state > WPA_SCANNING) &&
+	    (wpa_s->wpa_state <= WPA_COMPLETED)) {
+		wpa_printf(MSG_ERROR, "PNO: In assoc process");
+		return -EAGAIN;
+	}
+
+	if (wpa_s->wpa_state == WPA_SCANNING) {
+		wpa_supplicant_cancel_scan(wpa_s);
+		if (wpa_s->sched_scanning) {
+			wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
+				   "ongoing sched scan");
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+			wpa_s->pno_sched_pending = 1;
+			return 0;
+		}
+	}
+
+	os_memset(&params, 0, sizeof(params));
+
+	num_ssid = num_match_ssid = 0;
+	ssid = wpa_s->conf->ssid;
+	while (ssid) {
+		if (!wpas_network_disabled(wpa_s, ssid)) {
+			num_match_ssid++;
+			if (ssid->scan_ssid)
+				num_ssid++;
+		}
+		ssid = ssid->next;
+	}
+
+	if (num_match_ssid == 0) {
+		wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
+		return -1;
+	}
+
+	if (num_match_ssid > num_ssid) {
+		params.num_ssids++; /* wildcard */
+		num_ssid++;
+	}
+
+	if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+		wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
+			   "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
+		num_ssid = WPAS_MAX_SCAN_SSIDS;
+	}
+
+	if (num_match_ssid > wpa_s->max_match_sets) {
+		num_match_ssid = wpa_s->max_match_sets;
+		wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match");
+	}
+	params.filter_ssids = os_calloc(num_match_ssid,
+					sizeof(struct wpa_driver_scan_filter));
+	if (params.filter_ssids == NULL)
+		return -1;
+	i = 0;
+	ssid = wpa_s->conf->ssid;
+	while (ssid) {
+		if (!wpas_network_disabled(wpa_s, ssid)) {
+			if (ssid->scan_ssid && params.num_ssids < num_ssid) {
+				params.ssids[params.num_ssids].ssid =
+					ssid->ssid;
+				params.ssids[params.num_ssids].ssid_len =
+					 ssid->ssid_len;
+				params.num_ssids++;
+			}
+			os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
+				  ssid->ssid_len);
+			params.filter_ssids[i].ssid_len = ssid->ssid_len;
+			params.num_filter_ssids++;
+			i++;
+			if (i == num_match_ssid)
+				break;
+		}
+		ssid = ssid->next;
+	}
+
+	if (wpa_s->conf->filter_rssi)
+		params.filter_rssi = wpa_s->conf->filter_rssi;
+
+	interval = wpa_s->conf->sched_scan_interval ?
+		wpa_s->conf->sched_scan_interval : 10;
+
+	if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
+		params.freqs = wpa_s->manual_sched_scan_freqs;
+	}
+
+	ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+	os_free(params.filter_ssids);
+	if (ret == 0)
+		wpa_s->pno = 1;
+	else
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO");
+	return ret;
+}
+
+
+int wpas_stop_pno(struct wpa_supplicant *wpa_s)
+{
+	int ret = 0;
+
+	if (!wpa_s->pno)
+		return 0;
+
+	ret = wpa_supplicant_stop_sched_scan(wpa_s);
+
+	wpa_s->pno = 0;
+	wpa_s->pno_sched_pending = 0;
+
+	if (wpa_s->wpa_state == WPA_SCANNING)
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+	return ret;
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 2144787..946d2b3 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Scanning
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -29,6 +29,8 @@
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
 				  u32 vendor_type);
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+					 u32 vendor_type);
 struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
 					     u32 vendor_type);
 int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
@@ -37,5 +39,14 @@
 void scan_only_handler(struct wpa_supplicant *wpa_s,
 		       struct wpa_scan_results *scan_res);
 int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_scan_params *params,
+				    int interval);
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
+void wpa_scan_free_params(struct wpa_driver_scan_params *params);
+int wpas_start_pno(struct wpa_supplicant *wpa_s);
+int wpas_stop_pno(struct wpa_supplicant *wpa_s);
 
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 925d132..5188b9f 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - SME
- * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -46,7 +46,7 @@
 {
 	int i;
 	for (i = 0; i < idx; i++) {
-		if (array[i] == -1)
+		if (array[i] <= 0)
 			return 0;
 	}
 	return 1;
@@ -56,9 +56,9 @@
 static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
 {
 	int *groups = wpa_s->conf->sae_groups;
-	int default_groups[] = { 19, 20, 21, 25, 26 };
+	int default_groups[] = { 19, 20, 21, 25, 26, 0 };
 
-	if (!groups)
+	if (!groups || groups[0] <= 0)
 		groups = default_groups;
 
 	/* Configuration may have changed, so validate current index */
@@ -151,12 +151,13 @@
 #endif /* CONFIG_IEEE80211R */
 	int i, bssid_changed;
 	struct wpabuf *resp = NULL;
-	u8 ext_capab[10];
+	u8 ext_capab[18];
 	int ext_capab_len;
 
 	if (bss == NULL) {
 		wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
 			"the network");
+		wpas_connect_work_done(wpa_s);
 		return;
 	}
 
@@ -244,6 +245,7 @@
 					      &wpa_s->sme.assoc_req_ie_len)) {
 			wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
 				"key management and encryption suites");
+			wpas_connect_work_done(wpa_s);
 			return;
 		}
 	} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
@@ -263,6 +265,7 @@
 			wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
 				"key management and encryption suites (no "
 				"scan results)");
+			wpas_connect_work_done(wpa_s);
 			return;
 		}
 #ifdef CONFIG_WPS
@@ -357,17 +360,25 @@
 		struct wpabuf *hs20;
 		hs20 = wpabuf_alloc(20);
 		if (hs20) {
-			wpas_hs20_add_indication(hs20);
-			os_memcpy(wpa_s->sme.assoc_req_ie +
-				  wpa_s->sme.assoc_req_ie_len,
-				  wpabuf_head(hs20), wpabuf_len(hs20));
-			wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+			size_t len;
+
+			wpas_hs20_add_indication(hs20, pps_mo_id);
+			len = sizeof(wpa_s->sme.assoc_req_ie) -
+				wpa_s->sme.assoc_req_ie_len;
+			if (wpabuf_len(hs20) <= len) {
+				os_memcpy(wpa_s->sme.assoc_req_ie +
+					  wpa_s->sme.assoc_req_ie_len,
+					  wpabuf_head(hs20), wpabuf_len(hs20));
+				wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+			}
 			wpabuf_free(hs20);
 		}
 	}
 #endif /* CONFIG_HS20 */
 
-	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+					     sizeof(ext_capab));
 	if (ext_capab_len > 0) {
 		u8 *pos = wpa_s->sme.assoc_req_ie;
 		if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
@@ -386,8 +397,10 @@
 							 bss->bssid);
 		else
 			resp = sme_auth_build_sae_confirm(wpa_s);
-		if (resp == NULL)
+		if (resp == NULL) {
+			wpas_connect_work_done(wpa_s);
 			return;
+		}
 		params.sae_data = wpabuf_head(resp);
 		params.sae_data_len = wpabuf_len(resp);
 		wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
@@ -410,6 +423,32 @@
 	if (old_ssid != wpa_s->current_ssid)
 		wpas_notify_network_changed(wpa_s);
 
+#ifdef CONFIG_P2P
+	/*
+	 * If multi-channel concurrency is not supported, check for any
+	 * frequency conflict. In case of any frequency conflict, remove the
+	 * least prioritized connection.
+	 */
+	if (wpa_s->num_multichan_concurrent < 2) {
+		int freq, num;
+		num = get_shared_radio_freqs(wpa_s, &freq, 1);
+		if (num > 0 && freq > 0 && freq != params.freq) {
+			wpa_printf(MSG_DEBUG,
+				   "Conflicting frequency found (%d != %d)",
+				   freq, params.freq);
+			if (wpas_p2p_handle_frequency_conflicts(wpa_s,
+								params.freq,
+								ssid) < 0) {
+				wpas_connection_failed(wpa_s, bss->bssid);
+				wpa_supplicant_mark_disassoc(wpa_s);
+				wpabuf_free(resp);
+				wpas_connect_work_done(wpa_s);
+				return;
+			}
+		}
+	}
+#endif /* CONFIG_P2P */
+
 	wpa_s->sme.auth_alg = params.auth_alg;
 	if (wpa_drv_authenticate(wpa_s, &params) < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
@@ -417,6 +456,7 @@
 		wpas_connection_failed(wpa_s, bss->bssid);
 		wpa_supplicant_mark_disassoc(wpa_s);
 		wpabuf_free(resp);
+		wpas_connect_work_done(wpa_s);
 		return;
 	}
 
@@ -432,14 +472,70 @@
 }
 
 
+static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_connect_work *cwork = work->ctx;
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+
+	if (deinit) {
+		if (work->started)
+			wpa_s->connect_work = NULL;
+
+		wpas_connect_work_free(cwork);
+		return;
+	}
+
+	wpa_s->connect_work = work;
+
+	if (!wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
+		wpas_connect_work_done(wpa_s);
+		return;
+	}
+
+	sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+}
+
+
 void sme_authenticate(struct wpa_supplicant *wpa_s,
 		      struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
+	struct wpa_connect_work *cwork;
+
+	if (bss == NULL || ssid == NULL)
+		return;
+	if (wpa_s->connect_work) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist");
+		return;
+	}
+
+	if (radio_work_pending(wpa_s, "sme-connect")) {
+		/*
+		 * The previous sme-connect work might no longer be valid due to
+		 * the fact that the BSS list was updated. In addition, it makes
+		 * sense to adhere to the 'newer' decision.
+		 */
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"SME: Remove previous pending sme-connect");
+		radio_remove_works(wpa_s, "sme-connect", 0);
+	}
+
+	cwork = os_zalloc(sizeof(*cwork));
+	if (cwork == NULL)
+		return;
+	cwork->bss = bss;
+	cwork->ssid = ssid;
+	cwork->sme = 1;
+
 #ifdef CONFIG_SAE
 	wpa_s->sme.sae.state = SAE_NOTHING;
 	wpa_s->sme.sae.send_confirm = 0;
+	wpa_s->sme.sae_group_index = 0;
 #endif /* CONFIG_SAE */
-	sme_send_authentication(wpa_s, bss, ssid, 1);
+
+	if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
+			   sme_auth_start_cb, cwork) < 0)
+		wpas_connect_work_free(cwork);
 }
 
 
@@ -482,15 +578,18 @@
 		return -1;
 
 	if (auth_transaction == 1) {
+		int *groups = wpa_s->conf->sae_groups;
+
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
 		if (wpa_s->current_bss == NULL ||
 		    wpa_s->current_ssid == NULL)
 			return -1;
 		if (wpa_s->sme.sae.state != SAE_COMMITTED)
 			return -1;
+		if (groups && groups[0] <= 0)
+			groups = NULL;
 		if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
-				     wpa_s->conf->sae_groups) !=
-		    WLAN_STATUS_SUCCESS)
+				     groups) != WLAN_STATUS_SUCCESS)
 			return -1;
 
 		if (sae_process_commit(&wpa_s->sme.sae) < 0) {
@@ -585,6 +684,8 @@
 			return;
 		}
 
+		wpas_connect_work_done(wpa_s);
+
 		switch (data->auth.auth_type) {
 		case WLAN_AUTH_OPEN:
 			wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
@@ -647,9 +748,10 @@
 	params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
 		wpa_s->sme.assoc_req_ie : NULL;
 	params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
-	params.pairwise_suite =
-		wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
-	params.group_suite = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
+	params.pairwise_suite = wpa_s->pairwise_cipher;
+	params.group_suite = wpa_s->group_cipher;
+	params.key_mgmt_suite = wpa_s->key_mgmt;
+	params.wpa_proto = wpa_s->wpa_proto;
 #ifdef CONFIG_HT_OVERRIDES
 	os_memset(&htcaps, 0, sizeof(htcaps));
 	os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
@@ -696,6 +798,10 @@
 		params.wpa_proto = WPA_PROTO_WPA;
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
 					elems.wpa_ie_len + 2);
+	} else if (elems.osen) {
+		params.wpa_proto = WPA_PROTO_OSEN;
+		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2,
+					elems.osen_len + 2);
 	} else
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 	if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
@@ -903,8 +1009,11 @@
 	struct ieee80211_2040_intol_chan_report *ic_report;
 	struct wpabuf *buf;
 
-	wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR,
-		   MAC2STR(wpa_s->bssid));
+	wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR
+		   " (num_channels=%u num_intol=%u)",
+		   MAC2STR(wpa_s->bssid), num_channels, num_intol);
+	wpa_hexdump(MSG_DEBUG, "SME: 20/40 BSS Intolerant Channels",
+		    chan_list, num_channels);
 
 	buf = wpabuf_alloc(2 + /* action.category + action_code */
 			   sizeof(struct ieee80211_2040_bss_coex_ie) +
@@ -986,8 +1095,14 @@
 
 		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)
+				num_intol++;
+
 			/* Check whether the channel is already considered */
 			for (i = 0; i < num_channels; i++) {
 				if (channel == chan_list[i])
@@ -996,9 +1111,6 @@
 			if (i != num_channels)
 				continue;
 
-			if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
-				num_intol++;
-
 			chan_list[num_channels++] = channel;
 		}
 	}
@@ -1061,6 +1173,7 @@
 
 	os_memset(&params, 0, sizeof(params));
 	wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params);
+	params.low_priority = 1;
 	wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
 
 	if (wpa_supplicant_trigger_scan(wpa_s, &params))
@@ -1147,9 +1260,9 @@
 static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
 {
 	u32 tu;
-	struct os_time now, passed;
-	os_get_time(&now);
-	os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed);
+	struct os_reltime now, passed;
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &wpa_s->sme.sa_query_start, &passed);
 	tu = (passed.sec * 1000000 + passed.usec) / 1024;
 	if (sa_query_max_timeout < tu) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out");
@@ -1199,7 +1312,7 @@
 		return;
 	if (wpa_s->sme.sa_query_count == 0) {
 		/* Starting a new SA Query procedure */
-		os_get_time(&wpa_s->sme.sa_query_start);
+		os_get_reltime(&wpa_s->sme.sa_query_start);
 	}
 	trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
 	wpa_s->sme.sa_query_trans_id = nbuf;
@@ -1238,6 +1351,7 @@
 				 const u8 *da, u16 reason_code)
 {
 	struct wpa_ssid *ssid;
+	struct os_reltime now;
 
 	if (wpa_s->wpa_state != WPA_COMPLETED)
 		return;
@@ -1254,6 +1368,12 @@
 	if (wpa_s->sme.sa_query_count > 0)
 		return;
 
+	os_get_reltime(&now);
+	if (wpa_s->sme.last_unprot_disconnect.sec &&
+	    !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10))
+		return; /* limit SA Query procedure frequency */
+	wpa_s->sme.last_unprot_disconnect = now;
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
 		"possible AP/STA state mismatch - trigger SA Query");
 	sme_start_sa_query(wpa_s);
diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
index 484a406..39971f2 100644
--- a/wpa_supplicant/tests/test_wpa.c
+++ b/wpa_supplicant/tests/test_wpa.c
@@ -17,10 +17,6 @@
 #include "ap/wpa_auth.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
-
 struct wpa {
 	u8 auth_addr[ETH_ALEN];
 	u8 supp_addr[ETH_ALEN];
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
index 92ca536..b6f9236 100644
--- a/wpa_supplicant/wifi_display.c
+++ b/wpa_supplicant/wifi_display.c
@@ -16,6 +16,9 @@
 #include "wifi_display.h"
 
 
+#define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
+
+
 int wifi_display_init(struct wpa_global *global)
 {
 	global->wifi_display = 1;
@@ -38,6 +41,9 @@
 	struct wpabuf *ie, *buf;
 	size_t len, plen;
 
+	if (global->p2p == NULL)
+		return 0;
+
 	wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
 
 	if (!global->wifi_display) {
@@ -249,3 +255,53 @@
 				1,
 				wpabuf_len(global->wfd_subelem[subelem]) - 1);
 }
+
+
+char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
+{
+	char *subelem = NULL;
+	const u8 *buf;
+	size_t buflen;
+	size_t i = 0;
+	u16 elen;
+
+	if (!wfd_subelems)
+		return NULL;
+
+	buf = wpabuf_head_u8(wfd_subelems);
+	if (!buf)
+		return NULL;
+
+	buflen = wpabuf_len(wfd_subelems);
+
+	while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
+		elen = WPA_GET_BE16(buf + i + 1);
+		if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
+			break; /* truncated subelement */
+
+		if (buf[i] == id) {
+			/*
+			 * Limit explicitly to an arbitrary length to avoid
+			 * unnecessarily large allocations. In practice, this
+			 * is limited to maximum frame length anyway, so the
+			 * maximum memory allocation here is not really that
+			 * large. Anyway, the Wi-Fi Display subelements that
+			 * are fetched with this function are even shorter.
+			 */
+			if (elen > 1000)
+				break;
+			subelem = os_zalloc(2 * elen + 1);
+			if (!subelem)
+				return NULL;
+			wpa_snprintf_hex(subelem, 2 * elen + 1,
+					 buf + i +
+					 WIFI_DISPLAY_SUBELEM_HEADER_LEN,
+					 elen);
+			break;
+		}
+
+		i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
+	}
+
+	return subelem;
+}
diff --git a/wpa_supplicant/wifi_display.h b/wpa_supplicant/wifi_display.h
index b75d4f2..7554817 100644
--- a/wpa_supplicant/wifi_display.h
+++ b/wpa_supplicant/wifi_display.h
@@ -16,5 +16,6 @@
 int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
 int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
 			     char *buf, size_t buflen);
+char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id);
 
 #endif /* WIFI_DISPLAY_H */
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 4f8d895..4a792c4 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -18,6 +18,7 @@
 #include "ctrl_iface.h"
 #include "bss.h"
 #include "wnm_sta.h"
+#include "hs20_supplicant.h"
 
 #define MAX_TFS_IE_LEN  1024
 #define WNM_MAX_NEIGHBOR_REPORT 10
@@ -181,7 +182,7 @@
 	/* Install GTK/IGTK */
 
 	/* point to key data field */
-	ptr = (u8 *) frm + 1 + 1 + 2;
+	ptr = (u8 *) frm + 1 + 2;
 	end = ptr + key_len_total;
 	wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
 
@@ -234,19 +235,23 @@
 					const u8 *frm, int len)
 {
 	/*
-	 * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
+	 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
 	 * WNM-Sleep Mode IE | TFS Response IE
 	 */
-	u8 *pos = (u8 *) frm; /* point to action field */
-	u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
+	u8 *pos = (u8 *) frm; /* point to payload after the action field */
+	u16 key_len_total;
 	struct wnm_sleep_element *wnmsleep_ie = NULL;
 	/* multiple TFS Resp IE (assuming consecutive) */
 	u8 *tfsresp_ie_start = NULL;
 	u8 *tfsresp_ie_end = NULL;
 
-	wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
-		   frm[0], frm[1], key_len_total);
-	pos += 4 + key_len_total;
+	if (len < 3)
+		return;
+	key_len_total = WPA_GET_LE16(frm + 1);
+
+	wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
+		   frm[0], key_len_total);
+	pos += 3 + key_len_total;
 	if (pos > frm + len) {
 		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
 		return;
@@ -314,6 +319,7 @@
 		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
 	}
 
+	wpa_s->wnm_num_neighbor_report = 0;
 	os_free(wpa_s->wnm_neighbor_report_elements);
 	wpa_s->wnm_neighbor_report_elements = NULL;
 }
@@ -328,10 +334,10 @@
 			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
 			break;
 		}
+		os_free(rep->tsf_info);
 		rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
 		if (rep->tsf_info == NULL)
 			break;
-		rep->tsf_info->present = 1;
 		os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
 		os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
 		break;
@@ -341,11 +347,11 @@
 				   "country string");
 			break;
 		}
+		os_free(rep->con_coun_str);
 		rep->con_coun_str =
 			os_zalloc(sizeof(struct condensed_country_string));
 		if (rep->con_coun_str == NULL)
 			break;
-		rep->con_coun_str->present = 1;
 		os_memcpy(rep->con_coun_str->country_string, pos, 2);
 		break;
 	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
@@ -354,25 +360,25 @@
 				   "candidate");
 			break;
 		}
+		os_free(rep->bss_tran_can);
 		rep->bss_tran_can =
 			os_zalloc(sizeof(struct bss_transition_candidate));
 		if (rep->bss_tran_can == NULL)
 			break;
-		rep->bss_tran_can->present = 1;
 		rep->bss_tran_can->preference = pos[0];
 		break;
 	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
-		if (elen < 12) {
+		if (elen < 10) {
 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
 				   "duration");
 			break;
 		}
+		os_free(rep->bss_term_dur);
 		rep->bss_term_dur =
 			os_zalloc(sizeof(struct bss_termination_duration));
 		if (rep->bss_term_dur == NULL)
 			break;
-		rep->bss_term_dur->present = 1;
-		os_memcpy(rep->bss_term_dur->duration, pos, 12);
+		os_memcpy(rep->bss_term_dur->duration, pos, 10);
 		break;
 	case WNM_NEIGHBOR_BEARING:
 		if (elen < 8) {
@@ -380,51 +386,51 @@
 				   "bearing");
 			break;
 		}
+		os_free(rep->bearing);
 		rep->bearing = os_zalloc(sizeof(struct bearing));
 		if (rep->bearing == NULL)
 			break;
-		rep->bearing->present = 1;
 		os_memcpy(rep->bearing->bearing, pos, 8);
 		break;
 	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
-		if (elen < 2) {
+		if (elen < 1) {
 			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
 				   "pilot");
 			break;
 		}
+		os_free(rep->meas_pilot);
 		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
 		if (rep->meas_pilot == NULL)
 			break;
-		rep->meas_pilot->present = 1;
 		rep->meas_pilot->measurement_pilot = pos[0];
-		rep->meas_pilot->num_vendor_specific = pos[1];
-		os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
+		rep->meas_pilot->subelem_len = elen - 1;
+		os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
 		break;
 	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
-		if (elen < 4) {
+		if (elen < 5) {
 			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
 				   "capabilities");
 			break;
 		}
+		os_free(rep->rrm_cap);
 		rep->rrm_cap =
 			os_zalloc(sizeof(struct rrm_enabled_capabilities));
 		if (rep->rrm_cap == NULL)
 			break;
-		rep->rrm_cap->present = 1;
-		os_memcpy(rep->rrm_cap->capabilities, pos, 4);
+		os_memcpy(rep->rrm_cap->capabilities, pos, 5);
 		break;
 	case WNM_NEIGHBOR_MULTIPLE_BSSID:
-		if (elen < 2) {
+		if (elen < 1) {
 			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
 			break;
 		}
+		os_free(rep->mul_bssid);
 		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
 		if (rep->mul_bssid == NULL)
 			break;
-		rep->mul_bssid->present = 1;
 		rep->mul_bssid->max_bssid_indicator = pos[0];
-		rep->mul_bssid->num_vendor_specific = pos[1];
-		os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
+		rep->mul_bssid->subelem_len = elen - 1;
+		os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
 		break;
 	}
 }
@@ -455,8 +461,15 @@
 
 		id = *pos++;
 		elen = *pos++;
+		wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
+		left -= 2;
+		if (elen > left) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Truncated neighbor report subelement");
+			break;
+		}
 		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
-		left -= 2 + elen;
+		left -= elen;
 		pos += elen;
 	}
 }
@@ -470,9 +483,12 @@
 
 	u8 i, j;
 
-	if (scan_res == NULL || num_neigh_rep == 0)
+	if (scan_res == NULL || num_neigh_rep == 0 || !wpa_s->current_bss)
 		return 0;
 
+	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
+		   MAC2STR(wpa_s->bssid), wpa_s->current_bss->level);
+
 	for (i = 0; i < num_neigh_rep; i++) {
 		for (j = 0; j < scan_res->num; j++) {
 			/* Check for a better RSSI AP */
@@ -483,8 +499,16 @@
 				/* Got a BSSID with better RSSI value */
 				os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
 					  ETH_ALEN);
+				wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR
+					   " with better scan RSSI %d",
+					   MAC2STR(scan_res->res[j]->bssid),
+					   scan_res->res[j]->level);
 				return 1;
 			}
+			wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR
+				   " RSSI %d", j,
+				   MAC2STR(scan_res->res[j]->bssid),
+				   scan_res->res[j]->level);
 		}
 	}
 
@@ -521,6 +545,14 @@
 	if (target_bssid) {
 		os_memcpy(pos, target_bssid, ETH_ALEN);
 		pos += ETH_ALEN;
+	} else if (status == WNM_BSS_TM_ACCEPT) {
+		/*
+		 * P802.11-REVmc clarifies that the Target BSSID field is always
+		 * present when status code is zero, so use a fake value here if
+		 * no BSSID is yet known.
+		 */
+		os_memset(pos, 0, ETH_ALEN);
+		pos += ETH_ALEN;
 	}
 
 	len = pos - (u8 *) &mgmt->u.action.category;
@@ -562,7 +594,7 @@
 			wnm_send_bss_transition_mgmt_resp(wpa_s,
 						  wpa_s->wnm_dialog_token,
 						  WNM_BSS_TM_ACCEPT,
-						  0, NULL);
+						  0, bssid);
 		}
 
 		wpa_s->reassociate = 1;
@@ -670,10 +702,12 @@
 				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
 				return;
 			}
-			wnm_parse_neighbor_report(
-				wpa_s, pos, len,
-				&wpa_s->wnm_neighbor_report_elements[
-					wpa_s->wnm_num_neighbor_report]);
+			if (tag == WLAN_EID_NEIGHBOR_REPORT) {
+				struct neighbor_report *rep;
+				rep = &wpa_s->wnm_neighbor_report_elements[
+					wpa_s->wnm_num_neighbor_report];
+				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
+			}
 
 			pos += len;
 			wpa_s->wnm_num_neighbor_report++;
@@ -717,7 +751,7 @@
 					   WLAN_FC_STYPE_ACTION);
 	mgmt->u.action.category = WLAN_ACTION_WNM;
 	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
-	mgmt->u.action.u.bss_tm_query.dialog_token = 0;
+	mgmt->u.action.u.bss_tm_query.dialog_token = 1;
 	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
 	pos = mgmt->u.action.u.bss_tm_query.variable;
 
@@ -731,23 +765,170 @@
 }
 
 
+static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
+					    const u8 *sa, const u8 *data,
+					    int len)
+{
+	const u8 *pos, *end, *next;
+	u8 ie, ie_len;
+
+	pos = data;
+	end = data + len;
+
+	while (pos + 1 < end) {
+		ie = *pos++;
+		ie_len = *pos++;
+		wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
+			   ie, ie_len);
+		if (ie_len > end - pos) {
+			wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
+				   "subelement");
+			break;
+		}
+		next = pos + ie_len;
+		if (ie_len < 4) {
+			pos = next;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
+			   WPA_GET_BE24(pos), pos[3]);
+
+#ifdef CONFIG_HS20
+		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
+		    WPA_GET_BE24(pos) == OUI_WFA &&
+		    pos[3] == HS20_WNM_SUB_REM_NEEDED) {
+			/* Subscription Remediation subelement */
+			const u8 *ie_end;
+			u8 url_len;
+			char *url;
+			u8 osu_method;
+
+			wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
+				   "subelement");
+			ie_end = pos + ie_len;
+			pos += 4;
+			url_len = *pos++;
+			if (url_len == 0) {
+				wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
+				url = NULL;
+				osu_method = 1;
+			} else {
+				if (pos + url_len + 1 > ie_end) {
+					wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
+						   url_len,
+						   (int) (ie_end - pos));
+					break;
+				}
+				url = os_malloc(url_len + 1);
+				if (url == NULL)
+					break;
+				os_memcpy(url, pos, url_len);
+				url[url_len] = '\0';
+				osu_method = pos[url_len];
+			}
+			hs20_rx_subscription_remediation(wpa_s, url,
+							 osu_method);
+			os_free(url);
+			pos = next;
+			continue;
+		}
+
+		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
+		    WPA_GET_BE24(pos) == OUI_WFA &&
+		    pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
+			const u8 *ie_end;
+			u8 url_len;
+			char *url;
+			u8 code;
+			u16 reauth_delay;
+
+			ie_end = pos + ie_len;
+			pos += 4;
+			code = *pos++;
+			reauth_delay = WPA_GET_LE16(pos);
+			pos += 2;
+			url_len = *pos++;
+			wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
+				   "Imminent - Reason Code %u   "
+				   "Re-Auth Delay %u  URL Length %u",
+				   code, reauth_delay, url_len);
+			if (pos + url_len > ie_end)
+				break;
+			url = os_malloc(url_len + 1);
+			if (url == NULL)
+				break;
+			os_memcpy(url, pos, url_len);
+			url[url_len] = '\0';
+			hs20_rx_deauth_imminent_notice(wpa_s, code,
+						       reauth_delay, url);
+			os_free(url);
+			pos = next;
+			continue;
+		}
+#endif /* CONFIG_HS20 */
+
+		pos = next;
+	}
+}
+
+
+static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
+					const u8 *sa, const u8 *frm, int len)
+{
+	const u8 *pos, *end;
+	u8 dialog_token, type;
+
+	/* Dialog Token [1] | Type [1] | Subelements */
+
+	if (len < 2 || sa == NULL)
+		return;
+	end = frm + len;
+	pos = frm;
+	dialog_token = *pos++;
+	type = *pos++;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
+		"(dialog_token %u type %u sa " MACSTR ")",
+		dialog_token, type, MAC2STR(sa));
+	wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
+		    pos, end - pos);
+
+	if (wpa_s->wpa_state != WPA_COMPLETED ||
+	    os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
+			"from our AP - ignore it");
+		return;
+	}
+
+	switch (type) {
+	case 1:
+		ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
+		break;
+	default:
+		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
+			"WNM-Notification type %u", type);
+		break;
+	}
+}
+
+
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
-			      struct rx_action *action)
+			      const struct ieee80211_mgmt *mgmt, size_t len)
 {
 	const u8 *pos, *end;
 	u8 act;
 
-	if (action->data == NULL || action->len == 0)
+	if (len < IEEE80211_HDRLEN + 2)
 		return;
 
-	pos = action->data;
-	end = pos + action->len;
+	pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
 	act = *pos++;
+	end = ((const u8 *) mgmt) + len;
 
 	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
-		   act, MAC2STR(action->sa));
+		   act, MAC2STR(mgmt->sa));
 	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
-	    os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+	    os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
 		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
 			   "frame");
 		return;
@@ -756,10 +937,13 @@
 	switch (act) {
 	case WNM_BSS_TRANS_MGMT_REQ:
 		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
-						 !(action->da[0] & 0x01));
+						 !(mgmt->da[0] & 0x01));
 		break;
 	case WNM_SLEEP_MODE_RESP:
-		ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
+		ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
+		break;
+	case WNM_NOTIFICATION_REQ:
+		ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
 		break;
 	default:
 		wpa_printf(MSG_ERROR, "WNM: Unknown request");
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 2933926..d2eb96d 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -9,52 +9,41 @@
 #ifndef WNM_STA_H
 #define WNM_STA_H
 
-struct rx_action;
-struct wpa_supplicant;
-
 struct tsf_info {
-	u8 present;
 	u8 tsf_offset[2];
 	u8 beacon_interval[2];
 };
 
 struct condensed_country_string {
-	u8 present;
 	u8 country_string[2];
 };
 
 struct bss_transition_candidate {
-	u8 present;
 	u8 preference;
 };
 
 struct bss_termination_duration {
-	u8 present;
-	u8 duration[12];
+	u8 duration[10];
 };
 
 struct bearing {
-	u8 present;
 	u8 bearing[8];
 };
 
 struct measurement_pilot {
-	u8 present;
 	u8 measurement_pilot;
-	u8 num_vendor_specific;
-	u8 vendor_specific[255];
+	u8 subelem_len;
+	u8 subelems[255];
 };
 
 struct rrm_enabled_capabilities {
-	u8 present;
-	u8 capabilities[4];
+	u8 capabilities[5];
 };
 
 struct multiple_bssid {
-	u8 present;
 	u8 max_bssid_indicator;
-	u8 num_vendor_specific;
-	u8 vendor_specific[255];
+	u8 subelem_len;
+	u8 subelems[255];
 };
 
 struct neighbor_report {
@@ -78,7 +67,7 @@
 				 u8 action, u16 intval, struct wpabuf *tfs_req);
 
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
-			      struct rx_action *action);
+			      const struct ieee80211_mgmt *mgmt, size_t len);
 
 void wnm_scan_response(struct wpa_supplicant *wpa_s,
 		       struct wpa_scan_results *scan_res);
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index a379d65..0ad6187 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -28,7 +28,7 @@
 
 static const char *wpa_cli_version =
 "wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *wpa_cli_license =
@@ -496,6 +496,8 @@
 		return wpa_ctrl_command(ctrl, "STATUS-VERBOSE");
 	if (argc > 0 && os_strcmp(argv[0], "wps") == 0)
 		return wpa_ctrl_command(ctrl, "STATUS-WPS");
+	if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
+		return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
 	return wpa_ctrl_command(ctrl, "STATUS");
 }
 
@@ -613,7 +615,9 @@
 		"p2p_oper_reg_class", "p2p_oper_channel",
 		"p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
 		"p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
+		"p2p_no_go_freq",
 		"p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
+		"p2p_go_vht",
 		"p2p_ignore_shared_freq", "country", "bss_max_count",
 		"bss_expiration_age", "bss_expiration_scan_count",
 		"filter_ssids", "filter_rssi", "max_num_sta",
@@ -623,9 +627,10 @@
 		"wps_nfc_dev_pw", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
 		"sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements",
-		"ignore_old_scan_res", "freq_list"
+		"ignore_old_scan_res", "freq_list", "external_sim",
+		"tdls_external_control", "p2p_search_delay"
 	};
-	int i, num_fields = sizeof(fields) / sizeof(fields[0]);
+	int i, num_fields = ARRAY_SIZE(fields);
 
 	if (arg == 1) {
 		char **res = os_calloc(num_fields + 1, sizeof(char *));
@@ -671,6 +676,12 @@
 }
 
 
+static int wpa_cli_cmd_reattach(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "REATTACH");
+}
+
+
 static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
 				       char *argv[])
 {
@@ -831,58 +842,6 @@
 }
 
 
-static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc,
-					   char *argv[])
-{
-	int ret;
-	char *buf;
-	size_t buflen;
-
-	if (argc != 1) {
-		printf("Invalid 'nfc_rx_handover_req' command - one argument "
-		       "is required.\n");
-		return -1;
-	}
-
-	buflen = 21 + os_strlen(argv[0]);
-	buf = os_malloc(buflen);
-	if (buf == NULL)
-		return -1;
-	os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]);
-
-	ret = wpa_ctrl_command(ctrl, buf);
-	os_free(buf);
-
-	return ret;
-}
-
-
-static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc,
-					   char *argv[])
-{
-	int ret;
-	char *buf;
-	size_t buflen;
-
-	if (argc != 1) {
-		printf("Invalid 'nfc_rx_handover_sel' command - one argument "
-		       "is required.\n");
-		return -1;
-	}
-
-	buflen = 21 + os_strlen(argv[0]);
-	buf = os_malloc(buflen);
-	if (buf == NULL)
-		return -1;
-	os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]);
-
-	ret = wpa_ctrl_command(ctrl, buf);
-	os_free(buf);
-
-	return ret;
-}
-
-
 static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc,
 					   char *argv[])
 {
@@ -1268,6 +1227,38 @@
 }
 
 
+static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256], *pos, *end;
+	int i, ret;
+
+	if (argc < 2) {
+		printf("Invalid SIM command: needs two arguments "
+		       "(network id and SIM operation response)\n");
+		return -1;
+	}
+
+	end = cmd + sizeof(cmd);
+	pos = cmd;
+	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s",
+			  argv[0], argv[1]);
+	if (ret < 0 || ret >= end - pos) {
+		printf("Too long SIM command.\n");
+		return -1;
+	}
+	pos += ret;
+	for (i = 2; i < argc; i++) {
+		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (ret < 0 || ret >= end - pos) {
+			printf("Too long SIM command.\n");
+			return -1;
+		}
+		pos += ret;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
@@ -1425,6 +1416,24 @@
 }
 
 
+static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	if (argc == 0) {
+		wpa_cli_show_network_variables();
+		return 0;
+	}
+
+	if (argc < 3) {
+		printf("Invalid DUP_NETWORK command: needs three arguments\n"
+		       "(src netid, dest netid, and variable name)\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "DUP_NETWORK", 3, argc, argv);
+}
+
+
 static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
@@ -1457,6 +1466,18 @@
 }
 
 
+static int wpa_cli_cmd_get_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc != 2) {
+		printf("Invalid GET_CRED command: needs two arguments\n"
+		       "(cred id, variable name)\n");
+		return -1;
+	}
+
+	return wpa_cli_cmd(ctrl, "GET_CRED", 2, argc, argv);
+}
+
+
 static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
@@ -1549,7 +1570,7 @@
 	os_free(ctrl_ifname);
 	ctrl_ifname = os_strdup(argv[0]);
 
-	if (wpa_cli_open_connection(ctrl_ifname, 1)) {
+	if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
 		printf("Connected to interface '%s.\n", ctrl_ifname);
 	} else {
 		printf("Could not connect to interface '%s' - re-trying\n",
@@ -1687,6 +1708,13 @@
 {
 	return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
 }
+
+static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "CHAN_SWITCH", 2, argc, argv);
+}
+
 #endif /* CONFIG_AP */
 
 
@@ -1702,10 +1730,12 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
 static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_ctrl_command(ctrl, "DROP_SA");
 }
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
 static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -2078,7 +2108,7 @@
 		"disc_int",
 		"per_sta_psk",
 	};
-	int i, num_fields = sizeof(fields) / sizeof(fields[0]);
+	int i, num_fields = ARRAY_SIZE(fields);
 
 	if (arg == 1) {
 		char **res = os_calloc(num_fields + 1, sizeof(char *));
@@ -2280,6 +2310,37 @@
 	return wpa_ctrl_command(ctrl, cmd);
 }
 
+
+static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	char cmd[512];
+
+	if (argc < 2) {
+		printf("Command needs two arguments (dst mac addr and "
+		       "icon name)\n");
+		return -1;
+	}
+
+	if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0)
+		return -1;
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "FETCH_OSU");
+}
+
+
+static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU");
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -2372,23 +2433,15 @@
 #ifdef ANDROID
 static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	char cmd[256];
-	int i;
-	int len;
-
-	if (argc < 1) {
-		printf("Invalid DRIVER command: needs one argument (cmd)\n");
-		return -1;
-	}
-
-	len = os_snprintf(cmd, sizeof(cmd), "DRIVER %s", argv[0]);
-	for (i=1; i < argc; i++)
-		len += os_snprintf(cmd + len, sizeof(cmd) - len, " %s", argv[i]);
-	cmd[sizeof(cmd) - 1] = '\0';
-	printf("%s: %s\n", __func__, cmd);
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
 }
-#endif
+#endif /* ANDROID */
+
+
+static int wpa_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "VENDOR", 1, argc, argv);
+}
 
 
 static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -2397,6 +2450,12 @@
 }
 
 
+static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "RADIO_WORK", 1, argc, argv);
+}
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -2463,6 +2522,9 @@
 	{ "reassociate", wpa_cli_cmd_reassociate, NULL,
 	  cli_cmd_flag_none,
 	  "= force reassociation" },
+	{ "reattach", wpa_cli_cmd_reattach, NULL,
+	  cli_cmd_flag_none,
+	  "= force reassociation back to the same BSS" },
 	{ "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<BSSID> = force preauthentication" },
@@ -2486,6 +2548,9 @@
 	  cli_cmd_flag_sensitive,
 	  "<network id> <passphrase> = configure private key passphrase\n"
 	  "  for an SSID" },
+	{ "sim", wpa_cli_cmd_sim, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<network id> <pin> = report SIM operation result" },
 	{ "bssid", wpa_cli_cmd_bssid, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> <BSSID> = set preferred BSSID for an SSID" },
@@ -2523,6 +2588,10 @@
 	{ "get_network", wpa_cli_cmd_get_network, NULL,
 	  cli_cmd_flag_none,
 	  "<network id> <variable> = get network variables" },
+	{ "dup_network", wpa_cli_cmd_dup_network, NULL,
+	  cli_cmd_flag_none,
+	  "<src network id> <dst network id> <variable> = duplicate network variables"
+	},
 	{ "list_creds", wpa_cli_cmd_list_creds, NULL,
 	  cli_cmd_flag_none,
 	  "= list configured credentials" },
@@ -2535,6 +2604,9 @@
 	{ "set_cred", wpa_cli_cmd_set_cred, NULL,
 	  cli_cmd_flag_sensitive,
 	  "<cred id> <variable> <value> = set credential variables" },
+	{ "get_cred", wpa_cli_cmd_get_cred, NULL,
+	  cli_cmd_flag_none,
+	  "<cred id> <variable> = get credential variables" },
 	{ "save_config", wpa_cli_cmd_save_config, NULL,
 	  cli_cmd_flag_none,
 	  "= save the current configuration" },
@@ -2628,12 +2700,6 @@
 	{ "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
 	  cli_cmd_flag_none,
 	  "<NDEF> <WPS> = create NFC handover select" },
-	{ "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL,
-	  cli_cmd_flag_none,
-	  "<hexdump of payload> = report received NFC handover request" },
-	{ "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL,
-	  cli_cmd_flag_none,
-	  "<hexdump of payload> = report received NFC handover select" },
 	{ "nfc_report_handover", wpa_cli_cmd_nfc_report_handover, NULL,
 	  cli_cmd_flag_none,
 	  "<role> <type> <hexdump of req> <hexdump of sel> = report completed "
@@ -2687,13 +2753,20 @@
 	{ "disassociate", wpa_cli_cmd_disassociate, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = disassociate a station" },
+	{ "chan_switch", wpa_cli_cmd_chanswitch, NULL,
+	  cli_cmd_flag_none,
+	  "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]"
+	  " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]"
+	  " = CSA parameters" },
 #endif /* CONFIG_AP */
 	{ "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none,
 	  "= notification of suspend/hibernate" },
 	{ "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none,
 	  "= notification of resume/thaw" },
+#ifdef CONFIG_TESTING_OPTIONS
 	{ "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none,
 	  "= drop SA without deauth/disassoc (test command)" },
+#endif /* CONFIG_TESTING_OPTIONS */
 	{ "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<addr> = roam to the specified BSS" },
@@ -2816,6 +2889,14 @@
 	{ "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
 	  wpa_cli_complete_bss, cli_cmd_flag_none,
 	  "<addr> <home realm> = get HS20 nai home realm list" },
+	{ "hs20_icon_request", wpa_cli_cmd_hs20_icon_request,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
+	  "<addr> <icon name> = get Hotspot 2.0 OSU icon" },
+	{ "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none,
+	  "= fetch OSU provider information from all APs" },
+	{ "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL,
+	  cli_cmd_flag_none,
+	  "= cancel fetch_osu command" },
 #endif /* CONFIG_HS20 */
 	{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
 	  cli_cmd_flag_none,
@@ -2853,10 +2934,14 @@
 	{ "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none,
 	  "= flush wpa_supplicant state" },
 #ifdef ANDROID
-	{ "driver", wpa_cli_cmd_driver, NULL,
-	  cli_cmd_flag_none,
+	{ "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none,
 	  "<command> = driver private commands" },
-#endif
+#endif /* ANDROID */
+	{ "radio_work", wpa_cli_cmd_radio_work, NULL, cli_cmd_flag_none,
+	  "= radio_work <show/add/done>" },
+	{ "vendor", wpa_cli_cmd_vendor, NULL, cli_cmd_flag_none,
+	  "<vendor id> <command id> [<hex formatted command argument>] = Send vendor command"
+	},
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -2917,7 +3002,7 @@
 	int i, count;
 	struct cli_txt_entry *e;
 
-	count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]);
+	count = ARRAY_SIZE(wpa_cli_commands);
 	count += dl_list_len(&p2p_groups);
 	count += dl_list_len(&ifnames);
 	res = os_calloc(count + 1, sizeof(char *));
@@ -3064,28 +3149,19 @@
 static int wpa_cli_exec(const char *program, const char *arg1,
 			const char *arg2)
 {
-	char *cmd;
+	char *arg;
 	size_t len;
 	int res;
-	int ret = 0;
 
-	len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
-	cmd = os_malloc(len);
-	if (cmd == NULL)
+	len = os_strlen(arg1) + os_strlen(arg2) + 2;
+	arg = os_malloc(len);
+	if (arg == NULL)
 		return -1;
-	res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
-	if (res < 0 || (size_t) res >= len) {
-		os_free(cmd);
-		return -1;
-	}
-	cmd[len - 1] = '\0';
-#ifndef _WIN32_WCE
-	if (system(cmd) < 0)
-		ret = -1;
-#endif /* _WIN32_WCE */
-	os_free(cmd);
+	os_snprintf(arg, len, "%s %s", arg1, arg2);
+	res = os_exec(program, arg, 1);
+	os_free(arg);
 
-	return ret;
+	return res;
 }
 
 
@@ -3165,6 +3241,10 @@
 		wpa_cli_exec(action_file, ctrl_ifname, pos);
 	} else if (str_match(pos, ESS_DISASSOC_IMMINENT)) {
 		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
 	} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
 		printf("wpa_supplicant is terminating - stop monitoring\n");
 		wpa_cli_quit = 1;
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 4afaae9..5426177 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -552,8 +552,6 @@
 }
 
 
-extern struct wpa_driver_ops *wpa_drivers[];
-
 static struct wpa_priv_interface *
 wpa_priv_interface_init(const char *dir, const char *params)
 {
@@ -946,8 +944,6 @@
 }
 
 
-extern int wpa_debug_level;
-
 int main(int argc, char *argv[])
 {
 	int c, i;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index b431662..9d21fe0 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -51,10 +51,11 @@
 #include "offchannel.h"
 #include "hs20_supplicant.h"
 #include "wnm_sta.h"
+#include "wpas_kay.h"
 
 const char *wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors";
 
 const char *wpa_supplicant_license =
 "This software may be distributed under the terms of the BSD license.\n"
@@ -104,10 +105,8 @@
 "\n";
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-extern struct wpa_driver_ops *wpa_drivers[];
+struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
+						struct wpa_driver_capa *capa);
 
 /* Configure default/group WEP keys for static WEP */
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
@@ -200,8 +199,6 @@
 	 * So, wait a second until scanning again.
 	 */
 	wpa_supplicant_req_scan(wpa_s, 1, 0);
-
-	wpas_p2p_continue_after_scan(wpa_s);
 }
 
 
@@ -217,7 +214,7 @@
 void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
 				     int sec, int usec)
 {
-	if (wpa_s->conf && wpa_s->conf->ap_scan == 0 &&
+	if (wpa_s->conf->ap_scan == 0 &&
 	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
 		return;
 
@@ -293,18 +290,20 @@
 				EAPOL_REQUIRE_KEY_BROADCAST;
 		}
 
-		if (wpa_s->conf && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
 			eapol_conf.required_keys = 0;
 	}
-	if (wpa_s->conf)
-		eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+	eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
 	eapol_conf.workaround = ssid->eap_workaround;
 	eapol_conf.eap_disabled =
 		!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
 		wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
 		wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
+	eapol_conf.external_sim = wpa_s->conf->external_sim;
 	eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
 #endif /* IEEE8021X_EAPOL */
+
+	ieee802_1x_alloc_kay_sm(wpa_s, ssid);
 }
 
 
@@ -379,6 +378,8 @@
 
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
+	int i;
+
 	bgscan_deinit(wpa_s);
 	autoscan_deinit(wpa_s);
 	scard_deinit(wpa_s->scard);
@@ -404,6 +405,11 @@
 	os_free(wpa_s->confanother);
 	wpa_s->confanother = NULL;
 
+#ifdef CONFIG_P2P
+	os_free(wpa_s->conf_p2p_dev);
+	wpa_s->conf_p2p_dev = NULL;
+#endif /* CONFIG_P2P */
+
 	wpa_sm_set_eapol(wpa_s->wpa, NULL);
 	eapol_sm_deinit(wpa_s->eapol);
 	wpa_s->eapol = NULL;
@@ -446,9 +452,7 @@
 	wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
 
-#ifdef CONFIG_P2P
 	wpas_p2p_deinit(wpa_s);
-#endif /* CONFIG_P2P */
 
 #ifdef CONFIG_OFFCHANNEL
 	offchannel_deinit(wpa_s);
@@ -459,11 +463,19 @@
 	os_free(wpa_s->next_scan_freqs);
 	wpa_s->next_scan_freqs = NULL;
 
+	os_free(wpa_s->manual_scan_freqs);
+	wpa_s->manual_scan_freqs = NULL;
+
+	os_free(wpa_s->manual_sched_scan_freqs);
+	wpa_s->manual_sched_scan_freqs = NULL;
+
 	gas_query_deinit(wpa_s->gas);
 	wpa_s->gas = NULL;
 
 	free_hw_features(wpa_s);
 
+	ieee802_1x_dealloc_kay_sm(wpa_s);
+
 	os_free(wpa_s->bssid_filter);
 	wpa_s->bssid_filter = NULL;
 
@@ -481,9 +493,21 @@
 	wpa_s->ext_pw = NULL;
 
 	wpabuf_free(wpa_s->last_gas_resp);
+	wpa_s->last_gas_resp = NULL;
+	wpabuf_free(wpa_s->prev_gas_resp);
+	wpa_s->prev_gas_resp = NULL;
 
 	os_free(wpa_s->last_scan_res);
 	wpa_s->last_scan_res = NULL;
+
+#ifdef CONFIG_HS20
+	hs20_deinit(wpa_s);
+#endif /* CONFIG_HS20 */
+
+	for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+		wpabuf_free(wpa_s->vendor_elem[i]);
+		wpa_s->vendor_elem[i] = NULL;
+	}
 }
 
 
@@ -497,29 +521,23 @@
  */
 void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 {
-	if (wpa_s->keys_cleared) {
-		/* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
-		 * timing issues with keys being cleared just before new keys
-		 * are set or just after association or something similar. This
-		 * shows up in group key handshake failing often because of the
-		 * client not receiving the first encrypted packets correctly.
-		 * Skipping some of the extra key clearing steps seems to help
-		 * in completing group key handshake more reliably. */
-		wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - "
-			"skip key clearing");
-		return;
-	}
+	int i, max;
+
+#ifdef CONFIG_IEEE80211W
+	max = 6;
+#else /* CONFIG_IEEE80211W */
+	max = 4;
+#endif /* CONFIG_IEEE80211W */
 
 	/* MLME-DELETEKEYS.request */
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
-	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 */
-	if (addr) {
+	for (i = 0; i < max; i++) {
+		if (wpa_s->keys_cleared & BIT(i))
+			continue;
+		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+				NULL, 0);
+	}
+	if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
+	    !is_zero_ether_addr(addr)) {
 		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
 				0);
 		/* MLME-SETPROTECTION.request(None) */
@@ -528,7 +546,7 @@
 			MLME_SETPROTECTION_PROTECT_TYPE_NONE,
 			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 	}
-	wpa_s->keys_cleared = 1;
+	wpa_s->keys_cleared = (u32) -1;
 }
 
 
@@ -570,14 +588,26 @@
 
 static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
 {
+	const char *name;
+
+	if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan)
+		name = wpa_s->current_ssid->bgscan;
+	else
+		name = wpa_s->conf->bgscan;
+	if (name == NULL || name[0] == '\0')
+		return;
 	if (wpas_driver_bss_selection(wpa_s))
 		return;
 	if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
 		return;
+#ifdef CONFIG_P2P
+	if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+		return;
+#endif /* CONFIG_P2P */
 
 	bgscan_deinit(wpa_s);
-	if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
-		if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+	if (wpa_s->current_ssid) {
+		if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
 				"bgscan");
 			/*
@@ -651,11 +681,16 @@
 		wpa_supplicant_state_txt(wpa_s->wpa_state),
 		wpa_supplicant_state_txt(state));
 
-#ifdef ANDROID_P2P
-	if(state == WPA_ASSOCIATED && wpa_s->current_ssid) {
-		wpa_s->current_ssid->assoc_retry = 0;
+	if (state == WPA_INTERFACE_DISABLED) {
+		/* Assure normal scan when interface is restored */
+		wpa_s->normal_scans = 0;
 	}
-#endif /* ANDROID_P2P */
+
+	if (state == WPA_COMPLETED) {
+		wpas_connect_work_done(wpa_s);
+		/* Reinitialize normal_scan counter */
+		wpa_s->normal_scans = 0;
+	}
 
 	if (state != WPA_SCANNING)
 		wpa_supplicant_notify_scanning(wpa_s, 0);
@@ -664,7 +699,7 @@
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
-			MACSTR " completed (auth) [id=%d id_str=%s]",
+			MACSTR " completed [id=%d id_str=%s]",
 			MAC2STR(wpa_s->bssid),
 			ssid ? ssid->id : -1,
 			ssid && ssid->id_str ? ssid->id_str : "");
@@ -677,9 +712,8 @@
 		wpa_drv_set_supp_port(wpa_s, 1);
 #endif /* IEEE8021X_EAPOL */
 		wpa_s->after_wps = 0;
-#ifdef CONFIG_P2P
+		wpa_s->known_wps_freq = 0;
 		wpas_p2p_completed(wpa_s);
-#endif /* CONFIG_P2P */
 
 		sme_sched_obss_scan(wpa_s, 1);
 	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
@@ -696,7 +730,7 @@
 #ifdef CONFIG_BGSCAN
 	if (state == WPA_COMPLETED)
 		wpa_supplicant_start_bgscan(wpa_s);
-	else
+	else if (state < WPA_ASSOCIATED)
 		wpa_supplicant_stop_bgscan(wpa_s);
 #endif /* CONFIG_BGSCAN */
 
@@ -709,6 +743,12 @@
 	if (wpa_s->wpa_state != old_state) {
 		wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
 
+		/*
+		 * Notify the P2P Device interface about a state change in one
+		 * of the interfaces.
+		 */
+		wpas_p2p_indicate_state_change(wpa_s);
+
 		if (wpa_s->wpa_state == WPA_COMPLETED ||
 		    old_state == WPA_COMPLETED)
 			wpas_notify_auth_changed(wpa_s);
@@ -722,9 +762,15 @@
 #ifdef CONFIG_WPS
 	struct wpa_supplicant *wpa_s = global->ifaces;
 	while (wpa_s) {
+		struct wpa_supplicant *next = wpa_s->next;
 		if (wpas_wps_terminate_pending(wpa_s) == 1)
 			pending = 1;
-		wpa_s = wpa_s->next;
+#ifdef CONFIG_P2P
+		if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE ||
+		    (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group))
+			wpas_p2p_disconnect(wpa_s);
+#endif /* CONFIG_P2P */
+		wpa_s = next;
 	}
 #endif /* CONFIG_WPS */
 	if (pending)
@@ -853,34 +899,6 @@
 }
 
 
-enum wpa_key_mgmt key_mgmt2driver(int key_mgmt)
-{
-	switch (key_mgmt) {
-	case WPA_KEY_MGMT_NONE:
-		return KEY_MGMT_NONE;
-	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
-		return KEY_MGMT_802_1X_NO_WPA;
-	case WPA_KEY_MGMT_IEEE8021X:
-		return KEY_MGMT_802_1X;
-	case WPA_KEY_MGMT_WPA_NONE:
-		return KEY_MGMT_WPA_NONE;
-	case WPA_KEY_MGMT_FT_IEEE8021X:
-		return KEY_MGMT_FT_802_1X;
-	case WPA_KEY_MGMT_FT_PSK:
-		return KEY_MGMT_FT_PSK;
-	case WPA_KEY_MGMT_IEEE8021X_SHA256:
-		return KEY_MGMT_802_1X_SHA256;
-	case WPA_KEY_MGMT_PSK_SHA256:
-		return KEY_MGMT_PSK_SHA256;
-	case WPA_KEY_MGMT_WPS:
-		return KEY_MGMT_WPS;
-	case WPA_KEY_MGMT_PSK:
-	default:
-		return KEY_MGMT_PSK;
-	}
-}
-
-
 static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid,
 					 struct wpa_ie_data *ie)
@@ -951,13 +969,14 @@
 {
 	struct wpa_ie_data ie;
 	int sel, proto;
-	const u8 *bss_wpa, *bss_rsn;
+	const u8 *bss_wpa, *bss_rsn, *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_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 	} else
-		bss_wpa = bss_rsn = NULL;
+		bss_wpa = bss_rsn = bss_osen = NULL;
 
 	if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
 	    wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
@@ -973,11 +992,23 @@
 		   (ie.key_mgmt & ssid->key_mgmt)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
 		proto = WPA_PROTO_WPA;
+#ifdef CONFIG_HS20
+	} else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
+		/* TODO: parse OSEN element */
+		os_memset(&ie, 0, sizeof(ie));
+		ie.group_cipher = WPA_CIPHER_CCMP;
+		ie.pairwise_cipher = WPA_CIPHER_CCMP;
+		ie.key_mgmt = WPA_KEY_MGMT_OSEN;
+		proto = WPA_PROTO_OSEN;
+#endif /* CONFIG_HS20 */
 	} else if (bss) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
 		return -1;
 	} else {
-		if (ssid->proto & WPA_PROTO_RSN)
+		if (ssid->proto & WPA_PROTO_OSEN)
+			proto = WPA_PROTO_OSEN;
+		else if (ssid->proto & WPA_PROTO_RSN)
 			proto = WPA_PROTO_RSN;
 		else
 			proto = WPA_PROTO_WPA;
@@ -1010,7 +1041,7 @@
 	wpa_s->wpa_proto = proto;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
-			 !!(ssid->proto & WPA_PROTO_RSN));
+			 !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
 
 	if (bss || !wpa_s->ap_ies_from_associnfo) {
 		if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
@@ -1081,6 +1112,11 @@
 	} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+#ifdef CONFIG_HS20
+	} else if (sel & WPA_KEY_MGMT_OSEN) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
+		wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
+#endif /* CONFIG_HS20 */
 	} else {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
 			"authenticated key management type");
@@ -1102,6 +1138,18 @@
 		wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
 			"AES-128-CMAC");
+	} else if (sel & WPA_CIPHER_BIP_GMAC_128) {
+		wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+			"BIP-GMAC-128");
+	} else if (sel & WPA_CIPHER_BIP_GMAC_256) {
+		wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+			"BIP-GMAC-256");
+	} else if (sel & WPA_CIPHER_BIP_CMAC_256) {
+		wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+			"BIP-CMAC-256");
 	} else {
 		wpa_s->mgmt_group_cipher = 0;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
@@ -1220,8 +1268,16 @@
 #endif /* CONFIG_INTERWORKING */
 		break;
 	case 4: /* Bits 32-39 */
+#ifdef CONFIG_INTERWORKING
+		if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING)
+			*pos |= 0x01; /* Bit 32 - QoS Map */
+#endif /* CONFIG_INTERWORKING */
 		break;
 	case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+		if (wpa_s->conf->hs20)
+			*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
 		break;
 	case 6: /* Bits 48-55 */
 		break;
@@ -1229,13 +1285,18 @@
 }
 
 
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 {
 	u8 *pos = buf;
-	u8 len = 4, i;
+	u8 len = 6, i;
 
 	if (len < wpa_s->extended_capa_len)
 		len = wpa_s->extended_capa_len;
+	if (buflen < (size_t) len + 2) {
+		wpa_printf(MSG_INFO,
+			   "Not enough room for building extended capabilities element");
+		return -1;
+	}
 
 	*pos++ = WLAN_EID_EXT_CAPAB;
 	*pos++ = len;
@@ -1259,6 +1320,70 @@
 }
 
 
+static int wpas_valid_bss(struct wpa_supplicant *wpa_s,
+			  struct wpa_bss *test_bss)
+{
+	struct wpa_bss *bss;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (bss == test_bss)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int wpas_valid_ssid(struct wpa_supplicant *wpa_s,
+			   struct wpa_ssid *test_ssid)
+{
+	struct wpa_ssid *ssid;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid == test_ssid)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+			struct wpa_ssid *test_ssid)
+{
+	if (test_bss && !wpas_valid_bss(wpa_s, test_bss))
+		return 0;
+
+	return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid);
+}
+
+
+void wpas_connect_work_free(struct wpa_connect_work *cwork)
+{
+	if (cwork == NULL)
+		return;
+	os_free(cwork);
+}
+
+
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_connect_work *cwork;
+	struct wpa_radio_work *work = wpa_s->connect_work;
+
+	if (!work)
+		return;
+
+	wpa_s->connect_work = NULL;
+	cwork = work->ctx;
+	work->ctx = NULL;
+	wpas_connect_work_free(cwork);
+	radio_work_done(work);
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
+
 /**
  * wpa_supplicant_associate - Request association
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1270,29 +1395,12 @@
 void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
-	u8 wpa_ie[200];
-	size_t wpa_ie_len;
-	int use_crypt, ret, i, bssid_changed;
-	int algs = WPA_AUTH_ALG_OPEN;
-	enum wpa_cipher cipher_pairwise, cipher_group;
-	struct wpa_driver_associate_params params;
-	int wep_keys_set = 0;
-	int assoc_failed = 0;
-	struct wpa_ssid *old_ssid;
-	u8 ext_capab[10];
-	int ext_capab_len;
-#ifdef CONFIG_HT_OVERRIDES
-	struct ieee80211_ht_capabilities htcaps;
-	struct ieee80211_ht_capabilities htcaps_mask;
-#endif /* CONFIG_HT_OVERRIDES */
+	struct wpa_connect_work *cwork;
 
 #ifdef CONFIG_IBSS_RSN
 	ibss_rsn_deinit(wpa_s->ibss_rsn);
 	wpa_s->ibss_rsn = NULL;
 #endif /* CONFIG_IBSS_RSN */
-#ifdef ANDROID_P2P
-	int freq = 0;
-#endif
 
 	if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
 	    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
@@ -1328,8 +1436,77 @@
 		return;
 	}
 
+	if (wpa_s->connect_work) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
+		return;
+	}
+
+	if (radio_work_pending(wpa_s, "connect")) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
+		return;
+	}
+
+	cwork = os_zalloc(sizeof(*cwork));
+	if (cwork == NULL)
+		return;
+
+	cwork->bss = bss;
+	cwork->ssid = ssid;
+
+	if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
+			   wpas_start_assoc_cb, cwork) < 0) {
+		os_free(cwork);
+	}
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_connect_work *cwork = work->ctx;
+	struct wpa_bss *bss = cwork->bss;
+	struct wpa_ssid *ssid = cwork->ssid;
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	u8 wpa_ie[200];
+	size_t wpa_ie_len;
+	int use_crypt, ret, i, bssid_changed;
+	int algs = WPA_AUTH_ALG_OPEN;
+	unsigned int cipher_pairwise, cipher_group;
+	struct wpa_driver_associate_params params;
+	int wep_keys_set = 0;
+	int assoc_failed = 0;
+	struct wpa_ssid *old_ssid;
+#ifdef CONFIG_HT_OVERRIDES
+	struct ieee80211_ht_capabilities htcaps;
+	struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       struct ieee80211_vht_capabilities vhtcaps;
+       struct ieee80211_vht_capabilities vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	if (deinit) {
+		if (work->started) {
+			wpa_s->connect_work = NULL;
+
+			/* cancel possible auth. timeout */
+			eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
+					     NULL);
+		}
+		wpas_connect_work_free(cwork);
+		return;
+	}
+
+	wpa_s->connect_work = work;
+
+	if (!wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
+		wpas_connect_work_done(wpa_s);
+		return;
+	}
+
 	os_memset(&params, 0, sizeof(params));
 	wpa_s->reassociate = 0;
+	wpa_s->eap_expected_failure = 0;
 	if (bss && !wpas_driver_bss_selection(wpa_s)) {
 #ifdef CONFIG_IEEE80211R
 		const u8 *ie, *md = NULL;
@@ -1479,6 +1656,8 @@
 				"disallows" : "allows");
 		}
 	}
+
+	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_HS20
@@ -1486,30 +1665,49 @@
 		struct wpabuf *hs20;
 		hs20 = wpabuf_alloc(20);
 		if (hs20) {
-			wpas_hs20_add_indication(hs20);
-			os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
-				  wpabuf_len(hs20));
-			wpa_ie_len += wpabuf_len(hs20);
+			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+			size_t len;
+
+			wpas_hs20_add_indication(hs20, pps_mo_id);
+			len = sizeof(wpa_ie) - wpa_ie_len;
+			if (wpabuf_len(hs20) <= len) {
+				os_memcpy(wpa_ie + wpa_ie_len,
+					  wpabuf_head(hs20), wpabuf_len(hs20));
+				wpa_ie_len += wpabuf_len(hs20);
+			}
 			wpabuf_free(hs20);
 		}
 	}
 #endif /* CONFIG_HS20 */
 
-	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
-	if (ext_capab_len > 0) {
-		u8 *pos = wpa_ie;
-		if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
-			pos += 2 + pos[1];
-		os_memmove(pos + ext_capab_len, pos,
-			   wpa_ie_len - (pos - wpa_ie));
-		wpa_ie_len += ext_capab_len;
-		os_memcpy(pos, ext_capab, ext_capab_len);
+	/*
+	 * Workaround: Add Extended Capabilities element only if the AP
+	 * included this element in Beacon/Probe Response frames. Some older
+	 * APs seem to have interoperability issues if this element is
+	 * included, so while the standard may require us to include the
+	 * element in all cases, it is justifiable to skip it to avoid
+	 * interoperability issues.
+	 */
+	if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
+		u8 ext_capab[18];
+		int ext_capab_len;
+		ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+						     sizeof(ext_capab));
+		if (ext_capab_len > 0) {
+			u8 *pos = wpa_ie;
+			if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+				pos += 2 + pos[1];
+			os_memmove(pos + ext_capab_len, pos,
+				   wpa_ie_len - (pos - wpa_ie));
+			wpa_ie_len += ext_capab_len;
+			os_memcpy(pos, ext_capab, ext_capab_len);
+		}
 	}
 
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
-	cipher_pairwise = wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
-	cipher_group = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
+	cipher_pairwise = wpa_s->pairwise_cipher;
+	cipher_group = wpa_s->group_cipher;
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 		if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
@@ -1533,7 +1731,7 @@
 			/* Assume that dynamic WEP-104 keys will be used and
 			 * set cipher suites in order for drivers to expect
 			 * encryption. */
-			cipher_pairwise = cipher_group = CIPHER_WEP104;
+			cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
 		}
 	}
 #endif /* IEEE8021X_EAPOL */
@@ -1556,6 +1754,8 @@
 			params.bssid = bss->bssid;
 			params.freq = bss->freq;
 		}
+		params.bssid_hint = bss->bssid;
+		params.freq_hint = bss->freq;
 	} else {
 		params.ssid = ssid->ssid;
 		params.ssid_len = ssid->ssid_len;
@@ -1570,11 +1770,19 @@
 	if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 &&
 	    params.freq == 0)
 		params.freq = ssid->frequency; /* Initial channel for IBSS */
+
+	if (ssid->mode == WPAS_MODE_IBSS) {
+		if (ssid->beacon_int)
+			params.beacon_int = ssid->beacon_int;
+		else
+			params.beacon_int = wpa_s->conf->beacon_int;
+	}
+
 	params.wpa_ie = wpa_ie;
 	params.wpa_ie_len = wpa_ie_len;
 	params.pairwise_suite = cipher_pairwise;
 	params.group_suite = cipher_group;
-	params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+	params.key_mgmt_suite = wpa_s->key_mgmt;
 	params.wpa_proto = wpa_s->wpa_proto;
 	params.auth_alg = algs;
 	params.mode = ssid->mode;
@@ -1587,8 +1795,8 @@
 	params.wep_tx_keyidx = ssid->wep_tx_keyidx;
 
 	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
-	    (params.key_mgmt_suite == KEY_MGMT_PSK ||
-	     params.key_mgmt_suite == KEY_MGMT_FT_PSK)) {
+	    (params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
 		params.passphrase = ssid->passphrase;
 		if (ssid->psk_set)
 			params.psk = ssid->psk;
@@ -1628,19 +1836,35 @@
 	params.htcaps_mask = (u8 *) &htcaps_mask;
 	wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+	os_memset(&vhtcaps, 0, sizeof(vhtcaps));
+	os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
+	params.vhtcaps = &vhtcaps;
+	params.vhtcaps_mask = &vhtcaps_mask;
+	wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, &params);
+#endif /* CONFIG_VHT_OVERRIDES */
 
-#ifdef ANDROID_P2P
-	/* If multichannel concurrency is not supported, check for any frequency
-	 * conflict and take appropriate action.
+#ifdef CONFIG_P2P
+	/*
+	 * If multi-channel concurrency is not supported, check for any
+	 * frequency conflict. In case of any frequency conflict, remove the
+	 * least prioritized connection.
 	 */
-	if ((wpa_s->num_multichan_concurrent < 2) &&
-		((freq = wpa_drv_shared_freq(wpa_s)) > 0) && (freq != params.freq)) {
-		wpa_printf(MSG_DEBUG, "Shared interface with conflicting frequency found (%d != %d)"
-																, freq, params.freq);
-		if (wpas_p2p_handle_frequency_conflicts(wpa_s, params.freq, ssid) < 0) 
-			return;
+	if (wpa_s->num_multichan_concurrent < 2) {
+		int freq, num;
+		num = get_shared_radio_freqs(wpa_s, &freq, 1);
+		if (num > 0 && freq > 0 && freq != params.freq) {
+			wpa_printf(MSG_DEBUG,
+				   "Assoc conflicting freq found (%d != %d)",
+				   freq, params.freq);
+			if (wpas_p2p_handle_frequency_conflicts(wpa_s,
+								params.freq,
+								ssid) < 0)
+				return;
+		}
 	}
-#endif
+#endif /* CONFIG_P2P */
+
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -1952,6 +2176,59 @@
 
 
 /**
+ * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @pkcs11_engine_path: PKCS #11 engine path or NULL
+ * @pkcs11_module_path: PKCS #11 module path or NULL
+ * Returns: 0 on success; -1 on failure
+ *
+ * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid
+ * path. If resetting the EAPOL state machine with the new PKCS #11 engine and
+ * module path fails the paths will be reset to the default value (NULL).
+ */
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+					   const char *pkcs11_engine_path,
+					   const char *pkcs11_module_path)
+{
+	char *pkcs11_engine_path_copy = NULL;
+	char *pkcs11_module_path_copy = NULL;
+
+	if (pkcs11_engine_path != NULL) {
+		pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path);
+		if (pkcs11_engine_path_copy == NULL)
+			return -1;
+	}
+	if (pkcs11_module_path != NULL) {
+		pkcs11_module_path_copy = os_strdup(pkcs11_module_path);
+		if (pkcs11_module_path_copy == NULL) {
+			os_free(pkcs11_engine_path_copy);
+			return -1;
+		}
+	}
+
+	os_free(wpa_s->conf->pkcs11_engine_path);
+	os_free(wpa_s->conf->pkcs11_module_path);
+	wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy;
+	wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy;
+
+	wpa_sm_set_eapol(wpa_s->wpa, NULL);
+	eapol_sm_deinit(wpa_s->eapol);
+	wpa_s->eapol = NULL;
+	if (wpa_supplicant_init_eapol(wpa_s)) {
+		/* Error -> Reset paths to the default value (NULL) once. */
+		if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL)
+			wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL,
+							       NULL);
+
+		return -1;
+	}
+	wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+	return 0;
+}
+
+
+/**
  * wpa_supplicant_set_ap_scan - Set AP scan mode for interface
  * @wpa_s: wpa_supplicant structure for a network interface
  * @ap_scan: AP scan mode
@@ -2245,6 +2522,16 @@
 	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
 	wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
 
+#ifdef CONFIG_PEERKEY
+	if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
+	    wpa_s->current_ssid->peerkey &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+	    wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
+		return;
+	}
+#endif /* CONFIG_PEERKEY */
+
 	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
 	    (wpa_s->last_eapol_matches_bssid &&
 #ifdef CONFIG_AP
@@ -2270,7 +2557,7 @@
 		wpabuf_free(wpa_s->pending_eapol_rx);
 		wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
 		if (wpa_s->pending_eapol_rx) {
-			os_get_time(&wpa_s->pending_eapol_rx_time);
+			os_get_reltime(&wpa_s->pending_eapol_rx_time);
 			os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
 				  ETH_ALEN);
 		}
@@ -2455,9 +2742,15 @@
 	wpa_s->prev_scan_wildcard = 0;
 
 	if (wpa_supplicant_enabled_networks(wpa_s)) {
-		if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
+		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+			interface_count = 0;
+		}
+		if (!wpa_s->p2p_mgmt &&
+		    wpa_supplicant_delayed_sched_scan(wpa_s,
+						      interface_count % 3,
 						      100000))
-			wpa_supplicant_req_scan(wpa_s, interface_count,
+			wpa_supplicant_req_scan(wpa_s, interface_count % 3,
 						100000);
 		interface_count++;
 	} else
@@ -2552,7 +2845,7 @@
 				 struct ieee80211_ht_capabilities *htcaps_mask,
 				 int disabled)
 {
-	u16 msk;
+	le16 msk;
 
 	wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
 
@@ -2625,8 +2918,8 @@
 				int disabled)
 {
 	/* Masking these out disables HT40 */
-	u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
-			       HT_CAP_INFO_SHORT_GI40MHZ);
+	le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+				HT_CAP_INFO_SHORT_GI40MHZ);
 
 	wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
 
@@ -2647,8 +2940,8 @@
 			       int disabled)
 {
 	/* Masking these out disables SGI */
-	u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
-			       HT_CAP_INFO_SHORT_GI40MHZ);
+	le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
+				HT_CAP_INFO_SHORT_GI40MHZ);
 
 	wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
 
@@ -2663,6 +2956,27 @@
 }
 
 
+static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s,
+			       struct ieee80211_ht_capabilities *htcaps,
+			       struct ieee80211_ht_capabilities *htcaps_mask,
+			       int disabled)
+{
+	/* Masking these out disables LDPC */
+	le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP);
+
+	wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled);
+
+	if (disabled)
+		htcaps->ht_capabilities_info &= ~msk;
+	else
+		htcaps->ht_capabilities_info |= msk;
+
+	htcaps_mask->ht_capabilities_info |= msk;
+
+	return 0;
+}
+
+
 void wpa_supplicant_apply_ht_overrides(
 	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 	struct wpa_driver_associate_params *params)
@@ -2686,6 +3000,13 @@
 	wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
 	wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
 	wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
+	wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc);
+
+	if (ssid->ht40_intolerant) {
+		le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT);
+		htcaps->ht_capabilities_info |= bit;
+		htcaps_mask->ht_capabilities_info |= bit;
+	}
 }
 
 #endif /* CONFIG_HT_OVERRIDES */
@@ -2698,6 +3019,10 @@
 {
 	struct ieee80211_vht_capabilities *vhtcaps;
 	struct ieee80211_vht_capabilities *vhtcaps_mask;
+#ifdef CONFIG_HT_OVERRIDES
+	int max_ampdu;
+	const u32 max_ampdu_mask = VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX;
+#endif /* CONFIG_HT_OVERRIDES */
 
 	if (!ssid)
 		return;
@@ -2713,6 +3038,20 @@
 	vhtcaps->vht_capabilities_info = ssid->vht_capa;
 	vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
 
+#ifdef CONFIG_HT_OVERRIDES
+	/* if max ampdu is <= 3, we have to make the HT cap the same */
+	if (ssid->vht_capa_mask & max_ampdu_mask) {
+		max_ampdu = (ssid->vht_capa & max_ampdu_mask) >>
+			find_first_bit(max_ampdu_mask);
+
+		max_ampdu = max_ampdu < 3 ? max_ampdu : 3;
+		wpa_set_ampdu_factor(wpa_s,
+				     (void *) params->htcaps,
+				     (void *) params->htcaps_mask,
+				     max_ampdu);
+	}
+#endif /* CONFIG_HT_OVERRIDES */
+
 #define OVERRIDE_MCS(i)							\
 	if (ssid->vht_tx_mcs_nss_ ##i >= 0) {				\
 		vhtcaps_mask->vht_supported_mcs_set.tx_map |=		\
@@ -2747,7 +3086,7 @@
 	if (!wpa_s->conf->pcsc_reader)
 		return 0;
 
-	wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader);
+	wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
 	if (!wpa_s->scard)
 		return 1;
 
@@ -2813,10 +3152,329 @@
 }
 
 
+static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_capa *capa)
+{
+	struct wowlan_triggers *triggers;
+	int ret = 0;
+
+	if (!wpa_s->conf->wowlan_triggers)
+		return 0;
+
+	triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa);
+	if (triggers) {
+		ret = wpa_drv_wowlan(wpa_s, triggers);
+		os_free(triggers);
+	}
+	return ret;
+}
+
+
+static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
+					      const char *rn)
+{
+	struct wpa_supplicant *iface = wpa_s->global->ifaces;
+	struct wpa_radio *radio;
+
+	while (rn && iface) {
+		radio = iface->radio;
+		if (radio && os_strcmp(rn, radio->name) == 0) {
+			wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s",
+				   wpa_s->ifname, rn);
+			dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+			return radio;
+		}
+
+		iface = iface->next;
+	}
+
+	wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s",
+		   wpa_s->ifname, rn ? rn : "N/A");
+	radio = os_zalloc(sizeof(*radio));
+	if (radio == NULL)
+		return NULL;
+
+	if (rn)
+		os_strlcpy(radio->name, rn, sizeof(radio->name));
+	dl_list_init(&radio->ifaces);
+	dl_list_init(&radio->work);
+	dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+
+	return radio;
+}
+
+
+static void radio_work_free(struct wpa_radio_work *work)
+{
+	if (work->wpa_s->scan_work == work) {
+		/* This should not really happen. */
+		wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work",
+			work->type, work, work->started);
+		work->wpa_s->scan_work = NULL;
+	}
+
+#ifdef CONFIG_P2P
+	if (work->wpa_s->p2p_scan_work == work) {
+		/* This should not really happen. */
+		wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work",
+			work->type, work, work->started);
+		work->wpa_s->p2p_scan_work = NULL;
+	}
+#endif /* CONFIG_P2P */
+
+	dl_list_del(&work->list);
+	os_free(work);
+}
+
+
+static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_radio *radio = eloop_ctx;
+	struct wpa_radio_work *work;
+	struct os_reltime now, diff;
+	struct wpa_supplicant *wpa_s;
+
+	work = dl_list_first(&radio->work, struct wpa_radio_work, list);
+	if (work == NULL)
+		return;
+
+	if (work->started)
+		return; /* already started and still in progress */
+
+	wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
+			      radio_list);
+	if (wpa_s && wpa_s->external_scan_running) {
+		wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+		return;
+	}
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &work->time, &diff);
+	wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+		work->type, work, diff.sec, diff.usec);
+	work->started = 1;
+	work->time = now;
+	work->cb(work, 0);
+}
+
+
+/*
+ * This function removes both started and pending radio works running on
+ * the provided interface's radio.
+ * Prior to the removal of the radio work, its callback (cb) is called with
+ * deinit set to be 1. Each work's callback is responsible for clearing its
+ * internal data and restoring to a correct state.
+ * @wpa_s: wpa_supplicant data
+ * @type: type of works to be removed
+ * @remove_all: 1 to remove all the works on this radio, 0 to remove only
+ * this interface's works.
+ */
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+			const char *type, int remove_all)
+{
+	struct wpa_radio_work *work, *tmp;
+	struct wpa_radio *radio = wpa_s->radio;
+
+	dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work,
+			      list) {
+		if (type && os_strcmp(type, work->type) != 0)
+			continue;
+
+		/* skip other ifaces' works */
+		if (!remove_all && work->wpa_s != wpa_s)
+			continue;
+
+		wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s",
+			work->type, work, work->started ? " (started)" : "");
+		work->cb(work, 1);
+		radio_work_free(work);
+	}
+
+	/* in case we removed the started work */
+	radio_work_check_next(wpa_s);
+}
+
+
+static void radio_remove_interface(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_radio *radio = wpa_s->radio;
+
+	if (!radio)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s",
+		   wpa_s->ifname, radio->name);
+	dl_list_del(&wpa_s->radio_list);
+	radio_remove_works(wpa_s, NULL, 0);
+	wpa_s->radio = NULL;
+	if (!dl_list_empty(&radio->ifaces))
+		return; /* Interfaces remain for this radio */
+
+	wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
+	eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+	os_free(radio);
+}
+
+
+void radio_work_check_next(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_radio *radio = wpa_s->radio;
+
+	if (dl_list_empty(&radio->work))
+		return;
+	eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+	eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
+}
+
+
+/**
+ * radio_add_work - Add a radio work item
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency of the offchannel operation in MHz or 0
+ * @type: Unique identifier for each type of work
+ * @next: Force as the next work to be executed
+ * @cb: Callback function for indicating when radio is available
+ * @ctx: Context pointer for the work (work->ctx in cb())
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to request time for an operation that requires
+ * exclusive radio control. Once the radio is available, the registered callback
+ * function will be called. radio_work_done() must be called once the exclusive
+ * radio operation has been completed, so that the radio is freed for other
+ * operations. The special case of deinit=1 is used to free the context data
+ * during interface removal. That does not allow the callback function to start
+ * the radio operation, i.e., it must free any resources allocated for the radio
+ * work and return.
+ *
+ * The @freq parameter can be used to indicate a single channel on which the
+ * offchannel operation will occur. This may allow multiple radio work
+ * operations to be performed in parallel if they apply for the same channel.
+ * Setting this to 0 indicates that the work item may use multiple channels or
+ * requires exclusive control of the radio.
+ */
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+		   const char *type, int next,
+		   void (*cb)(struct wpa_radio_work *work, int deinit),
+		   void *ctx)
+{
+	struct wpa_radio_work *work;
+	int was_empty;
+
+	work = os_zalloc(sizeof(*work));
+	if (work == NULL)
+		return -1;
+	wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work);
+	os_get_reltime(&work->time);
+	work->freq = freq;
+	work->type = type;
+	work->wpa_s = wpa_s;
+	work->cb = cb;
+	work->ctx = ctx;
+
+	was_empty = dl_list_empty(&wpa_s->radio->work);
+	if (next)
+		dl_list_add(&wpa_s->radio->work, &work->list);
+	else
+		dl_list_add_tail(&wpa_s->radio->work, &work->list);
+	if (was_empty) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
+		radio_work_check_next(wpa_s);
+	}
+
+	return 0;
+}
+
+
+/**
+ * radio_work_done - Indicate that a radio work item has been completed
+ * @work: Completed work
+ *
+ * This function is called once the callback function registered with
+ * radio_add_work() has completed its work.
+ */
+void radio_work_done(struct wpa_radio_work *work)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct os_reltime now, diff;
+	unsigned int started = work->started;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &work->time, &diff);
+	wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds",
+		work->type, work, started ? "done" : "canceled",
+		diff.sec, diff.usec);
+	radio_work_free(work);
+	if (started)
+		radio_work_check_next(wpa_s);
+}
+
+
+int radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
+{
+	struct wpa_radio_work *work;
+	struct wpa_radio *radio = wpa_s->radio;
+
+	dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+		if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int wpas_init_driver(struct wpa_supplicant *wpa_s,
+			    struct wpa_interface *iface)
+{
+	const char *ifname, *driver, *rn;
+
+	driver = iface->driver;
+next_driver:
+	if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+		return -1;
+
+	wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
+	if (wpa_s->drv_priv == NULL) {
+		const char *pos;
+		pos = driver ? os_strchr(driver, ',') : NULL;
+		if (pos) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+				"driver interface - try next driver wrapper");
+			driver = pos + 1;
+			goto next_driver;
+		}
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
+			"interface");
+		return -1;
+	}
+	if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
+		wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
+			"driver_param '%s'", wpa_s->conf->driver_param);
+		return -1;
+	}
+
+	ifname = wpa_drv_get_ifname(wpa_s);
+	if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+			"interface name with '%s'", ifname);
+		os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+	}
+
+	rn = wpa_driver_get_radio_name(wpa_s);
+	if (rn && rn[0] == '\0')
+		rn = NULL;
+
+	wpa_s->radio = radio_add_interface(wpa_s, rn);
+	if (wpa_s->radio == NULL)
+		return -1;
+
+	return 0;
+}
+
+
 static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 				     struct wpa_interface *iface)
 {
-	const char *ifname, *driver;
 	struct wpa_driver_capa capa;
 
 	wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
@@ -2849,6 +3507,13 @@
 		wpa_s->confanother = os_rel2abs_path(iface->confanother);
 		wpa_config_read(wpa_s->confanother, wpa_s->conf);
 
+#ifdef CONFIG_P2P
+		wpa_s->conf_p2p_dev = os_rel2abs_path(iface->conf_p2p_dev);
+#ifndef ANDROID_P2P
+		wpa_config_read(wpa_s->conf_p2p_dev, wpa_s->conf);
+#endif
+#endif /* CONFIG_P2P */
+
 		/*
 		 * Override ctrl_interface and driver_param if set on command
 		 * line.
@@ -2908,38 +3573,9 @@
 	 * L2 receive handler so that association events are processed before
 	 * EAPOL-Key packets if both become available for the same select()
 	 * call. */
-	driver = iface->driver;
-next_driver:
-	if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+	if (wpas_init_driver(wpa_s, iface) < 0)
 		return -1;
 
-	wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
-	if (wpa_s->drv_priv == NULL) {
-		const char *pos;
-		pos = driver ? os_strchr(driver, ',') : NULL;
-		if (pos) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
-				"driver interface - try next driver wrapper");
-			driver = pos + 1;
-			goto next_driver;
-		}
-		wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
-			"interface");
-		return -1;
-	}
-	if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
-		wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
-			"driver_param '%s'", wpa_s->conf->driver_param);
-		return -1;
-	}
-
-	ifname = wpa_drv_get_ifname(wpa_s);
-	if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
-			"interface name with '%s'", ifname);
-		os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
-	}
-
 	if (wpa_supplicant_init_wpa(wpa_s) < 0)
 		return -1;
 
@@ -3053,20 +3689,27 @@
 		return -1;
 	}
 
-#ifdef CONFIG_P2P
 	if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
 		wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
 		return -1;
 	}
-#endif /* CONFIG_P2P */
 
 	if (wpa_bss_init(wpa_s) < 0)
 		return -1;
 
+	/*
+	 * Set Wake-on-WLAN triggers, if configured.
+	 * Note: We don't restore/remove the triggers on shutdown (it doesn't
+	 * have effect anyway when the interface is down).
+	 */
+	if (wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+		return -1;
+
 #ifdef CONFIG_EAP_PROXY
 {
 	size_t len;
-	wpa_s->mnc_len = eap_proxy_get_imsi(wpa_s->imsi, &len);
+	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
+						     &len);
 	if (wpa_s->mnc_len > 0) {
 		wpa_s->imsi[len] = '\0';
 		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
@@ -3100,14 +3743,10 @@
 	}
 
 	wpa_supplicant_cleanup(wpa_s);
+	wpas_p2p_deinit_iface(wpa_s);
 
-#ifdef CONFIG_P2P
-	if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
-			"the management interface is being removed");
-		wpas_p2p_deinit_global(wpa_s->global);
-	}
-#endif /* CONFIG_P2P */
+	wpas_ctrl_radio_work_flush(wpa_s);
+	radio_remove_interface(wpa_s);
 
 	if (wpa_s->drv_priv)
 		wpa_drv_deinit(wpa_s);
@@ -3502,7 +4141,8 @@
 	os_free(global->params.override_driver);
 	os_free(global->params.override_ctrl_interface);
 
-	os_free(global->p2p_disallow_freq);
+	os_free(global->p2p_disallow_freq.range);
+	os_free(global->p2p_go_avoid_freq.range);
 	os_free(global->add_psk);
 
 	os_free(global);
@@ -3532,11 +4172,7 @@
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
-
-#ifdef CONFIG_P2P
 	wpas_p2p_update_config(wpa_s);
-#endif /* CONFIG_P2P */
-
 	wpa_s->conf->changed_parameters = 0;
 }
 
@@ -3595,6 +4231,8 @@
 	int count;
 	int *freqs = NULL;
 
+	wpas_connect_work_done(wpa_s);
+
 	/*
 	 * Remove possible authentication timeout since the connection failed.
 	 */
@@ -3651,7 +4289,7 @@
 	if (count > 3 && wpa_s->current_ssid) {
 		wpa_printf(MSG_DEBUG, "Continuous association failures - "
 			   "consider temporary network disabling");
-		wpas_auth_failed(wpa_s);
+		wpas_auth_failed(wpa_s, "CONN_FAILED");
 	}
 
 	switch (count) {
@@ -3681,8 +4319,6 @@
 	 */
 	wpa_supplicant_req_scan(wpa_s, timeout / 1000,
 				1000 * (timeout % 1000));
-
-	wpas_p2p_continue_after_scan(wpa_s);
 }
 
 
@@ -3716,7 +4352,7 @@
 			wpa_s->reassociate = 1;
 		break;
 	case WPA_CTRL_REQ_EAP_PASSWORD:
-		os_free(eap->password);
+		bin_clear_free(eap->password, eap->password_len);
 		eap->password = (u8 *) os_strdup(value);
 		eap->password_len = os_strlen(value);
 		eap->pending_req_password = 0;
@@ -3724,7 +4360,7 @@
 			wpa_s->reassociate = 1;
 		break;
 	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
-		os_free(eap->new_password);
+		bin_clear_free(eap->new_password, eap->new_password_len);
 		eap->new_password = (u8 *) os_strdup(value);
 		eap->new_password_len = os_strlen(value);
 		eap->pending_req_new_password = 0;
@@ -3732,14 +4368,14 @@
 			wpa_s->reassociate = 1;
 		break;
 	case WPA_CTRL_REQ_EAP_PIN:
-		os_free(eap->pin);
+		str_clear_free(eap->pin);
 		eap->pin = os_strdup(value);
 		eap->pending_req_pin = 0;
 		if (ssid == wpa_s->current_ssid)
 			wpa_s->reassociate = 1;
 		break;
 	case WPA_CTRL_REQ_EAP_OTP:
-		os_free(eap->otp);
+		bin_clear_free(eap->otp, eap->otp_len);
 		eap->otp = (u8 *) os_strdup(value);
 		eap->otp_len = os_strlen(value);
 		os_free(eap->pending_req_otp);
@@ -3747,12 +4383,16 @@
 		eap->pending_req_otp_len = 0;
 		break;
 	case WPA_CTRL_REQ_EAP_PASSPHRASE:
-		os_free(eap->private_key_passwd);
-		eap->private_key_passwd = (u8 *) os_strdup(value);
+		str_clear_free(eap->private_key_passwd);
+		eap->private_key_passwd = os_strdup(value);
 		eap->pending_req_passphrase = 0;
 		if (ssid == wpa_s->current_ssid)
 			wpa_s->reassociate = 1;
 		break;
+	case WPA_CTRL_REQ_SIM:
+		str_clear_free(eap->external_sim_resp);
+		eap->external_sim_resp = os_strdup(value);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
 		return -1;
@@ -3797,7 +4437,7 @@
 	}
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
-	    !ssid->ext_psk)
+	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk)
 		return 1;
 
 	return 0;
@@ -3814,11 +4454,11 @@
 }
 
 
-void wpas_auth_failed(struct wpa_supplicant *wpa_s)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	int dur;
-	struct os_time now;
+	struct os_reltime now;
 
 	if (ssid == NULL) {
 		wpa_printf(MSG_DEBUG, "Authentication failure but no known "
@@ -3844,27 +4484,33 @@
 
 	if (ssid->auth_failures > 50)
 		dur = 300;
-	else if (ssid->auth_failures > 20)
-		dur = 120;
 	else if (ssid->auth_failures > 10)
-		dur = 60;
+		dur = 120;
 	else if (ssid->auth_failures > 5)
+		dur = 90;
+	else if (ssid->auth_failures > 3)
+		dur = 60;
+	else if (ssid->auth_failures > 2)
 		dur = 30;
 	else if (ssid->auth_failures > 1)
 		dur = 20;
 	else
 		dur = 10;
 
-	os_get_time(&now);
+	if (ssid->auth_failures > 1 &&
+	    wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt))
+		dur += os_random() % (ssid->auth_failures * 10);
+
+	os_get_reltime(&now);
 	if (now.sec + dur <= ssid->disabled_until.sec)
 		return;
 
 	ssid->disabled_until.sec = now.sec + dur;
 
 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
-		"id=%d ssid=\"%s\" auth_failures=%u duration=%d",
+		"id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s",
 		ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-		ssid->auth_failures, dur);
+		ssid->auth_failures, dur, reason);
 }
 
 
@@ -3943,90 +4589,44 @@
 }
 
 
-/**
- * wpas_wpa_is_in_progress - Check whether a connection is in progress
- * @wpa_s: Pointer to wpa_supplicant data
- *
- * This function is to check if the wpa state is in beginning of the connection
- * during 4-way handshake or group key handshake with WPA on any shared
- * interface.
- */
-int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s)
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+		    struct wpa_used_freq_data *freqs_data,
+		    unsigned int len)
 {
-	const char *rn, *rn2;
-	struct wpa_supplicant *ifs;
+	unsigned int i;
 
-	if (!wpa_s->driver->get_radio_name)
-                return 0;
-
-	rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-	if (rn == NULL || rn[0] == '\0')
-		return 0;
-
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		if (ifs == wpa_s || !ifs->driver->get_radio_name)
-			continue;
-
-		rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-		if (!rn2 || os_strcmp(rn, rn2) != 0)
-			continue;
-		if (ifs->wpa_state >= WPA_AUTHENTICATING &&
-		    ifs->wpa_state != WPA_COMPLETED) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "Connection is in progress "
-				"on interface %s - defer scan", ifs->ifname);
-			return 1;
-		}
+	wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s",
+		len, title);
+	for (i = 0; i < len; i++) {
+		struct wpa_used_freq_data *cur = &freqs_data[i];
+		wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X",
+			i, cur->freq, cur->flags);
 	}
-
-	return 0;
 }
 
 
 /*
  * Find the operating frequencies of any of the virtual interfaces that
- * are using the same radio as the current interface.
+ * are using the same radio as the current interface, and in addition, get
+ * information about the interface types that are using the frequency.
  */
-int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
-			   int *freq_array, unsigned int len)
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+				struct wpa_used_freq_data *freqs_data,
+				unsigned int len)
 {
-	const char *rn, *rn2;
 	struct wpa_supplicant *ifs;
 	u8 bssid[ETH_ALEN];
 	int freq;
 	unsigned int idx = 0, i;
 
-	os_memset(freq_array, 0, sizeof(int) * len);
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Determining shared radio frequencies (max len %u)", len);
+	os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len);
 
-	/* First add the frequency of the local interface */
-	if (wpa_s->current_ssid != NULL && wpa_s->assoc_freq != 0) {
-		if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
-		    wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)
-			freq_array[idx++] = wpa_s->current_ssid->frequency;
-		else if (wpa_drv_get_bssid(wpa_s, bssid) == 0)
-			freq_array[idx++] = wpa_s->assoc_freq;
-	}
-
-	/* If get_radio_name is not supported, use only the local freq */
-	if (!wpa_s->driver->get_radio_name) {
-		freq = wpa_drv_shared_freq(wpa_s);
-		if (freq > 0 && idx < len &&
-		    (idx == 0 || freq_array[0] != freq))
-			freq_array[idx++] = freq;
-		return idx;
-	}
-
-	rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-	if (rn == NULL || rn[0] == '\0')
-		return idx;
-
-	for (ifs = wpa_s->global->ifaces, idx = 0; ifs && idx < len;
-	     ifs = ifs->next) {
-		if (wpa_s == ifs || !ifs->driver->get_radio_name)
-			continue;
-
-		rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-		if (!rn2 || os_strcmp(rn, rn2) != 0)
-			continue;
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (idx == len)
+			break;
 
 		if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
 			continue;
@@ -4041,11 +4641,45 @@
 
 		/* Hold only distinct freqs */
 		for (i = 0; i < idx; i++)
-			if (freq_array[i] == freq)
+			if (freqs_data[i].freq == freq)
 				break;
 
 		if (i == idx)
-			freq_array[idx++] = freq;
+			freqs_data[idx++].freq = freq;
+
+		if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
+			freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+				WPA_FREQ_USED_BY_P2P_CLIENT :
+				WPA_FREQ_USED_BY_INFRA_STATION;
+		}
 	}
+
+	dump_freq_data(wpa_s, "completed iteration", freqs_data, idx);
 	return idx;
 }
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface.
+ */
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+			   int *freq_array, unsigned int len)
+{
+	struct wpa_used_freq_data *freqs_data;
+	int num, i;
+
+	os_memset(freq_array, 0, sizeof(int) * len);
+
+	freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data));
+	if (!freqs_data)
+		return -1;
+
+	num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+	for (i = 0; i < num; i++)
+		freq_array[i] = freqs_data[i].freq;
+
+	os_free(freqs_data);
+
+	return num;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d73d371..2a0dc20 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -81,6 +81,8 @@
 # to make wpa_supplicant interoperate with these APs, the version number is set
 # to 1 by default. This configuration value can be used to set it to the new
 # version (2).
+# Note: When using MACsec, eapol_version shall be set to 3, which is
+# defined in IEEE Std 802.1X-2010.
 eapol_version=1
 
 # AP scanning/selection
@@ -97,6 +99,8 @@
 #    non-WPA drivers when using IEEE 802.1X mode; do not try to associate with
 #    APs (i.e., external program needs to control association). This mode must
 #    also be used when using wired Ethernet drivers.
+#    Note: macsec_qca driver is one type of Ethernet driver which implements
+#    macsec feature.
 # 2: like 0, but associate with APs using security policy and SSID (but not
 #    BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to
 #    enable operation with hidden SSIDs and optimized roaming; in this mode,
@@ -241,14 +245,14 @@
 # This is an optional set of parameters for automatic scanning
 # within an interface in following format:
 #autoscan=<autoscan module name>:<module parameters>
-# autoscan is like bgscan but on disconnected or inactive state.
-# For instance, on exponential module parameters would be <base>:<limit>
+# autoscan is like bgscan but on disconnected or inactive state.
+# For instance, on exponential module parameters would be <base>:<limit>
 #autoscan=exponential:3:300
 # Which means a delay between scans on a base exponential of 3,
-# up to the limit of 300 seconds (3, 9, 27 ... 300)
-# For periodic module, parameters would be <fixed interval>
+# up to the limit of 300 seconds (3, 9, 27 ... 300)
+# For periodic module, parameters would be <fixed interval>
 #autoscan=periodic:30
-# So a delay of 30 seconds will be applied between each scan
+# So a delay of 30 seconds will be applied between each scan
 
 # filter_ssids - SSID-based scan result filtering
 # 0 = do not filter scan results (default)
@@ -265,6 +269,19 @@
 # inactive stations.
 #p2p_go_max_inactivity=300
 
+# Passphrase length (8..63) for P2P GO
+#
+# This parameter controls the length of the random passphrase that is
+# generated at the GO. Default: 8.
+#p2p_passphrase_len=8
+
+# Extra delay between concurrent P2P search iterations
+#
+# This value adds extra delay in milliseconds between concurrent search
+# iterations to make p2p_find friendlier to concurrent operations by avoiding
+# it from taking 100% of radio resources. The default value is 500 ms.
+#p2p_search_delay=500
+
 # Opportunistic Key Caching (also known as Proactive Key Caching) default
 # This parameter can be used to set the default behavior for the
 # proactive_key_caching parameter. By default, OKC is disabled unless enabled
@@ -342,6 +359,8 @@
 #
 # credential fields:
 #
+# temporary: Whether this credential is temporary and not to be saved
+#
 # priority: Priority group
 #	By default, all networks and credentials get the same priority group
 #	(0). This field can be used to give higher priority for credentials
@@ -399,9 +418,11 @@
 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
 #	format
 #
-# domain: Home service provider FQDN
+# domain: Home service provider FQDN(s)
 #	This is used to compare against the Domain Name List to figure out
-#	whether the AP is operated by the Home SP.
+#	whether the AP is operated by the Home SP. Multiple domain entries can
+#	be used to configure alternative FQDNs that will be considered home
+#	networks.
 #
 # roaming_consortium: Roaming Consortium OI
 #	If roaming_consortium_len is non-zero, this field contains the
@@ -428,6 +449,59 @@
 #	matching with the network. Multiple entries can be used to specify more
 #	than one SSID.
 #
+# roaming_partner: Roaming partner information
+#	This optional field can be used to configure preferences between roaming
+#	partners. The field is a string in following format:
+#	<FQDN>,<0/1 exact match>,<priority>,<* or country code>
+#	(non-exact match means any subdomain matches the entry; priority is in
+#	0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+#	(Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+#	This optional field can be used to keep track of the SP that provisioned
+#	the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+#	These fields can be used to specify minimum download/upload backhaul
+#	bandwidth that is preferred for the credential. This constraint is
+#	ignored if the AP does not advertise WAN Metrics information or if the
+#	limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+#	(PPS/<X+>/Policy/MaximumBSSLoadValue)
+#	This value is used as the maximum channel utilization for network
+#	selection purposes for home networks. If the AP does not advertise
+#	BSS Load or if the limit would prevent any connection, this constraint
+#	will be ignored.
+#
+# req_conn_capab: Required connection capability
+#	(PPS/<X+>/Policy/RequiredProtoPortTuple)
+#	This value is used to configure set of required protocol/port pairs that
+#	a roaming network shall support (include explicitly in Connection
+#	Capability ANQP element). This constraint is ignored if the AP does not
+#	advertise Connection Capability or if this constraint would prevent any
+#	network connection. This policy is not used in home networks.
+#	Format: <protocol>[:<comma-separated list of ports]
+#	Multiple entries can be used to list multiple requirements.
+#	For example, number of common TCP protocols:
+#	req_conn_capab=6,22,80,443
+#	For example, IPSec/IKE:
+#	req_conn_capab=17:500
+#	req_conn_capab=50
+#
+# 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
+#
+# sim_num: Identifier for which SIM to use in multi-SIM devices
+#
 # for example:
 #
 #cred={
@@ -504,9 +578,10 @@
 # 0 = infrastructure (Managed) mode, i.e., associate with an AP (default)
 # 1 = IBSS (ad-hoc, peer-to-peer)
 # 2 = AP (access point)
-# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP)
-# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). WPA-None requires
-# following network block options:
+# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) and
+# WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE (fixed group key
+# TKIP/CCMP) is available for backwards compatibility, but its use is
+# deprecated. WPA-None requires following network block options:
 # proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not
 # both), and psk must also be set.
 #
@@ -547,6 +622,12 @@
 # bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>:
 # <long interval>[:<database file name>]"
 # bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan"
+# Explicitly disable bgscan by setting
+# bgscan=""
+#
+# This option can also be set outside of all network blocks for the bgscan
+# parameter to apply for all the networks that have no specific bgscan
+# parameter.
 #
 # proto: list of accepted protocols
 # WPA = WPA/IEEE 802.11i/D3.0
@@ -611,8 +692,16 @@
 # bit0 (1): require dynamically generated unicast WEP key
 # bit1 (2): require dynamically generated broadcast WEP key
 # 	(3 = require both keys; default)
-# Note: When using wired authentication, eapol_flags must be set to 0 for the
-# authentication to be completed successfully.
+# Note: When using wired authentication (including macsec_qca driver),
+# eapol_flags must be set to 0 for the authentication to be completed
+# successfully.
+#
+# macsec_policy: IEEE 802.1X/MACsec options
+# This determines how sessions are secured with MACsec. It is currently
+# applicable only when using the macsec_qca driver interface.
+# 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.
 #
 # mixed_cell: This option can be used to configure whether so called mixed
 # cells, i.e., networks that use both plaintext and encryption in the same
@@ -791,6 +880,10 @@
 #	EAP workarounds are disabled with eap_workarounds=0.
 #	For EAP-FAST, this must be set to 0 (or left unconfigured for the
 #	default value to be used automatically).
+# tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers
+#	that have issues interoperating with updated TLS version)
+# tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
+#	that have issues interoperating with updated TLS version)
 #
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
@@ -881,6 +974,14 @@
 # 0 = SGI enabled (if AP supports it)
 # 1 = SGI disabled
 #
+# disable_ldpc: Whether LDPC should be disabled.
+# 0 = LDPC enabled (if AP supports it)
+# 1 = LDPC disabled
+#
+# ht40_intolerant: Whether 40 MHz intolerant should be indicated.
+# 0 = 40 MHz tolerant (default)
+# 1 = 40 MHz intolerant
+#
 # ht_mcs:  Configure allowed MCS rates.
 #  Parsed as an array of bytes, in base-16 (ascii-hex)
 # ht_mcs=""                                   // Use all available (default)
@@ -892,6 +993,9 @@
 # 0  = Enable MAX-AMSDU if hardware supports it.
 # 1  = Disable AMSDU
 #
+# ampdu_factor: Maximum A-MPDU Length Exponent
+# Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+#
 # ampdu_density:  Allow overriding AMPDU density configuration.
 #  Treated as hint by the kernel.
 # -1 = Do not make any changes.
@@ -1157,7 +1261,19 @@
 }
 
 
-# IBSS/ad-hoc network with WPA-None/TKIP.
+# IBSS/ad-hoc network with RSN
+network={
+	ssid="ibss-rsn"
+	key_mgmt=WPA-PSK
+	proto=RSN
+	psk="12345678"
+	mode=1
+	frequency=2412
+	pairwise=CCMP
+	group=CCMP
+}
+
+# IBSS/ad-hoc network with WPA-None/TKIP (deprecated)
 network={
 	ssid="test adhoc"
 	mode=1
@@ -1250,3 +1366,17 @@
 network={
 	key_mgmt=NONE
 }
+
+
+# Example MACsec configuration
+#network={
+#	key_mgmt=IEEE8021X
+#	eap=TTLS
+#	phase2="auth=PAP"
+#	anonymous_identity="anonymous@example.com"
+#	identity="user@example.com"
+#	password="secretr"
+#	ca_cert="/etc/cert/ca.pem"
+#	eapol_flags=0
+#	macsec_policy=1
+#}
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index d69cd61..bf3d19d 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Internal definitions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, 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,8 @@
 #include "utils/list.h"
 #include "common/defs.h"
 #include "common/sae.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_defs.h"
 #include "config_ssid.h"
 
 extern const char *wpa_supplicant_version;
@@ -63,6 +65,17 @@
 	 */
 	const char *confanother;
 
+#ifdef CONFIG_P2P
+	/**
+	 * conf_p2p_dev - Additional configuration file used to hold the
+	 * P2P Device configuration parameters.
+	 *
+	 * This can also be %NULL. In such a case, if a P2P Device dedicated
+	 * interfaces is created, the main configuration file will be used.
+	 */
+	const char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
 	/**
 	 * ctrl_interface - Control interface parameter
 	 *
@@ -227,12 +240,6 @@
 	char *service;
 };
 
-struct wpa_freq_range {
-	unsigned int min;
-	unsigned int max;
-};
-
-
 /**
  * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
  *
@@ -252,20 +259,20 @@
 	struct wpa_supplicant *p2p_group_formation;
 	struct wpa_supplicant *p2p_invite_group;
 	u8 p2p_dev_addr[ETH_ALEN];
-	struct os_time p2p_go_wait_client;
+	struct os_reltime p2p_go_wait_client;
 	struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
 	struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
 	int p2p_disabled;
 	int cross_connection;
-	struct wpa_freq_range *p2p_disallow_freq;
-	unsigned int num_p2p_disallow_freq;
+	struct wpa_freq_range_list p2p_disallow_freq;
+	struct wpa_freq_range_list p2p_go_avoid_freq;
 	enum wpa_conc_pref {
 		WPA_CONC_PREF_NOT_SET,
 		WPA_CONC_PREF_STA,
 		WPA_CONC_PREF_P2P
 	} conc_pref;
-	unsigned int p2p_cb_on_scan_complete:1;
 	unsigned int p2p_per_sta_psk:1;
+	unsigned int p2p_fail_on_wps_complete:1;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	int wifi_display;
@@ -278,6 +285,61 @@
 
 
 /**
+ * struct wpa_radio - Internal data for per-radio information
+ *
+ * This structure is used to share data about configured interfaces
+ * (struct wpa_supplicant) that share the same physical radio, e.g., to allow
+ * better coordination of offchannel operations.
+ */
+struct wpa_radio {
+	char name[16]; /* from driver_ops get_radio_name() or empty if not
+			* available */
+	struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
+	struct dl_list work; /* struct wpa_radio_work::list entries */
+};
+
+/**
+ * struct wpa_radio_work - Radio work item
+ */
+struct wpa_radio_work {
+	struct dl_list list;
+	unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */
+	const char *type;
+	struct wpa_supplicant *wpa_s;
+	void (*cb)(struct wpa_radio_work *work, int deinit);
+	void *ctx;
+	unsigned int started:1;
+	struct os_reltime time;
+};
+
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+		   const char *type, int next,
+		   void (*cb)(struct wpa_radio_work *work, int deinit),
+		   void *ctx);
+void radio_work_done(struct wpa_radio_work *work);
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+			const char *type, int remove_all);
+void radio_work_check_next(struct wpa_supplicant *wpa_s);
+int radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
+
+struct wpa_connect_work {
+	unsigned int sme:1;
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid;
+};
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+			struct wpa_ssid *test_ssid);
+void wpas_connect_work_free(struct wpa_connect_work *cwork);
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s);
+
+struct wpa_external_work {
+	unsigned int id;
+	char type[100];
+	unsigned int timeout;
+};
+
+/**
  * offchannel_send_action_result - Result of offchannel send Action frame
  */
 enum offchannel_send_action_result {
@@ -296,7 +358,7 @@
 		WPS_AP_SEL_REG_OUR
 	} type;
 	unsigned int tries;
-	struct os_time last_attempt;
+	struct os_reltime last_attempt;
 };
 
 struct wpa_ssid_value {
@@ -304,6 +366,14 @@
 	size_t ssid_len;
 };
 
+#define WPA_FREQ_USED_BY_INFRA_STATION BIT(0)
+#define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1)
+
+struct wpa_used_freq_data {
+	int freq;
+	unsigned int flags;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -314,6 +384,8 @@
  */
 struct wpa_supplicant {
 	struct wpa_global *global;
+	struct wpa_radio *radio; /* shared radio context */
+	struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */
 	struct wpa_supplicant *parent;
 	struct wpa_supplicant *next;
 	struct l2_packet_data *l2;
@@ -334,9 +406,14 @@
 
 	char *confname;
 	char *confanother;
+
+#ifdef CONFIG_P2P
+	char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
 	struct wpa_config *conf;
 	int countermeasures;
-	os_time_t last_michael_mic_error;
+	struct os_reltime last_michael_mic_error;
 	u8 bssid[ETH_ALEN];
 	u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
 				     * field contains the target BSSID. */
@@ -368,6 +445,9 @@
 
 	enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband;
 
+	/* Preferred network for the next connection attempt */
+	struct wpa_ssid *next_ssid;
+
 	/* previous scan was wildcard when interleaving between
 	 * wildcard scans and specific SSID scan when max_ssids=1 */
 	int prev_scan_wildcard;
@@ -400,8 +480,7 @@
 	struct wpa_bss **last_scan_res;
 	unsigned int last_scan_res_used;
 	unsigned int last_scan_res_size;
-	int last_scan_full;
-	struct os_time last_scan;
+	struct os_reltime last_scan;
 
 	struct wpa_driver_ops *driver;
 	int interface_removed; /* whether the network interface has been
@@ -412,6 +491,7 @@
 	struct ctrl_iface_priv *ctrl_iface;
 
 	enum wpa_states wpa_state;
+	struct wpa_radio_work *scan_work;
 	int scanning;
 	int sched_scanning;
 	int new_connection;
@@ -425,7 +505,8 @@
 
 	unsigned char last_eapol_src[ETH_ALEN];
 
-	int keys_cleared;
+	unsigned int keys_cleared; /* bitfield of key indexes that the driver is
+				    * known not to be configured with a key */
 
 	struct wpa_blacklist *blacklist;
 
@@ -470,14 +551,27 @@
 		 * to be run.
 		 */
 		MANUAL_SCAN_REQ
-	} scan_req;
-	struct os_time scan_trigger_time;
+	} scan_req, last_scan_req;
+	struct os_reltime scan_trigger_time, scan_start_time;
 	int scan_runs; /* number of scan runs since WPS was started */
 	int *next_scan_freqs;
+	int *manual_scan_freqs;
+	int *manual_sched_scan_freqs;
+	unsigned int manual_scan_passive:1;
+	unsigned int manual_scan_use_id:1;
+	unsigned int manual_scan_only_new:1;
+	unsigned int own_scan_requested:1;
+	unsigned int own_scan_running:1;
+	unsigned int external_scan_running:1;
+	unsigned int clear_driver_scan_cache:1;
+	unsigned int manual_scan_id;
 	int scan_interval; /* time in sec between scans to find suitable AP */
 	int normal_scans; /* normal scans run before sched_scan */
 	int scan_for_connection; /* whether the scan request was triggered for
 				  * finding a connection */
+#define MAX_SCAN_ID 16
+	int scan_id[MAX_SCAN_ID];
+	unsigned int scan_id_count;
 
 	unsigned int drv_flags;
 	unsigned int drv_enc;
@@ -509,9 +603,11 @@
 	int blacklist_cleared;
 
 	struct wpabuf *pending_eapol_rx;
-	struct os_time pending_eapol_rx_time;
+	struct os_reltime pending_eapol_rx_time;
 	u8 pending_eapol_rx_src[ETH_ALEN];
 	unsigned int last_eapol_matches_bssid:1;
+	unsigned int eap_expected_failure:1;
+	unsigned int reattach:1; /* reassociation to the same BSS requested */
 
 	struct ibss_rsn *ibss_rsn;
 
@@ -543,7 +639,8 @@
 		u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
 					* sa_query_count octets of pending
 					* SA Query transaction identifiers */
-		struct os_time sa_query_start;
+		struct os_reltime sa_query_start;
+		struct os_reltime last_unprot_disconnect;
 		u8 sched_obss_scan;
 		u16 obss_scan_int;
 		u16 bss_max_idle_period;
@@ -570,6 +667,7 @@
 	unsigned int pending_action_freq;
 	int pending_action_no_cck;
 	int pending_action_without_roc;
+	unsigned int pending_action_tx_done:1;
 	void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s,
 					    unsigned int freq, const u8 *dst,
 					    const u8 *src, const u8 *bssid,
@@ -603,6 +701,8 @@
 	u8 p2p_auth_invite[ETH_ALEN];
 	int p2p_sd_over_ctrl_iface;
 	int p2p_in_provisioning;
+	int p2p_in_invitation;
+	int p2p_invite_go_freq;
 	int pending_invite_ssid_id;
 	int show_group_started;
 	u8 go_dev_addr[ETH_ALEN];
@@ -610,6 +710,8 @@
 	u8 pending_join_iface_addr[ETH_ALEN];
 	u8 pending_join_dev_addr[ETH_ALEN];
 	int pending_join_wps_method;
+	u8 p2p_join_ssid[32];
+	size_t p2p_join_ssid_len;
 	int p2p_join_scan_count;
 	int auto_pd_scan_retry;
 	int force_long_sd;
@@ -639,20 +741,35 @@
 	 */
 	char cross_connect_uplink[100];
 
-	unsigned int sta_scan_pending:1;
 	unsigned int p2p_auto_join:1;
 	unsigned int p2p_auto_pd:1;
 	unsigned int p2p_persistent_group:1;
 	unsigned int p2p_fallback_to_go_neg:1;
 	unsigned int p2p_pd_before_go_neg:1;
 	unsigned int p2p_go_ht40:1;
+	unsigned int p2p_go_vht:1;
 	unsigned int user_initiated_pd:1;
+	unsigned int p2p_go_group_formation_completed:1;
+	unsigned int waiting_presence_resp;
+	int p2p_first_connection_timeout;
+	unsigned int p2p_nfc_tag_enabled:1;
+	unsigned int p2p_peer_oob_pk_hash_known:1;
+	unsigned int p2p_disable_ip_addr_req:1;
 	int p2p_persistent_go_freq;
 	int p2p_persistent_id;
 	int p2p_go_intent;
 	int p2p_connect_freq;
-	struct os_time p2p_auto_started;
+	struct os_reltime p2p_auto_started;
 	struct wpa_ssid *p2p_last_4way_hs_fail;
+	struct wpa_radio_work *p2p_scan_work;
+	struct wpa_radio_work *p2p_listen_work;
+	struct wpa_radio_work *p2p_send_action_work;
+
+	u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
+	struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
+					* formation */
+	u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	u8 p2p_ip_addr_info[3 * 4];
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;
@@ -672,7 +789,6 @@
 	int after_wps;
 	int known_wps_freq;
 	unsigned int wps_freq;
-	u16 wps_ap_channel;
 	int wps_fragment_size;
 	int auto_reconnect_disabled;
 
@@ -689,7 +805,15 @@
 	unsigned int auto_select:1;
 	unsigned int auto_network_select:1;
 	unsigned int fetch_all_anqp:1;
+	unsigned int fetch_osu_info:1;
+	unsigned int fetch_osu_icon_in_progress:1;
 	struct wpa_bss *interworking_gas_bss;
+	unsigned int osu_icon_id;
+	struct osu_provider *osu_prov;
+	size_t osu_prov_count;
+	struct os_reltime osu_icon_fetch_start;
+	unsigned int num_osu_scans;
+	unsigned int num_prov_found;
 #endif /* CONFIG_INTERWORKING */
 	unsigned int drv_capa_known;
 
@@ -698,19 +822,24 @@
 		u16 num_modes;
 		u16 flags;
 	} hw;
+#ifdef CONFIG_MACSEC
+	struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
 
 	int pno;
+	int pno_sched_pending;
 
 	/* WLAN_REASON_* reason codes. Negative if locally generated. */
 	int disconnect_reason;
 
 	struct ext_password_data *ext_pw;
 
-	struct wpabuf *last_gas_resp;
-	u8 last_gas_addr[ETH_ALEN];
-	u8 last_gas_dialog_token;
+	struct wpabuf *last_gas_resp, *prev_gas_resp;
+	u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN];
+	u8 last_gas_dialog_token, prev_gas_dialog_token;
 
 	unsigned int no_keep_alive:1;
+	unsigned int ext_mgmt_frame_handling:1;
 
 #ifdef CONFIG_WNM
 	u8 wnm_dialog_token;
@@ -729,6 +858,11 @@
 #endif /* CONFIG_TESTING_GET_GTK */
 
 	unsigned int num_multichan_concurrent;
+	struct wpa_radio_work *connect_work;
+
+	unsigned int ext_work_id;
+
+	struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES];
 };
 
 
@@ -776,6 +910,9 @@
 				    struct wpa_ssid *ssid);
 void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
 				   struct wpa_ssid *ssid);
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+					   const char *pkcs11_engine_path,
+					   const char *pkcs11_module_path);
 int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
 			       int ap_scan);
 int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
@@ -807,21 +944,19 @@
 void wpa_supplicant_terminate_proc(struct wpa_global *global);
 void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 			     const u8 *buf, size_t len);
-enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
 void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
 void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
-void wpas_auth_failed(struct wpa_supplicant *wpa_s);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
 void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
 			      struct wpa_ssid *ssid, int clear_failures);
 int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
 		    size_t ssid_len);
 void wpas_request_connection(struct wpa_supplicant *wpa_s);
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf);
-int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s);
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -867,6 +1002,13 @@
 
 int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
 
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+		    struct wpa_used_freq_data *freqs_data,
+		    unsigned int len);
+
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+				struct wpa_used_freq_data *freqs_data,
+				unsigned int len);
 int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 			   int *freq_array, unsigned int len);
 
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index a08eb33..f3f2a64 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -1,6 +1,6 @@
 ##### wpa_supplicant configuration file template #####
 update_config=1
-ctrl_interface=wlan0
 eapol_version=1
 ap_scan=1
 fast_reauth=1
+pmf=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 61a42bd..350b122 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -26,6 +26,7 @@
 #include "bss.h"
 #include "scan.h"
 #include "notify.h"
+#include "wpas_kay.h"
 
 
 #ifndef CONFIG_NO_CONFIG_BLOBS
@@ -216,29 +217,50 @@
 }
 
 
-static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
+static const char * result_str(enum eapol_supp_result result)
+{
+	switch (result) {
+	case EAPOL_SUPP_RESULT_FAILURE:
+		return "FAILURE";
+	case EAPOL_SUPP_RESULT_SUCCESS:
+		return "SUCCESS";
+	case EAPOL_SUPP_RESULT_EXPECTED_FAILURE:
+		return "EXPECTED_FAILURE";
+	}
+	return "?";
+}
+
+
+static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol,
+				    enum eapol_supp_result result,
 				    void *ctx)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	int res, pmk_len;
 	u8 pmk[PMK_LEN];
 
-	wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully",
-		   success ? "" : "un");
+	wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s",
+		   result_str(result));
 
 	if (wpas_wps_eapol_cb(wpa_s) > 0)
 		return;
 
-	if (!success) {
+	wpa_s->eap_expected_failure = result ==
+		EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+
+	if (result != EAPOL_SUPP_RESULT_SUCCESS) {
 		/*
 		 * Make sure we do not get stuck here waiting for long EAPOL
 		 * timeout if the AP does not disconnect in case of
 		 * authentication failure.
 		 */
 		wpa_supplicant_req_auth_timeout(wpa_s, 2, 0);
+	} else {
+		ieee802_1x_notify_create_actor(wpa_s, wpa_s->last_eapol_src);
 	}
 
-	if (!success || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+	if (result != EAPOL_SUPP_RESULT_SUCCESS ||
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
 		return;
 
 	if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt))
@@ -539,12 +561,12 @@
 
 static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
 					 u8 action_code, u8 dialog_token,
-					 u16 status_code, const u8 *buf,
-					 size_t len)
+					 u16 status_code, u32 peer_capab,
+					 const u8 *buf, size_t len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
-				      status_code, buf, len);
+				      status_code, peer_capab, buf, len);
 }
 
 
@@ -560,7 +582,9 @@
 	const u8 *supp_rates, size_t supp_rates_len,
 	const struct ieee80211_ht_capabilities *ht_capab,
 	const struct ieee80211_vht_capabilities *vht_capab,
-	u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len)
+	u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len,
+	const u8 *supp_channels, size_t supp_channels_len,
+	const u8 *supp_oper_classes, size_t supp_oper_classes_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct hostapd_sta_add_params params;
@@ -588,6 +612,10 @@
 	params.set = !add;
 	params.ext_capab = ext_capab;
 	params.ext_capab_len = ext_capab_len;
+	params.supp_channels = supp_channels;
+	params.supp_channels_len = supp_channels_len;
+	params.supp_oper_classes = supp_oper_classes;
+	params.supp_oper_classes_len = supp_oper_classes_len;
 
 	return wpa_drv_sta_add(wpa_s, &params);
 }
@@ -611,6 +639,8 @@
 		return WPA_CTRL_REQ_EAP_OTP;
 	else if (os_strcmp(field, "PASSPHRASE") == 0)
 		return WPA_CTRL_REQ_EAP_PASSPHRASE;
+	else if (os_strcmp(field, "SIM") == 0)
+		return WPA_CTRL_REQ_SIM;
 	return WPA_CTRL_REQ_UNKNOWN;
 }
 
@@ -647,6 +677,9 @@
 		*txt = "Private key passphrase";
 		ret = "PASSPHRASE";
 		break;
+	case WPA_CTRL_REQ_SIM:
+		ret = "SIM";
+		break;
 	default:
 		break;
 	}
@@ -918,6 +951,22 @@
 		conf.ssid = ssid->ssid;
 		conf.ssid_len = ssid->ssid_len;
 		conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
+#ifdef CONFIG_P2P
+		if (ssid->p2p_group && wpa_s->current_bss &&
+		    !wpa_s->p2p_disable_ip_addr_req) {
+			struct wpabuf *p2p;
+			p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+							  P2P_IE_VENDOR_TYPE);
+			if (p2p) {
+				u8 group_capab;
+				group_capab = p2p_get_group_capab(p2p);
+				if (group_capab &
+				    P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION)
+					conf.p2p = 1;
+				wpabuf_free(p2p);
+			}
+		}
+#endif /* CONFIG_P2P */
 	}
 	wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
 }
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
new file mode 100644
index 0000000..354decf
--- /dev/null
+++ b/wpa_supplicant/wpas_kay.c
@@ -0,0 +1,378 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <openssl/ssl.h>
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_i.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "pae/ieee802_1x_key.h"
+#include "pae/ieee802_1x_kay.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "config_ssid.h"
+#include "driver_i.h"
+#include "wpas_kay.h"
+
+
+#define DEFAULT_KEY_LEN		16
+/* secure Connectivity Association Key Name (CKN) */
+#define DEFAULT_CKN_LEN		16
+
+
+static int wpas_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	return wpa_drv_macsec_init(priv, params);
+}
+
+
+static int wpas_macsec_deinit(void *priv)
+{
+	return wpa_drv_macsec_deinit(priv);
+}
+
+
+static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled)
+{
+	return wpa_drv_enable_protect_frames(wpa_s, enabled);
+}
+
+
+static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window)
+{
+	return wpa_drv_set_replay_protect(wpa_s, enabled, window);
+}
+
+
+static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs,
+					 size_t cs_len)
+{
+	return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len);
+}
+
+
+static int wpas_enable_controlled_port(void *wpa_s, Boolean enabled)
+{
+	return wpa_drv_enable_controlled_port(wpa_s, enabled);
+}
+
+
+static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel,
+				      u8 an, u32 *lowest_pn)
+{
+	return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn);
+}
+
+
+static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel,
+				      u8 an, u32 *next_pn)
+{
+	return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn);
+}
+
+
+static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel,
+				      u8 an, u32 next_pn)
+{
+	return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn);
+}
+
+
+static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel)
+{
+	return wpa_drv_get_available_receive_sc(wpa_s, channel);
+}
+
+
+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 wpas_create_receive_sc(void *wpa_s, u32 channel,
+				  struct ieee802_1x_mka_sci *sci,
+				  enum validate_frames vf,
+				  enum confidentiality_offset co)
+{
+	return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port,
+					 conf_offset_val(co), vf);
+}
+
+
+static int wpas_delete_receive_sc(void *wpa_s, u32 channel)
+{
+	return wpa_drv_delete_receive_sc(wpa_s, channel);
+}
+
+
+static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an,
+				  u32 lowest_pn, const u8 *sak)
+{
+	return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak);
+}
+
+
+static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an)
+{
+	return wpa_drv_enable_receive_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an)
+{
+	return wpa_drv_disable_receive_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel)
+{
+	return wpa_drv_get_available_transmit_sc(wpa_s, channel);
+}
+
+
+static int
+wpas_create_transmit_sc(void *wpa_s, u32 channel,
+			const struct ieee802_1x_mka_sci *sci,
+			enum confidentiality_offset co)
+{
+	return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port,
+					  conf_offset_val(co));
+}
+
+
+static int wpas_delete_transmit_sc(void *wpa_s, u32 channel)
+{
+	return wpa_drv_delete_transmit_sc(wpa_s, channel);
+}
+
+
+static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an,
+				   u32 next_pn, Boolean confidentiality,
+				   const u8 *sak)
+{
+	return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn,
+					  confidentiality, sak);
+}
+
+
+static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+{
+	return wpa_drv_enable_transmit_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+{
+	return wpa_drv_disable_transmit_sa(wpa_s, channel, an);
+}
+
+
+int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	struct ieee802_1x_kay_ctx *kay_ctx;
+	struct ieee802_1x_kay *res = NULL;
+	enum macsec_policy policy;
+
+	ieee802_1x_dealloc_kay_sm(wpa_s);
+
+	if (!ssid || ssid->macsec_policy == 0)
+		return 0;
+
+	policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE;
+
+	kay_ctx = os_zalloc(sizeof(*kay_ctx));
+	if (!kay_ctx)
+		return -1;
+
+	kay_ctx->ctx = wpa_s;
+
+	kay_ctx->macsec_init = wpas_macsec_init;
+	kay_ctx->macsec_deinit = wpas_macsec_deinit;
+	kay_ctx->enable_protect_frames = wpas_enable_protect_frames;
+	kay_ctx->set_replay_protect = wpas_set_replay_protect;
+	kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite;
+	kay_ctx->enable_controlled_port = wpas_enable_controlled_port;
+	kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
+	kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
+	kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
+	kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc;
+	kay_ctx->create_receive_sc = wpas_create_receive_sc;
+	kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
+	kay_ctx->create_receive_sa = wpas_create_receive_sa;
+	kay_ctx->enable_receive_sa = wpas_enable_receive_sa;
+	kay_ctx->disable_receive_sa = wpas_disable_receive_sa;
+	kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc;
+	kay_ctx->create_transmit_sc = wpas_create_transmit_sc;
+	kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc;
+	kay_ctx->create_transmit_sa = wpas_create_transmit_sa;
+	kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa;
+	kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa;
+
+	res = ieee802_1x_kay_init(kay_ctx, policy, wpa_s->ifname,
+				  wpa_s->own_addr);
+	if (res == NULL) {
+		os_free(kay_ctx);
+		return -1;
+	}
+
+	wpa_s->kay = res;
+
+	return 0;
+}
+
+
+void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->kay)
+		return;
+
+	ieee802_1x_kay_deinit(wpa_s->kay);
+	wpa_s->kay = NULL;
+}
+
+
+static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s,
+					  const u8 *addr, u8 *sid, size_t *len)
+{
+	const u8 *session_id;
+	size_t id_len, need_len;
+
+	session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len);
+	if (session_id == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to get SessionID from EAPOL state machines");
+		return -1;
+	}
+
+	need_len = 1 + 2 * SSL3_RANDOM_SIZE;
+	if (need_len > id_len) {
+		wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
+		return -1;
+	}
+
+	os_memcpy(sid, session_id, need_len);
+	*len = need_len;
+
+	return 0;
+}
+
+
+static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr,
+				   u8 *msk, size_t *len)
+{
+	u8 key[EAP_MSK_LEN];
+	size_t keylen;
+	struct eapol_sm *sm;
+	int res;
+
+	sm = wpa_s->eapol;
+	if (sm == NULL)
+		return -1;
+
+	keylen = EAP_MSK_LEN;
+	res = eapol_sm_get_key(sm, key, keylen);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to get MSK from EAPOL state machines");
+		return -1;
+	}
+
+	if (keylen > *len)
+		keylen = *len;
+	os_memcpy(msk, key, keylen);
+	*len = keylen;
+
+	return 0;
+}
+
+
+void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+				      const u8 *peer_addr)
+{
+	u8 *sid;
+	size_t sid_len = 128;
+	struct mka_key_name *ckn;
+	struct mka_key *cak;
+	struct mka_key *msk;
+	void *res = NULL;
+
+	if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG,
+		   "IEEE 802.1X: External notification - Create MKA for "
+		   MACSTR, MAC2STR(peer_addr));
+
+	msk = os_zalloc(sizeof(*msk));
+	sid = os_zalloc(sid_len);
+	ckn = os_zalloc(sizeof(*ckn));
+	cak = os_zalloc(sizeof(*cak));
+	if (!msk || !sid || !ckn || !cak)
+		goto fail;
+
+	msk->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_auth_get_msk(wpa_s, wpa_s->bssid, msk->key, &msk->len)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK");
+		goto fail;
+	}
+
+	if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len))
+	{
+		wpa_printf(MSG_ERROR,
+			   "IEEE 802.1X: Could not get EAP Session Id");
+		goto fail;
+	}
+
+	/* Derive CAK from MSK */
+	cak->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_cak_128bits_aes_cmac(msk->key, wpa_s->own_addr,
+					    peer_addr, cak->key)) {
+		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_128bits_aes_cmac(msk->key, wpa_s->own_addr,
+					    peer_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(wpa_s->kay, ckn, cak, 0,
+					EAP_EXCHANGE, FALSE);
+
+fail:
+	if (msk) {
+		os_memset(msk, 0, sizeof(*msk));
+		os_free(msk);
+	}
+	os_free(sid);
+	os_free(ckn);
+	if (cak) {
+		os_memset(cak, 0, sizeof(*cak));
+		os_free(cak);
+	}
+
+	return res;
+}
diff --git a/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h
new file mode 100644
index 0000000..b7236d0
--- /dev/null
+++ b/wpa_supplicant/wpas_kay.h
@@ -0,0 +1,41 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAS_KAY_H
+#define WPAS_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid);
+void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+				      const u8 *peer_addr);
+void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+					  struct wpa_ssid *ssid)
+{
+	return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+			       const u8 *peer_addr)
+{
+	return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPAS_KAY_H */
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
new file mode 100644
index 0000000..e4c83b5
--- /dev/null
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -0,0 +1,102 @@
+/*
+ * wpa_supplicant module tests
+ * Copyright (c) 2014, 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 "wpa_supplicant_i.h"
+#include "blacklist.h"
+
+
+static int wpas_blacklist_module_tests(void)
+{
+	struct wpa_supplicant wpa_s;
+	int ret = -1;
+
+	os_memset(&wpa_s, 0, sizeof(wpa_s));
+
+	wpa_blacklist_clear(&wpa_s);
+
+	if (wpa_blacklist_get(NULL, NULL) != NULL ||
+	    wpa_blacklist_get(NULL, (u8 *) "123456") != NULL ||
+	    wpa_blacklist_get(&wpa_s, NULL) != NULL ||
+	    wpa_blacklist_get(&wpa_s, (u8 *) "123456") != NULL)
+		goto fail;
+
+	if (wpa_blacklist_add(NULL, NULL) == 0 ||
+	    wpa_blacklist_add(NULL, (u8 *) "123456") == 0 ||
+	    wpa_blacklist_add(&wpa_s, NULL) == 0)
+		goto fail;
+
+	if (wpa_blacklist_del(NULL, NULL) == 0 ||
+	    wpa_blacklist_del(NULL, (u8 *) "123456") == 0 ||
+	    wpa_blacklist_del(&wpa_s, NULL) == 0 ||
+	    wpa_blacklist_del(&wpa_s, (u8 *) "123456") == 0)
+		goto fail;
+
+	if (wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "444444") < 0 ||
+	    wpa_blacklist_del(&wpa_s, (u8 *) "333333") < 0 ||
+	    wpa_blacklist_del(&wpa_s, (u8 *) "xxxxxx") == 0 ||
+	    wpa_blacklist_get(&wpa_s, (u8 *) "xxxxxx") != NULL ||
+	    wpa_blacklist_get(&wpa_s, (u8 *) "111111") == NULL ||
+	    wpa_blacklist_get(&wpa_s, (u8 *) "222222") == NULL ||
+	    wpa_blacklist_get(&wpa_s, (u8 *) "444444") == NULL ||
+	    wpa_blacklist_del(&wpa_s, (u8 *) "111111") < 0 ||
+	    wpa_blacklist_del(&wpa_s, (u8 *) "222222") < 0 ||
+	    wpa_blacklist_del(&wpa_s, (u8 *) "444444") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0)
+		goto fail;
+
+	ret = 0;
+fail:
+	wpa_blacklist_clear(&wpa_s);
+
+	if (ret)
+		wpa_printf(MSG_ERROR, "blacklist module test failure");
+
+	return ret;
+}
+
+
+int wpas_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "wpa_supplicant module tests");
+
+	if (wpas_blacklist_module_tests() < 0)
+		ret = -1;
+
+#ifdef CONFIG_WPS
+	{
+		int wps_module_tests(void);
+		if (wps_module_tests() < 0)
+			ret = -1;
+	}
+#endif /* CONFIG_WPS */
+
+	{
+		int utils_module_tests(void);
+		if (utils_module_tests() < 0)
+			ret = -1;
+	}
+
+	{
+		int common_module_tests(void);
+		if (common_module_tests() < 0)
+			ret = -1;
+	}
+
+	return ret;
+}
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index f6c2fcb..fd0d14a 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant / WPS integration
- * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -52,8 +52,30 @@
 }
 
 
+static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	int use_fast_assoc = timeout_ctx != NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb");
+	if (!use_fast_assoc ||
+	    wpa_supplicant_fast_associate(wpa_s) != 1)
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s)
+{
+	eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0);
+	eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1);
+}
+
+
 int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
 {
+	if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
+		return 1;
+
 	if (!wpa_s->wps_success &&
 	    wpa_s->current_ssid &&
 	    eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
@@ -119,9 +141,18 @@
 			wpabuf_free(wps);
 		}
 
-		if (!use_fast_assoc ||
-		    wpa_supplicant_fast_associate(wpa_s) != 1)
-			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		/*
+		 * Complete the next step from an eloop timeout to allow pending
+		 * driver events related to the disconnection to be processed
+		 * first. This makes it less likely for disconnection event to
+		 * cause problems with the following connection.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout");
+		wpas_wps_assoc_with_cred_cancel(wpa_s);
+		eloop_register_timeout(0, 10000,
+				       wpas_wps_assoc_with_cred, wpa_s,
+				       use_fast_assoc ? (void *) 1 :
+				       (void *) 0);
 		return 1;
 	}
 
@@ -260,31 +291,6 @@
 		    ssid->group_cipher != new_ssid->group_cipher)
 			continue;
 
-		if (ssid->passphrase && new_ssid->passphrase) {
-			if (os_strlen(ssid->passphrase) !=
-			    os_strlen(new_ssid->passphrase))
-				continue;
-			if (os_strcmp(ssid->passphrase, new_ssid->passphrase) !=
-			    0)
-				continue;
-		} else if (ssid->passphrase || new_ssid->passphrase)
-			continue;
-
-		if ((ssid->psk_set || new_ssid->psk_set) &&
-		    os_memcmp(ssid->psk, new_ssid->psk, sizeof(ssid->psk)) != 0)
-			continue;
-
-		if (ssid->auth_alg == WPA_ALG_WEP) {
-			if (ssid->wep_tx_keyidx != new_ssid->wep_tx_keyidx)
-				continue;
-			if (os_memcmp(ssid->wep_key, new_ssid->wep_key,
-				      sizeof(ssid->wep_key)))
-				continue;
-			if (os_memcmp(ssid->wep_key_len, new_ssid->wep_key_len,
-				      sizeof(ssid->wep_key_len)))
-				continue;
-		}
-
 		/* Remove the duplicated older network entry. */
 		wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
 		wpas_notify_network_removed(wpa_s, ssid);
@@ -298,7 +304,6 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
-	u8 key_idx = 0;
 	u16 auth_type;
 #ifdef CONFIG_WPS_REG_DISABLE_OPEN
 	int registrar = 0;
@@ -344,7 +349,6 @@
 	}
 
 	if (auth_type != WPS_AUTH_OPEN &&
-	    auth_type != WPS_AUTH_SHARED &&
 	    auth_type != WPS_AUTH_WPAPSK &&
 	    auth_type != WPS_AUTH_WPA2PSK) {
 		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
@@ -392,6 +396,17 @@
 		ssid = wpa_config_add_network(wpa_s->conf);
 		if (ssid == NULL)
 			return -1;
+		if (wpa_s->current_ssid) {
+			/*
+			 * Should the GO issue multiple credentials for some
+			 * reason, each credential should be marked as a
+			 * temporary P2P group similarly to the one that gets
+			 * marked as such based on the pre-configured values
+			 * used for the WPS network block.
+			 */
+			ssid->p2p_group = wpa_s->current_ssid->p2p_group;
+			ssid->temporary = wpa_s->current_ssid->temporary;
+		}
 		wpas_notify_network_added(wpa_s, ssid);
 	}
 
@@ -407,38 +422,6 @@
 	switch (cred->encr_type) {
 	case WPS_ENCR_NONE:
 		break;
-	case WPS_ENCR_WEP:
-		if (cred->key_len <= 0)
-			break;
-		if (cred->key_len != 5 && cred->key_len != 13 &&
-		    cred->key_len != 10 && cred->key_len != 26) {
-			wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length "
-				   "%lu", (unsigned long) cred->key_len);
-			return -1;
-		}
-		if (cred->key_idx > NUM_WEP_KEYS) {
-			wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d",
-				   cred->key_idx);
-			return -1;
-		}
-		if (cred->key_idx)
-			key_idx = cred->key_idx - 1;
-		if (cred->key_len == 10 || cred->key_len == 26) {
-			if (hexstr2bin((char *) cred->key,
-				       ssid->wep_key[key_idx],
-				       cred->key_len / 2) < 0) {
-				wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key "
-					   "%d", key_idx);
-				return -1;
-			}
-			ssid->wep_key_len[key_idx] = cred->key_len / 2;
-		} else {
-			os_memcpy(ssid->wep_key[key_idx], cred->key,
-				  cred->key_len);
-			ssid->wep_key_len[key_idx] = cred->key_len;
-		}
-		ssid->wep_tx_keyidx = key_idx;
-		break;
 	case WPS_ENCR_TKIP:
 		ssid->pairwise_cipher = WPA_CIPHER_TKIP;
 		break;
@@ -463,11 +446,6 @@
 		}
 #endif /* CONFIG_WPS_REG_DISABLE_OPEN */
 		break;
-	case WPS_AUTH_SHARED:
-		ssid->auth_alg = WPA_AUTH_ALG_SHARED;
-		ssid->key_mgmt = WPA_KEY_MGMT_NONE;
-		ssid->proto = 0;
-		break;
 	case WPS_AUTH_WPAPSK:
 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
 		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -509,9 +487,6 @@
 
 	wpas_wps_security_workaround(wpa_s, ssid, cred);
 
-	if (cred->ap_channel)
-		wpa_s->wps_ap_channel = cred->ap_channel;
-
 	wpas_wps_remove_dup_network(wpa_s, ssid);
 
 #ifndef CONFIG_NO_CONFIG_WRITE
@@ -533,15 +508,6 @@
 }
 
 
-#ifdef CONFIG_P2P
-static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_supplicant *wpa_s = eloop_ctx;
-	wpas_p2p_notif_pbc_overlap(wpa_s);
-}
-#endif /* CONFIG_P2P */
-
-
 static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
 					 struct wps_event_m2d *m2d)
 {
@@ -560,7 +526,7 @@
 		 * Notify P2P from eloop timeout to avoid issues with the
 		 * interface getting removed while processing a message.
 		 */
-		eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s,
+		eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
 				       NULL);
 	}
 #endif /* CONFIG_P2P */
@@ -608,9 +574,7 @@
 	eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL);
 
 	wpas_notify_wps_event_fail(wpa_s, fail);
-#ifdef CONFIG_P2P
 	wpas_p2p_wps_failed(wpa_s, fail);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -669,9 +633,7 @@
 	eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
 			       NULL);
 
-#ifdef CONFIG_P2P
 	wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -882,12 +844,16 @@
 	int id;
 	struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
 
+	wpa_s->after_wps = 0;
+	wpa_s->known_wps_freq = 0;
+
 	prev_current = wpa_s->current_ssid;
 
 	/* Enable the networks disabled during wpas_wps_reassoc */
 	wpas_wps_reenable_networks(wpa_s);
 
 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
 
 	/* Remove any existing WPS network from configuration */
 	ssid = wpa_s->conf->ssid;
@@ -927,7 +893,8 @@
 
 
 static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
-					      int registrar, const u8 *bssid)
+					      int registrar, const u8 *dev_addr,
+					      const u8 *bssid)
 {
 	struct wpa_ssid *ssid;
 
@@ -947,6 +914,11 @@
 		return NULL;
 	}
 
+#ifdef CONFIG_P2P
+	if (dev_addr)
+		os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN);
+#endif /* CONFIG_P2P */
+
 	if (bssid) {
 #ifndef CONFIG_P2P
 		struct wpa_bss *bss;
@@ -1027,13 +999,17 @@
 
 
 static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
-			     struct wpa_ssid *selected, const u8 *bssid)
+			     struct wpa_ssid *selected, const u8 *bssid,
+			     int freq)
 {
 	struct wpa_bss *bss;
 
 	wpa_s->after_wps = 0;
 	wpa_s->known_wps_freq = 0;
-	if (bssid) {
+	if (freq) {
+		wpa_s->after_wps = 5;
+		wpa_s->wps_freq = freq;
+	} else if (bssid) {
 		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
 		if (bss && bss->freq > 0) {
 			wpa_s->known_wps_freq = 1;
@@ -1049,6 +1025,8 @@
 	wpa_s->normal_scans = 0;
 	wpa_s->wps_success = 0;
 	wpa_s->blacklist_cleared = 0;
+
+	wpa_supplicant_cancel_sched_scan(wpa_s);
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -1058,7 +1036,7 @@
 {
 	struct wpa_ssid *ssid;
 	wpas_clear_wps(wpa_s);
-	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
+	ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
 	if (ssid == NULL)
 		return -1;
 	ssid->temporary = 1;
@@ -1081,24 +1059,47 @@
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
-	wpas_wps_reassoc(wpa_s, ssid, bssid);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
 	return 0;
 }
 
 
-int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
-		       const char *pin, int p2p_group, u16 dev_pw_id)
+static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s,
+				 const u8 *dev_addr, const u8 *bssid,
+				 const char *pin, int p2p_group, u16 dev_pw_id,
+				 const u8 *peer_pubkey_hash,
+				 const u8 *ssid_val, size_t ssid_len, int freq)
 {
 	struct wpa_ssid *ssid;
-	char val[128];
+	char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
 	unsigned int rpin = 0;
+	char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
 
 	wpas_clear_wps(wpa_s);
-	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
-	if (ssid == NULL)
+	if (bssid && is_zero_ether_addr(bssid))
+		bssid = NULL;
+	ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not add network");
 		return -1;
+	}
 	ssid->temporary = 1;
 	ssid->p2p_group = p2p_group;
+	if (ssid_val) {
+		ssid->ssid = os_malloc(ssid_len);
+		if (ssid->ssid) {
+			os_memcpy(ssid->ssid, ssid_val, ssid_len);
+			ssid->ssid_len = ssid_len;
+		}
+	}
+	if (peer_pubkey_hash) {
+		os_memcpy(hash, " pkhash=", 8);
+		wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
+					   peer_pubkey_hash,
+					   WPS_OOB_PUBKEY_HASH_LEN);
+	} else {
+		hash[0] = '\0';
+	}
 #ifdef CONFIG_P2P
 	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
 		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
@@ -1112,25 +1113,38 @@
 	}
 #endif /* CONFIG_P2P */
 	if (pin)
-		os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
-			    pin, dev_pw_id);
-	else {
+		os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
+			    pin, dev_pw_id, hash);
+	else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+		os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
+			    dev_pw_id, hash);
+	} else {
 		rpin = wps_generate_pin();
-		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
-			    rpin, dev_pw_id);
+		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
+			    rpin, dev_pw_id, hash);
 	}
-	if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+	if (wpa_config_set(ssid, "phase1", val, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
 		return -1;
+	}
 	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,
 			       wpa_s, NULL);
 	wpa_s->wps_ap_iter = 1;
-	wpas_wps_reassoc(wpa_s, ssid, bssid);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, freq);
 	return rpin;
 }
 
 
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		       const char *pin, int p2p_group, u16 dev_pw_id)
+{
+	return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
+				     dev_pw_id, NULL, NULL, 0, 0);
+}
+
+
 /* Cancel the wps pbc/pin requests */
 int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
 {
@@ -1155,8 +1169,13 @@
 	} else {
 		wpas_wps_reenable_networks(wpa_s);
 		wpas_wps_clear_ap_info(wpa_s);
+		if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) >
+		    0)
+			wpas_clear_wps(wpa_s);
 	}
 
+	wpa_s->after_wps = 0;
+
 	return 0;
 }
 
@@ -1172,7 +1191,7 @@
 	if (!pin)
 		return -1;
 	wpas_clear_wps(wpa_s);
-	ssid = wpas_wps_add_network(wpa_s, 1, bssid);
+	ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
 	if (ssid == NULL)
 		return -1;
 	ssid->temporary = 1;
@@ -1200,7 +1219,7 @@
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
-	wpas_wps_reassoc(wpa_s, ssid, bssid);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
 	return 0;
 }
 
@@ -1267,7 +1286,6 @@
 
 static u16 wps_fix_config_methods(u16 config_methods)
 {
-#ifdef CONFIG_WPS2
 	if ((config_methods &
 	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
 	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
@@ -1282,7 +1300,6 @@
 			   "virtual_push_button for WPS 2.0 compliance");
 		config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 	}
-#endif /* CONFIG_WPS2 */
 
 	return config_methods;
 }
@@ -1291,7 +1308,9 @@
 static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
 			      struct wps_context *wps)
 {
-	wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
+	char buf[50];
+	const char *src;
+
 	if (is_nil_uuid(wpa_s->conf->uuid)) {
 		struct wpa_supplicant *first;
 		first = wpa_s->global->ifaces;
@@ -1302,18 +1321,18 @@
 				os_memcpy(wps->uuid,
 					  wpa_s->global->ifaces->wps->uuid,
 					  WPS_UUID_LEN);
-			wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
-				    "interface", wps->uuid, WPS_UUID_LEN);
+			src = "from the first interface";
 		} else {
 			uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
-			wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
-				    "address", wps->uuid, WPS_UUID_LEN);
+			src = "based on MAC address";
 		}
 	} else {
 		os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
-		wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
-			    wps->uuid, WPS_UUID_LEN);
+		src = "based on configuration";
 	}
+
+	uuid_bin2str(wps->uuid, buf, sizeof(buf));
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf);
 }
 
 
@@ -1418,19 +1437,39 @@
 }
 
 
+#ifdef CONFIG_WPS_ER
+static void wpas_wps_nfc_clear(struct wps_context *wps)
+{
+	wps->ap_nfc_dev_pw_id = 0;
+	wpabuf_free(wps->ap_nfc_dh_pubkey);
+	wps->ap_nfc_dh_pubkey = NULL;
+	wpabuf_free(wps->ap_nfc_dh_privkey);
+	wps->ap_nfc_dh_privkey = NULL;
+	wpabuf_free(wps->ap_nfc_dev_pw);
+	wps->ap_nfc_dev_pw = NULL;
+}
+#endif /* CONFIG_WPS_ER */
+
+
 void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
 {
+	wpas_wps_assoc_with_cred_cancel(wpa_s);
 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
 	wpas_wps_clear_ap_info(wpa_s);
 
+#ifdef CONFIG_P2P
+	eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL);
+#endif /* CONFIG_P2P */
+
 	if (wpa_s->wps == NULL)
 		return;
 
 #ifdef CONFIG_WPS_ER
 	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
 	wpa_s->wps_er = NULL;
+	wpas_wps_nfc_clear(wpa_s->wps);
 #endif /* CONFIG_WPS_ER */
 
 	wps_registrar_deinit(wpa_s->wps->registrar);
@@ -1880,8 +1919,10 @@
 
 	if (os_strcmp(settings->encr, "NONE") == 0)
 		cred.encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
 	else if (os_strcmp(settings->encr, "WEP") == 0)
 		cred.encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
 	else if (os_strcmp(settings->encr, "TKIP") == 0)
 		cred.encr_type = WPS_ENCR_TKIP;
 	else if (os_strcmp(settings->encr, "CCMP") == 0)
@@ -1952,19 +1993,6 @@
 }
 
 
-int wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
-{
-	struct wpa_ssid *ssid;
-
-	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
-		if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
-			return 1;
-	}
-
-	return 0;
-}
-
-
 void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
 {
 	struct wps_context *wps = wpa_s->wps;
@@ -2090,15 +2118,32 @@
 }
 
 
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr,
+		       const u8 *bssid,
+		       const struct wpabuf *dev_pw, u16 dev_pw_id,
+		       int p2p_group, const u8 *peer_pubkey_hash,
+		       const u8 *ssid, size_t ssid_len, int freq)
 {
 	struct wps_context *wps = wpa_s->wps;
 	char pw[32 * 2 + 1];
 
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+		dev_pw = wpa_s->conf->wps_nfc_dev_pw;
+		dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+	}
+
 	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
-	    wpa_s->conf->wps_nfc_dh_privkey == NULL ||
-	    wpa_s->conf->wps_nfc_dev_pw == NULL)
+	    wpa_s->conf->wps_nfc_dh_privkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Missing DH params - "
+			   "cannot start NFC-triggered connection");
 		return -1;
+	}
+
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - "
+			   "cannot start NFC-triggered connection", dev_pw_id);
+		return -1;
+	}
 
 	dh5_free(wps->dh_ctx);
 	wpabuf_free(wps->dh_pubkey);
@@ -2111,6 +2156,7 @@
 		wps->dh_pubkey = NULL;
 		wpabuf_free(wps->dh_privkey);
 		wps->dh_privkey = NULL;
+		wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key");
 		return -1;
 	}
 	wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
@@ -2119,22 +2165,25 @@
 		wps->dh_pubkey = NULL;
 		wpabuf_free(wps->dh_privkey);
 		wps->dh_privkey = NULL;
+		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context");
 		return -1;
 	}
 
-	wpa_snprintf_hex_uppercase(pw, sizeof(pw),
-				   wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
-				   wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
-	return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
-				  wpa_s->conf->wps_nfc_dev_pw_id);
+	if (dev_pw) {
+		wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+					   wpabuf_head(dev_pw),
+					   wpabuf_len(dev_pw));
+	}
+	return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid,
+				     dev_pw ? pw : NULL,
+				     p2p_group, dev_pw_id, peer_pubkey_hash,
+				     ssid, ssid_len, freq);
 }
 
 
 static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
 			     struct wps_parse_attr *attr)
 {
-	wpa_s->wps_ap_channel = 0;
-
 	/*
 	 * Disable existing networks temporarily to allow the newly learned
 	 * credential to be preferred. Enable the temporarily disabled networks
@@ -2150,12 +2199,8 @@
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 		return 0;
 
-	wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
-		   "based on the received credential added");
-	wpa_s->normal_scans = 0;
-	wpa_supplicant_reinit_autoscan(wpa_s);
-	if (wpa_s->wps_ap_channel) {
-		u16 chan = wpa_s->wps_ap_channel;
+	if (attr->ap_channel) {
+		u16 chan = WPA_GET_BE16(attr->ap_channel);
 		int freq = 0;
 
 		if (chan >= 1 && chan <= 13)
@@ -2166,14 +2211,21 @@
 			freq = 5000 + 5 * chan;
 
 		if (freq) {
-			wpa_printf(MSG_DEBUG, "WPS: Credential indicated "
-				   "AP channel %u -> %u MHz", chan, freq);
+			wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz",
+				   chan, freq);
 			wpa_s->after_wps = 5;
 			wpa_s->wps_freq = freq;
 		}
 	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+		   "based on the received credential added");
+	wpa_s->normal_scans = 0;
+	wpa_supplicant_reinit_autoscan(wpa_s);
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
+
+	wpa_supplicant_cancel_sched_scan(wpa_s);
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 
 	return 0;
@@ -2217,7 +2269,7 @@
 
 
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
-			  const struct wpabuf *data)
+			  const struct wpabuf *data, int forced_freq)
 {
 	const struct wpabuf *wps = data;
 	struct wpabuf *tmp = NULL;
@@ -2230,6 +2282,15 @@
 		/* Assume this contains full NDEF record */
 		tmp = ndef_parse_wifi(data);
 		if (tmp == NULL) {
+#ifdef CONFIG_P2P
+			tmp = ndef_parse_p2p(data);
+			if (tmp) {
+				ret = wpas_p2p_nfc_tag_process(wpa_s, tmp,
+							       forced_freq);
+				wpabuf_free(tmp);
+				return ret;
+			}
+#endif /* CONFIG_P2P */
 			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
 			return -1;
 		}
@@ -2242,24 +2303,45 @@
 }
 
 
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr)
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef)
 {
-	if (cr)
-		return ndef_build_wifi_hc(1);
-	return ndef_build_wifi_hr();
+	struct wpabuf *ret;
+
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+	    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+			   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+		return NULL;
+
+	ret = wps_build_nfc_handover_req(wpa_s->wps,
+					 wpa_s->conf->wps_nfc_dh_pubkey);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
 }
 
 
 #ifdef CONFIG_WPS_NFC
-struct wpabuf * wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s,
-					     int ndef, const char *uuid)
+
+static struct wpabuf *
+wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef,
+			     const char *uuid)
 {
 #ifdef CONFIG_WPS_ER
 	struct wpabuf *ret;
 	u8 u[UUID_LEN], *use_uuid = NULL;
 	u8 addr[ETH_ALEN], *use_addr = NULL;
+	struct wps_context *wps = wpa_s->wps;
 
-	if (!wpa_s->wps_er)
+	if (wps == NULL)
 		return NULL;
 
 	if (uuid == NULL)
@@ -2271,11 +2353,23 @@
 	else
 		return NULL;
 
-	/*
-	 * Handover Select carrier record for WPS uses the same format as
-	 * configuration token.
-	 */
-	ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) {
+		if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+				   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+			return NULL;
+	}
+
+	wpas_wps_nfc_clear(wps);
+	wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+	wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+	wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+		wpas_wps_nfc_clear(wps);
+		return NULL;
+	}
+
+	ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid,
+				      use_addr, wpa_s->conf->wps_nfc_dh_pubkey);
 	if (ndef && ret) {
 		struct wpabuf *tmp;
 		tmp = ndef_build_wifi(ret);
@@ -2306,29 +2400,125 @@
 }
 
 
-int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
-				 const struct wpabuf *data)
-{
-	/* TODO */
-	return -1;
-}
-
-
-int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
-				 const struct wpabuf *data)
+static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+					const struct wpabuf *data)
 {
 	struct wpabuf *wps;
-	int ret;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+	const u8 *bssid = NULL;
+	int freq = 0;
 
 	wps = ndef_parse_wifi(data);
 	if (wps == NULL)
 		return -1;
 	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
 		   "payload from NFC connection handover");
-	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
-	ret = wpas_wps_nfc_tag_process(wpa_s, wps);
-	wpabuf_free(wps);
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in Wi-Fi Handover Select Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
 
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Select Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Select Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Select Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover "
+			   "Select Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len);
+
+	if (attr.mac_addr) {
+		bssid = attr.mac_addr;
+		wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR,
+			   MAC2STR(bssid));
+	}
+
+	if (attr.rf_bands)
+		wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands);
+
+	if (attr.ap_channel) {
+		u16 chan = WPA_GET_BE16(attr.ap_channel);
+
+		wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan);
+
+		if (chan >= 1 && chan <= 13 &&
+		    (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ))
+			freq = 2407 + 5 * chan;
+		else if (chan == 14 &&
+			 (attr.rf_bands == NULL ||
+			  *attr.rf_bands & WPS_RF_24GHZ))
+			freq = 2484;
+		else if (chan >= 30 &&
+			 (attr.rf_bands == NULL ||
+			  *attr.rf_bands & WPS_RF_50GHZ))
+			freq = 5000 + 5 * chan;
+
+		if (freq) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: AP indicated channel %u -> %u MHz",
+				   chan, freq);
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Select Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0,
+				 attr.oob_dev_password,
+				 attr.ssid, attr.ssid_len, freq);
+
+out:
+	wpabuf_free(wps);
 	return ret;
 }
 
@@ -2343,15 +2533,107 @@
 	return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
 }
 
+
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct wpabuf *wps;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+
+	/*
+	 * Enrollee/station is always initiator of the NFC connection handover,
+	 * so use the request message here to find Enrollee public key hash.
+	 */
+	wps = ndef_parse_wifi(req);
+	if (wps == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+		   "payload from NFC connection handover");
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in rt Wi-Fi Handover Request Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Request Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+			   "Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar,
+					     attr.oob_dev_password,
+					     DEV_PW_NFC_CONNECTION_HANDOVER,
+					     NULL, 0, 1);
+
+out:
+	wpabuf_free(wps);
+	return ret;
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 
-extern int wpa_debug_level;
-
 static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
 {
 	size_t i;
-	struct os_time now;
+	struct os_reltime now;
 
 	if (wpa_debug_level > MSG_DEBUG)
 		return;
@@ -2359,7 +2641,7 @@
 	if (wpa_s->wps_ap == NULL)
 		return;
 
-	os_get_time(&now);
+	os_get_reltime(&now);
 
 	for (i = 0; i < wpa_s->num_wps_ap; i++) {
 		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
@@ -2463,11 +2745,14 @@
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
 	struct wps_ap_info *ap;
+
+	wpa_s->after_wps = 0;
+
 	if (!wpa_s->wps_ap_iter)
 		return;
 	ap = wpas_wps_get_ap_info(wpa_s, bssid);
 	if (ap == NULL)
 		return;
 	ap->tries++;
-	os_get_time(&ap->last_attempt);
+	os_get_reltime(&ap->last_attempt);
 }
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 2a212ca..2263512 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -60,24 +60,27 @@
 struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
 					     int ndef, const char *uuid);
 int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
-int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
 void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
 struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 					  int ndef, const char *id_str);
 struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+		       const u8 *bssid,
+		       const struct wpabuf *dev_pw, u16 dev_pw_id,
+		       int p2p_group, const u8 *peer_pubkey_hash,
+		       const u8 *ssid, size_t ssid_len, int freq);
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
-			  const struct wpabuf *data);
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr);
+			  const struct wpabuf *data, int forced_freq);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef);
 struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 					  int ndef, int cr, const char *uuid);
-int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
-				 const struct wpabuf *data);
-int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
-				 const struct wpabuf *data);
 int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
 				 const struct wpabuf *req,
 				 const struct wpabuf *sel);
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
 void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_results *scan_res);
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);